]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorSarah Hoffmann <lonvia@denofr.de>
Fri, 6 Mar 2015 06:57:20 +0000 (07:57 +0100)
committerSarah Hoffmann <lonvia@denofr.de>
Fri, 6 Mar 2015 06:57:20 +0000 (07:57 +0100)
Conflicts:
sql/functions.sql
sql/tables.sql

1  2 
sql/functions.sql
sql/tables.sql

diff --combined sql/functions.sql
index 29e6544c4500014a8c9f5161dea5ee674b25cfde,194a64d7782490fc78cf22ce1934644ddc6b0332..c3590c1ebb837a3d6a65b78969019550a205bf91
@@@ -747,8 -747,77 +747,77 @@@ END
  $$
  LANGUAGE plpgsql;
  
+ -- find the parant road of an interpolation
+ CREATE OR REPLACE FUNCTION get_interpolation_parent(wayid BIGINT, street TEXT, place TEXT,
+                                                     partition INTEGER, centroid GEOMETRY, geom GEOMETRY)
+ RETURNS BIGINT AS $$
+ DECLARE
+   addr_street TEXT;
+   addr_place TEXT;
+   parent_place_id BIGINT;
+   address_street_word_ids INTEGER[];
+   waynodes BIGINT[];
+   location RECORD;
+ BEGIN
+   addr_street = street;
+   addr_place = place;
+   IF addr_street is null and addr_place is null THEN
+     select nodes from planet_osm_ways where id = wayid INTO waynodes;
+     FOR location IN SELECT placex.street, placex.addr_place from placex 
+                     where osm_type = 'N' and osm_id = ANY(waynodes)
+                           and (placex.street is not null or placex.addr_place is not null)
+                           and indexed_status < 100
+                     limit 1 LOOP
+       addr_street = location.street;
+       addr_place = location.addr_place;
+     END LOOP;
+   END IF;
+   IF addr_street IS NOT NULL THEN
+     address_street_word_ids := get_name_ids(make_standard_name(addr_street));
+     IF address_street_word_ids IS NOT NULL THEN
+       FOR location IN SELECT place_id from getNearestNamedRoadFeature(partition, centroid, address_street_word_ids) LOOP
+         parent_place_id := location.place_id;
+       END LOOP;
+     END IF;
+   END IF;
+   IF parent_place_id IS NULL AND addr_place IS NOT NULL THEN
+     address_street_word_ids := get_name_ids(make_standard_name(addr_place));
+     IF address_street_word_ids IS NOT NULL THEN
+       FOR location IN SELECT place_id from getNearestNamedPlaceFeature(partition, centroid, address_street_word_ids) LOOP
+         parent_place_id := location.place_id;
+       END LOOP;
+     END IF;
+   END IF;
+   IF parent_place_id is null THEN
+     FOR location IN SELECT place_id FROM placex
+         WHERE ST_DWithin(geom, placex.geometry, 0.001) and placex.rank_search = 26
+         ORDER BY (ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0))+
+                   ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,0.5))+
+                   ST_distance(placex.geometry, ST_LineInterpolatePoint(geom,1))) ASC limit 1
+     LOOP
+       parent_place_id := location.place_id;
+     END LOOP;
+   END IF;
+   IF parent_place_id is null THEN
+     RETURN 0;
+   END IF;
  
- CREATE OR REPLACE FUNCTION create_interpolation(wayid BIGINT, interpolationtype TEXT) RETURNS INTEGER
+   RETURN parent_place_id;
+ END;
+ $$
+ LANGUAGE plpgsql;
+ CREATE OR REPLACE FUNCTION create_interpolation(wayid BIGINT, interpolationtype TEXT,
+                                                 parent_id BIGINT, partition INTEGER,
+                                                 country_code TEXT,  geometry_sector INTEGER,
+                                                 defpostalcode TEXT, geom GEOMETRY) RETURNS INTEGER
    AS $$
  DECLARE
  
    linegeo GEOMETRY;
    splitline GEOMETRY;
    sectiongeo GEOMETRY;
-   search_place_id BIGINT;
-   defpostalcode TEXT;
+   pointgeo GEOMETRY;
  
  BEGIN
+   delete from placex where osm_type = 'W' and osm_id = wayid
+                                           and class = 'place' and type = 'address';
    IF interpolationtype = 'odd' OR interpolationtype = 'even' THEN
      stepsize := 2;
    ELSEIF interpolationtype = 'all' THEN
      RETURN 0;
    END IF;
  
-   select postcode, geometry from placex where osm_type = 'W' and osm_id = wayid 
-     INTO defpostalcode, linegeo;
-   IF ST_GeometryType(linegeo) != 'ST_LineString' THEN
-     RETURN 0;
-   END IF;
+   linegeo := geom;
    startnumber := NULL;
    newpoints := 0;
  
      -- it is guaranteed to be the original node. For place/house types use the
      -- one with the smallest id because the original node was created first.
      -- Ignore all nodes marked for deletion. (Might happen when the type changes.)
-     select place_id from placex where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT and indexed_status < 100 order by (type = 'house'),place_id limit 1 INTO search_place_id;
-     IF search_place_id IS NOT NULL THEN
-       select * from placex where place_id = search_place_id INTO nextnode;
+     select * from placex where osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT
+                                and indexed_status < 100
+                          order by (type = 'address'),place_id limit 1 INTO nextnode;
+     IF nextnode.place_id IS NOT NULL THEN
  
          IF nodeidpos > 1 and nodeidpos < array_upper(waynodes, 1) THEN
            -- Make sure that the point is actually on the line. That might
  
            startnumber := startnumber + stepsize;
            -- correct for odd/even
-           IF (interpolationtype = 'odd' AND startnumber%2 = 0) OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
+           IF (interpolationtype = 'odd' AND startnumber%2 = 0)
+              OR (interpolationtype = 'even' AND startnumber%2 = 1) THEN
              startnumber := startnumber - 1;
            END IF;
            endnumber := endnumber - 1;
  
-           delete from placex where osm_type = 'N' and osm_id = prevnode.osm_id and type = 'house' and place_id != prevnode.place_id;
+           -- keep for compatibility with previous versions
+           delete from placex where osm_type = 'N' and osm_id = prevnode.osm_id
+                                and place_id != prevnode.place_id and class = 'place'
+                                and type = 'house';
            FOR housenum IN startnumber..endnumber BY stepsize LOOP
-             -- this should really copy postcodes but it puts a huge burden on
-             -- the system for no big benefit ideally postcodes should move up to the way
-             insert into placex (osm_type, osm_id, class, type, admin_level,
-               housenumber, street, addr_place, isin, postcode,
-               country_code, parent_place_id, rank_address, rank_search,
-               indexed_status, geometry)
-               values ('N', prevnode.osm_id, 'place', 'house', prevnode.admin_level,
-               housenum, prevnode.street, prevnode.addr_place, prevnode.isin, coalesce(prevnode.postcode, defpostalcode),
-               prevnode.country_code, prevnode.parent_place_id, prevnode.rank_address, prevnode.rank_search,
-               1, ST_LineInterpolatePoint(sectiongeo, (housenum::float-orginalstartnumber::float)/originalnumberrange::float));
+             pointgeo := ST_LineInterpolatePoint(sectiongeo, (housenum::float-orginalstartnumber::float)/originalnumberrange::float);
+             insert into placex (place_id, partition, osm_type, osm_id,
+                                 class, type, admin_level, housenumber,
+                                 postcode,
+                                 country_code, parent_place_id, rank_address, rank_search,
+                                 indexed_status, indexed_date, geometry_sector,
+                                 calculated_country_code, centroid, geometry)
+               values (nextval('seq_place'), partition, 'W', wayid,
+                       'place', 'address', prevnode.admin_level, housenum,
+                       coalesce(prevnode.postcode, defpostalcode),
+                       prevnode.country_code, parent_id, 30, 30,
+                       0, now(), geometry_sector, country_code,
+                       pointgeo, pointgeo);
              newpoints := newpoints + 1;
  --RAISE WARNING 'interpolation number % % ',prevnode.place_id,housenum;
            END LOOP;
@@@ -886,9 -959,9 +959,9 @@@ DECLAR
  BEGIN
    --DEBUG: RAISE WARNING '% %',NEW.osm_type,NEW.osm_id;
  
-   -- remove operator tag for most places, messes too much with search_name indexes
-   IF NEW.class not in ('amenity', 'shop') THEN
-     NEW.name := delete(NEW.name, 'operator');
+   -- ignore interpolated addresses
+   IF NEW.class = 'place' and NEW.type = 'address' THEN
+     RETURN NEW;
    END IF;
  
    -- just block these
          NEW.rank_address := NEW.rank_search;
        ELSEIF NEW.type in ('houses') THEN
          -- can't guarantee all required nodes loaded yet due to caching in osm2pgsql
-         -- insert new point into place for each derived building
-         --i := create_interpolation(NEW.osm_id, NEW.housenumber);
          NEW.rank_search := 28;
          NEW.rank_address := 0;
        END IF;
      END IF;
    END IF;
  
     -- add to tables for special search
     -- Note: won't work on initial import because the classtype tables
     -- do not yet exist. It won't hurt either.
@@@ -1276,6 -1348,11 +1348,11 @@@ BEGI
      RETURN NEW;
    END IF;
  
+   -- ignore interpolated addresses
+   IF NEW.class = 'place' and NEW.type = 'address' THEN
+     RETURN NEW;
+   END IF;
    --DEBUG: RAISE WARNING 'placex_update % %',NEW.osm_type,NEW.osm_id;
  
  --RAISE WARNING '%',NEW.place_id;
        RETURN NEW;
      END IF;
  
-     IF NEW.class = 'place' AND NEW.type = 'houses' THEN
-       i := create_interpolation(NEW.osm_id, NEW.housenumber);
-       RETURN NEW;
-     END IF;
      -- Speed up searches - just use the centroid of the feature
      -- cheaper but less acurate
      place_centroid := ST_PointOnSurface(NEW.geometry);
      END IF;
      NEW.geometry_sector := geometry_sector(NEW.partition, place_centroid);
  
+     -- interpolations
+     IF NEW.class = 'place' AND NEW.type = 'houses'THEN
+       IF NEW.osm_type = 'W' and ST_GeometryType(NEW.geometry) = 'ST_LineString' THEN
+         NEW.parent_place_id := get_interpolation_parent(NEW.osm_id, NEW.street, NEW.addr_place,
+                                                         NEW.partition, place_centroid, NEW.geometry);
+         i := create_interpolation(NEW.osm_id, NEW.housenumber, NEW.parent_place_id,
+                                   NEW.partition, NEW.calculated_country_code,
+                                   NEW.geometry_sector, NEW.postcode, NEW.geometry);
+       END IF;
+       RETURN NEW;
+     END IF;
      -- waterway ways are linked when they are part of a relation and have the same class/type
      IF NEW.osm_type = 'R' and NEW.class = 'waterway' THEN
          FOR relation_members IN select members from planet_osm_rels r where r.id = NEW.osm_id and r.parts != array[]::bigint[]
  
        NEW.parent_place_id := null;
  
-       -- to do that we have to find our parent road
-       -- Copy data from linked items (points on ways, addr:street links, relations)
-       -- Note that addr:street links can only be indexed once the street itself is indexed
-       IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
+       -- if we have a POI and there is no address information,
+       -- see if we can get it from a surrounding building
+       IF NEW.osm_type = 'N' AND NEW.street IS NULL AND NEW.addr_place IS NULL
+          AND NEW.housenumber IS NULL THEN
+         FOR location IN select * from placex where ST_Covers(geometry, place_centroid)
+               and (housenumber is not null or street is not null or addr_place is not null)
+               and rank_search > 28 AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')
+               limit 1
+         LOOP
+           NEW.housenumber := location.housenumber;
+           NEW.street := location.street;
+           NEW.addr_place := location.addr_place;
+         END LOOP;
+       END IF;
  
-         -- if there is no address information, see if we can get it from a surrounding building
-         IF NEW.street IS NULL AND NEW.addr_place IS NULL AND NEW.housenumber IS NULL THEN
-           FOR location IN select * from placex where ST_Covers(geometry, place_centroid) and rank_search > 28 and (housenumber is not null or street is not null or addr_place is not null) AND ST_GeometryType(geometry) in ('ST_Polygon','ST_MultiPolygon')
-           LOOP
-             NEW.housenumber := location.housenumber;
-             NEW.street := location.street;
-             NEW.addr_place := location.addr_place;
-           END LOOP;
-         END IF;
+       -- We have to find our parent road.
+       -- Copy data from linked items (points on ways, addr:street links, relations)
  
-         -- Is this node part of a relation?
-         FOR relation IN select * from planet_osm_rels where parts @> ARRAY[NEW.osm_id] and members @> ARRAY['n'||NEW.osm_id]
+       -- Is this object part of a relation?
+         FOR relation IN select * from planet_osm_rels where parts @> ARRAY[NEW.osm_id] and members @> ARRAY[lower(NEW.osm_type)||NEW.osm_id]
          LOOP
            -- At the moment we only process one type of relation - associatedStreet
-           IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN
+           IF relation.tags @> ARRAY['associatedStreet'] THEN
              FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP
                IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN
  --RAISE WARNING 'node in relation %',relation;
-                 SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint 
+                 SELECT place_id from placex where osm_type = 'W'
+                   and osm_id = substring(relation.members[i],2,200)::bigint
                    and rank_search = 26 and name is not null INTO NEW.parent_place_id;
                END IF;
              END LOOP;
            END IF;
-         END LOOP;      
+         END LOOP;
+       -- Note that addr:street links can only be indexed once the street itself is indexed
+        IF NEW.parent_place_id IS NULL AND NEW.street IS NOT NULL THEN
+         address_street_word_ids := get_name_ids(make_standard_name(NEW.street));
+         IF address_street_word_ids IS NOT NULL THEN
+           FOR location IN SELECT * from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
+               NEW.parent_place_id := location.place_id;
+           END LOOP;
+         END IF;
+       END IF;
+       IF NEW.parent_place_id IS NULL AND NEW.addr_place IS NOT NULL THEN
+         address_street_word_ids := get_name_ids(make_standard_name(NEW.addr_place));
+         IF address_street_word_ids IS NOT NULL THEN
+           FOR location IN SELECT * from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
+             NEW.parent_place_id := location.place_id;
+           END LOOP;
+         END IF;
+       END IF;
+       IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'N' THEN
  
  --RAISE WARNING 'x1';
          -- Is this node part of a way?
-         FOR way IN select id from planet_osm_ways where nodes @> ARRAY[NEW.osm_id] LOOP
- --RAISE WARNING '%', way;
-         FOR location IN select * from placex where osm_type = 'W' and osm_id = way.id
+         FOR location IN select p.* from placex p, planet_osm_ways w
+            where p.osm_type = 'W' and p.rank_search >= 26
+              and p.geometry && NEW.geometry and p.osm_id = w.id and NEW.osm_id = any(w.nodes)
          LOOP
  --RAISE WARNING '%', location;
            -- Way IS a road then we are on it - that must be our road
              NEW.parent_place_id := location.place_id;
            END IF;
  
+           -- If this way is a street interpolation line then it is probably as good as we are going to get
+           IF NEW.parent_place_id IS NULL AND location.class = 'place' and location.type='houses' THEN
+             NEW.parent_place_id := location.parent_place_id;
+           END IF;
            -- Is the WAY part of a relation
            IF NEW.parent_place_id IS NULL THEN
                FOR relation IN select * from planet_osm_rels where parts @> ARRAY[location.osm_id] and members @> ARRAY['w'||location.osm_id]
                    END LOOP;
                  END IF;
                END LOOP;
-           END IF;    
-           
-           -- If the way contains an explicit name of a street copy it
-           -- Slightly less strict then above because data is copied from any object.
-           IF NEW.street IS NULL AND NEW.addr_place IS NULL THEN
- --RAISE WARNING 'node in way that has a streetname %',location;
-             NEW.street := location.street;
-             NEW.addr_place := location.addr_place;
            END IF;
  
-           -- If this way is a street interpolation line then it is probably as good as we are going to get
-           IF NEW.parent_place_id IS NULL AND NEW.street IS NULL AND NEW.addr_place IS NULL AND location.class = 'place' and location.type='houses' THEN
-             -- Try and find a way that is close roughly parellel to this line
-             FOR relation IN SELECT place_id FROM placex
-               WHERE ST_DWithin(location.geometry, placex.geometry, 0.001) and placex.rank_search = 26
-                 and st_geometrytype(location.geometry) in ('ST_LineString')
-               ORDER BY (ST_distance(placex.geometry, ST_LineInterpolatePoint(location.geometry,0))+
-                         ST_distance(placex.geometry, ST_LineInterpolatePoint(location.geometry,0.5))+
-                         ST_distance(placex.geometry, ST_LineInterpolatePoint(location.geometry,1))) ASC limit 1
-             LOOP
- --RAISE WARNING 'using nearest street to address interpolation line,0.001 %',relation;
-               NEW.parent_place_id := relation.place_id;
-             END LOOP;
+           -- If the way mentions a street or place address, try that for parenting.
+           IF NEW.parent_place_id IS NULL AND location.street IS NOT NULL THEN
+             address_street_word_ids := get_name_ids(make_standard_name(location.street));
+             IF address_street_word_ids IS NOT NULL THEN
+               FOR linkedplacex IN SELECT place_id from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
+                   NEW.parent_place_id := linkedplacex.place_id;
+               END LOOP;
+             END IF;
            END IF;
  
-         END LOOP;
-         END LOOP;
-                 
-       END IF;
- --RAISE WARNING 'x2';
-       IF NEW.parent_place_id IS NULL AND NEW.osm_type = 'W' THEN
-         -- Is this way part of a relation?
-         FOR relation IN select * from planet_osm_rels where parts @> ARRAY[NEW.osm_id] and members @> ARRAY['w'||NEW.osm_id]
-         LOOP
-           -- At the moment we only process one type of relation - associatedStreet
-           IF relation.tags @> ARRAY['associatedStreet'] AND array_upper(relation.members, 1) IS NOT NULL THEN
-             FOR i IN 1..array_upper(relation.members, 1) BY 2 LOOP
-               IF NEW.parent_place_id IS NULL AND relation.members[i+1] = 'street' THEN
- --RAISE WARNING 'way that is in a relation %',relation;
-                 SELECT place_id from placex where osm_type='W' and osm_id = substring(relation.members[i],2,200)::bigint
-                   and rank_search = 26 and name is not null INTO NEW.parent_place_id;
-               END IF;
-             END LOOP;
+           IF NEW.parent_place_id IS NULL AND location.addr_place IS NOT NULL THEN
+             address_street_word_ids := get_name_ids(make_standard_name(location.addr_place));
+             IF address_street_word_ids IS NOT NULL THEN
+               FOR linkedplacex IN SELECT place_id from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
+                 NEW.parent_place_id := linkedplacex.place_id;
+               END LOOP;
+             END IF;
            END IF;
-         END LOOP;
-       END IF;
-       
- --RAISE WARNING 'x3 %',NEW.parent_place_id;
  
-       IF NEW.parent_place_id IS NULL AND NEW.street IS NOT NULL THEN
-         address_street_word_ids := get_name_ids(make_standard_name(NEW.street));
-         IF address_street_word_ids IS NOT NULL THEN
-           FOR location IN SELECT * from getNearestNamedRoadFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
-               NEW.parent_place_id := location.place_id;
-           END LOOP;
-         END IF;
-       END IF;
+         END LOOP;
  
-       IF NEW.parent_place_id IS NULL AND NEW.addr_place IS NOT NULL THEN
-         address_street_word_ids := get_name_ids(make_standard_name(NEW.addr_place));
-         IF address_street_word_ids IS NOT NULL THEN
-           FOR location IN SELECT * from getNearestNamedPlaceFeature(NEW.partition, place_centroid, address_street_word_ids) LOOP
-             NEW.parent_place_id := location.place_id;
-           END LOOP;
-         END IF;
        END IF;
  
  --RAISE WARNING 'x4 %',NEW.parent_place_id;
@@@ -2016,6 -2087,11 +2087,11 @@@ BEGI
    -- mark for delete
    UPDATE placex set indexed_status = 100 where osm_type = OLD.osm_type and osm_id = OLD.osm_id and class = OLD.class and type = OLD.type;
  
+   -- interpolations are special
+   IF OLD.class = 'place' and OLD.type = 'houses' THEN
+     UPDATE placex set indexed_status = 100 where osm_type = OLD.osm_type and osm_id = OLD.osm_id and class = 'place' and type = 'address';
+   END IF;
    RETURN OLD;
  
  END;
@@@ -2042,11 -2118,6 +2118,11 @@@ BEGI
      --DEBUG: RAISE WARNING '%', existingplacex;
    END IF;
  
 +  -- remove operator tag for most places, messes too much with search_name indexes
 +  IF NEW.class not in ('amenity', 'shop') THEN
 +    NEW.name := delete(NEW.name, 'operator');
 +  END IF;
 +
    -- Just block these - lots and pointless
    IF NEW.class in ('landuse','natural') and NEW.name is null THEN
      -- if the name tag was removed, older versions might still be lurking in the place table
      END IF;
  
    END IF;
 +
 +  -- refuse to update multiplpoygons with too many objects, too much of a performance hit
 +  IF ST_NumGeometries(NEW.geometry) > 2000 THEN
 +    RAISE WARNING 'Dropping update of % % because of geometry complexity.', NEW.osm_type, NEW.osm_id;
 +    RETURN NULL;
 +  END IF;
  
    IF coalesce(existing.name::text, '') != coalesce(NEW.name::text, '')
       OR coalesce(existing.extratags::text, '') != coalesce(NEW.extratags::text, '')
  
    END IF;
  
+   -- for interpolations invalidate all nodes on the line
+   IF NEW.class = 'place' and NEW.type = 'houses' and NEW.osm_type = 'W' THEN
+     update placex p set indexed_status = 2 from planet_osm_ways w where w.id = NEW.osm_id and p.osm_type = 'N' and p.osm_id = any(w.nodes);
+   END IF;
    -- Abort the add (we modified the existing place instead)
    RETURN NULL;
  
diff --combined sql/tables.sql
index 428eadc2d7c2c8d950532944fcd19b476afcad35,9126c6299e35b1e50986ff137ba350bc073828fe..dcee40700036bcc07707a60e988f9e66fd1da444
@@@ -23,6 -23,19 +23,6 @@@ CREATE TABLE import_npi_log 
    event text
    );
  
 ---drop table IF EXISTS query_log;
 -CREATE TABLE query_log (
 -  starttime timestamp,
 -  query text,
 -  ipaddress text,
 -  endtime timestamp,
 -  results integer
 -  );
 -CREATE INDEX idx_query_log ON query_log USING BTREE (starttime);
 -GRANT SELECT ON query_log TO "{www-user}" ;
 -GRANT INSERT ON query_log TO "{www-user}" ;
 -GRANT UPDATE ON query_log TO "{www-user}" ;
 -
  CREATE TABLE new_query_log (
    type text,
    starttime timestamp,
@@@ -30,7 -43,6 +30,7 @@@
    useragent text,
    language text,
    query text,
 +  searchterm text,
    endtime timestamp,
    results integer,
    format text,
@@@ -41,7 -53,12 +41,9 @@@ GRANT INSERT ON new_query_log TO "{www-
  GRANT UPDATE ON new_query_log TO "{www-user}" ;
  GRANT SELECT ON new_query_log TO "{www-user}" ;
  
+ GRANT SELECT ON TABLE country_name TO "{www-user}";
+ GRANT SELECT ON TABLE gb_postcode TO "{www-user}";
  
 -create view vw_search_query_log as SELECT substr(query, 1, 50) AS query, starttime, endtime - starttime AS duration, substr(useragent, 1, 20) as 
 -useragent, language, results, ipaddress FROM new_query_log WHERE type = 'search' ORDER BY starttime DESC;
 -
  drop table IF EXISTS word;
  CREATE TABLE word (
    word_id INTEGER,