From c2d6821f2fb71dc4d3cb2d80dd373990378f6621 Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Mon, 16 Feb 2026 16:25:19 +0100 Subject: [PATCH] adapt interpolation handling to use separate place_interpolation table --- lib-sql/functions/interpolation.sql | 443 +++++++++++++++----------- lib-sql/functions/place_triggers.sql | 59 ---- lib-sql/functions/placex_triggers.sql | 10 +- lib-sql/functions/utils.sql | 24 +- lib-sql/indices.sql | 17 +- lib-sql/table-triggers.sql | 7 +- lib-sql/tables.sql | 5 - 7 files changed, 296 insertions(+), 269 deletions(-) diff --git a/lib-sql/functions/interpolation.sql b/lib-sql/functions/interpolation.sql index 7e8877cc..a31c1793 100644 --- a/lib-sql/functions/interpolation.sql +++ b/lib-sql/functions/interpolation.sql @@ -2,11 +2,106 @@ -- -- This file is part of Nominatim. (https://nominatim.org) -- --- Copyright (C) 2022 by the Nominatim developer community. +-- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Functions for address interpolation objects in location_property_osmline. +CREATE OR REPLACE FUNCTION place_interpolation_insert() + RETURNS TRIGGER + AS $$ +DECLARE + existing RECORD; + existingplacex BIGINT[]; + +BEGIN + -- Remove the place from the list of places to be deleted + DELETE FROM place_interpolation_to_be_deleted pdel + WHERE pdel.osm_type = NEW.osm_type and pdel.osm_id = NEW.osm_id; + + SELECT * INTO existing FROM place_interpolation p + WHERE p.osm_type = NEW.osm_type AND p.osm_id = NEW.osm_id; + + IF NOT (NEW.type in ('odd', 'even', 'all') OR NEW.type similar to '[1-9]') THEN + -- the new interpolation is illegal, simply remove existing entries + DELETE FROM location_property_osmline o + WHERE o.osm_type = NEW.osm_type AND o.osm_id = NEW.osm_id; + ELSE + -- Get the existing entry from the interpolation table. + SELECT array_agg(place_id) INTO existingplacex + FROM location_property_osmline o + WHERE o.osm_type = NEW.osm_type AND o.osm_id = NEW.osm_id; + + IF array_length(existingplacex, 1) is NULL THEN + INSERT INTO location_property_osmline (osm_type, osm_id, type, address, linegeo) + VALUES (NEW.osm_type, NEW.osm_id, NEW.type, NEW.address, NEW.geometry); + ELSE + -- Update the interpolation table: + -- The first entry gets the original data, all other entries + -- are removed and will be recreated on indexing. + -- (An interpolation can be split up, if it has more than 2 address nodes) + -- Update unconditionally here as the changes might be coming from the + -- nodes on the interpolation. + UPDATE location_property_osmline + SET type = NEW.type, + address = NEW.address, + linegeo = NEW.geometry, + startnumber = null, + indexed_status = 1 + WHERE place_id = existingplacex[1]; + IF array_length(existingplacex, 1) > 1 THEN + DELETE FROM location_property_osmline + WHERE place_id = any(existingplacex[2:]); + END IF; + END IF; + + -- need to invalidate nodes because they might copy address info + IF NEW.address is not NULL + AND (existing.osm_type is NULL + OR coalesce(existing.address, ''::hstore) != NEW.address) + THEN + UPDATE placex SET indexed_status = 2 + WHERE osm_type = 'N' AND osm_id = ANY(NEW.nodes) + AND indexed_status = 0; + END IF; + END IF; + + -- finally update/insert place_interpolation itself + + IF existing.osm_type is not NULL THEN + IF existing.type != NEW.type + OR coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore) + OR existing.geometry::text != NEW.geometry::text + THEN + UPDATE place_interpolation p + SET type = NEW.type, + address = NEW.address, + geometry = NEW.geometry + WHERE p.osm_type = NEW.osm_type AND p.osm_id = NEW.osm_id; + END IF; + + RETURN NULL; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION place_interpolation_delete() + RETURNS TRIGGER + AS $$ +DECLARE + deferred BOOLEAN; +BEGIN + {% if debug %}RAISE WARNING 'Delete for % % %/%', OLD.osm_type, OLD.osm_id, OLD.class, OLD.type;{% endif %} + + INSERT INTO place_interpolation_to_be_deleted (osm_type, osm_id) + VALUES(OLD.osm_type, OLD.osm_id); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_interpolation_address(in_address HSTORE, wayid BIGINT) RETURNS HSTORE @@ -28,7 +123,7 @@ BEGIN and indexed_status < 100 LOOP -- mark it as a derived address - RETURN location.address || in_address || hstore('_inherited', ''); + RETURN location.address || coalesce(in_address, '{}'::hstore) || hstore('_inherited', ''); END LOOP; RETURN in_address; @@ -73,51 +168,6 @@ $$ LANGUAGE plpgsql STABLE PARALLEL SAFE; -CREATE OR REPLACE FUNCTION reinsert_interpolation(way_id BIGINT, addr HSTORE, - geom GEOMETRY) - RETURNS INT - AS $$ -DECLARE - existing BIGINT[]; -BEGIN - IF addr is NULL OR NOT addr ? 'interpolation' - OR NOT (addr->'interpolation' in ('odd', 'even', 'all') - or addr->'interpolation' similar to '[1-9]') - THEN - -- the new interpolation is illegal, simply remove existing entries - DELETE FROM location_property_osmline WHERE osm_id = way_id; - ELSE - -- Get the existing entry from the interpolation table. - SELECT array_agg(place_id) INTO existing - FROM location_property_osmline WHERE osm_id = way_id; - - IF existing IS NULL or array_length(existing, 1) = 0 THEN - INSERT INTO location_property_osmline (osm_id, address, linegeo) - VALUES (way_id, addr, geom); - ELSE - -- Update the interpolation table: - -- The first entry gets the original data, all other entries - -- are removed and will be recreated on indexing. - -- (An interpolation can be split up, if it has more than 2 address nodes) - UPDATE location_property_osmline - SET address = addr, - linegeo = geom, - startnumber = null, - indexed_status = 1 - WHERE place_id = existing[1]; - IF array_length(existing, 1) > 1 THEN - DELETE FROM location_property_osmline - WHERE place_id = any(existing[2:]); - END IF; - END IF; - END IF; - - RETURN 1; -END; -$$ -LANGUAGE plpgsql; - - CREATE OR REPLACE FUNCTION osmline_insert() RETURNS TRIGGER AS $$ @@ -128,20 +178,32 @@ BEGIN NEW.indexed_date := now(); IF NEW.indexed_status IS NULL THEN - IF NEW.address is NULL OR NOT NEW.address ? 'interpolation' - OR NOT (NEW.address->'interpolation' in ('odd', 'even', 'all') - or NEW.address->'interpolation' similar to '[1-9]') - THEN - -- alphabetic interpolation is not supported + IF NOT(NEW.type in ('odd', 'even', 'all') OR NEW.type similar to '[1-9]') THEN + -- alphabetic interpolation is not supported + RETURN NULL; + END IF; + + IF NEW.address is not NULL AND NEW.address ? 'housenumber' THEN + IF NEW.address->'housenumber' not similar to '[0-9]+-[0-9]+' THEN + -- housenumber needs to look like an interpolation RETURN NULL; END IF; - NEW.indexed_status := 1; --STATUS_NEW + centroid := ST_Centroid(NEW.linegeo); + -- interpolation of a housenumber, make sure we have a line to interpolate on + NEW.geometry := ST_MakeLine(centroid, ST_Project(centroid, 0.0000001, 0)); + ELSE centroid := get_center_point(NEW.linegeo); - NEW.country_code := lower(get_country_code(centroid)); + IF NEW.osm_type != 'W' THEN + RETURN NULL; + END IF; + END IF; - NEW.partition := get_partition(NEW.country_code); - NEW.geometry_sector := geometry_sector(NEW.partition, centroid); + NEW.indexed_status := 1; --STATUS_NEW + NEW.country_code := lower(get_country_code(centroid)); + + NEW.partition := get_partition(NEW.country_code); + NEW.geometry_sector := geometry_sector(NEW.partition, centroid); END IF; RETURN NEW; @@ -167,6 +229,7 @@ DECLARE sectiongeo GEOMETRY; postcode TEXT; stepmod SMALLINT; + splitstring TEXT[]; BEGIN -- deferred delete IF OLD.indexed_status = 100 THEN @@ -182,157 +245,173 @@ BEGIN get_center_point(NEW.linegeo), NEW.linegeo); - -- Cannot find a parent street. We will not be able to display a reliable - -- address, so drop entire interpolation. - IF NEW.parent_place_id is NULL THEN - DELETE FROM location_property_osmline where place_id = OLD.place_id; - RETURN NULL; - END IF; - NEW.token_info := token_strip_info(NEW.token_info); IF NEW.address ? '_inherited' THEN - NEW.address := hstore('interpolation', NEW.address->'interpolation'); + NEW.address := NULL; END IF; -- If the line was newly inserted, split the line as necessary. - IF OLD.indexed_status = 1 THEN - IF NEW.address->'interpolation' in ('odd', 'even') THEN + IF NEW.parent_place_id is not NULL AND NEW.startnumber is NULL THEN + IF NEW.type in ('odd', 'even') THEN NEW.step := 2; - stepmod := CASE WHEN NEW.address->'interpolation' = 'odd' THEN 1 ELSE 0 END; + stepmod := CASE WHEN NEW.type = 'odd' THEN 1 ELSE 0 END; ELSE - NEW.step := CASE WHEN NEW.address->'interpolation' = 'all' - THEN 1 - ELSE (NEW.address->'interpolation')::SMALLINT END; + NEW.step := CASE WHEN NEW.type = 'all' THEN 1 ELSE (NEW.type)::SMALLINT END; stepmod := NULL; END IF; - SELECT nodes INTO waynodes - FROM planet_osm_ways WHERE id = NEW.osm_id; + IF NEW.address is not NULL AND NEW.address ? 'housenumer' THEN + -- interpolation interval is in housenumber + splitstring := string_to_array(NEW.address->'housenumber', '-'); + NEW.startnumber := (splitstring[1])::INTEGER; + NEW.endnumber := (splitstring[2])::INTEGER; - IF array_upper(waynodes, 1) IS NULL THEN - RETURN NEW; - END IF; - - linegeo := null; - SELECT null::integer as hnr INTO prevnode; - - -- Go through all nodes on the interpolation line that have a housenumber. - FOR nextnode IN - SELECT DISTINCT ON (nodeidpos) - osm_id, address, geometry, - -- Take the postcode from the node only if it has a housenumber itself. - -- Note that there is a corner-case where the node has a wrongly - -- formatted postcode and therefore 'postcode' contains a derived - -- variant. - CASE WHEN address ? 'postcode' THEN placex.postcode ELSE NULL::text END as postcode, - (address->'housenumber')::integer as hnr - FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos - WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT - and address is not NULL and address ? 'housenumber' - and address->'housenumber' ~ '^[0-9]{1,6}$' - and ST_Distance(NEW.linegeo, geometry) < 0.0005 - ORDER BY nodeidpos - LOOP - {% if debug %}RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);{% endif %} - IF linegeo is null THEN - linegeo := NEW.linegeo; - ELSE - splitpoint := ST_LineLocatePoint(linegeo, nextnode.geometry); - IF splitpoint = 0 THEN - -- Corner case where the splitpoint falls on the first point - -- and thus would not return a geometry. Skip that section. - sectiongeo := NULL; - ELSEIF splitpoint = 1 THEN - -- Point is at the end of the line. - sectiongeo := linegeo; - linegeo := NULL; - ELSE - -- Split the line. - sectiongeo := ST_LineSubstring(linegeo, 0, splitpoint); - linegeo := ST_LineSubstring(linegeo, splitpoint, 1); + IF stepmod is not NULL THEN + IF NEW.startnumber % NEW.step != stepmod THEN + NEW.startnumber := NEW.startnumber + 1; END IF; + IF NEW.endnumber % NEW.step != stepmod THEN + NEW.endnumber := NEW.endnumber - 1; + END IF; + ELSE + NEW.endnumber := NEW.startnumber + ((NEW.endnumber - NEW.startnumber) / NEW.step) * NEW.step; END IF; - IF prevnode.hnr is not null - -- Check if there are housenumbers to interpolate between the - -- regularly mapped housenumbers. - -- (Conveniently also fails if one of the house numbers is not a number.) - and abs(prevnode.hnr - nextnode.hnr) > NEW.step - -- If the interpolation geometry is broken or two nodes are at the - -- same place, then splitting might produce a point. Ignore that. - and ST_GeometryType(sectiongeo) = 'ST_LineString' - THEN - IF prevnode.hnr < nextnode.hnr THEN - startnumber := prevnode.hnr; - endnumber := nextnode.hnr; - ELSE - startnumber := nextnode.hnr; - endnumber := prevnode.hnr; - sectiongeo := ST_Reverse(sectiongeo); - END IF; + IF NEW.startnumber = NEW.endnumber THEN + NEW.geometry := ST_PointN(NEW.geometry, 1); + ELSEIF NEW.startnumber > NEW.endnumber THEN + NEW.startnumber := NULL; + END IF; + ELSE + -- classic interpolation way + SELECT nodes INTO waynodes + FROM planet_osm_ways WHERE id = NEW.osm_id; - -- Adjust the interpolation, so that only inner housenumbers - -- are taken into account. - IF stepmod is null THEN - newstart := startnumber + NEW.step; + IF array_upper(waynodes, 1) IS NULL THEN + RETURN NEW; + END IF; + + linegeo := null; + SELECT null::integer as hnr INTO prevnode; + + -- Go through all nodes on the interpolation line that have a housenumber. + FOR nextnode IN + SELECT DISTINCT ON (nodeidpos) + osm_id, address, geometry, + -- Take the postcode from the node only if it has a housenumber itself. + -- Note that there is a corner-case where the node has a wrongly + -- formatted postcode and therefore 'postcode' contains a derived + -- variant. + CASE WHEN address ? 'postcode' THEN placex.postcode ELSE NULL::text END as postcode, + (address->'housenumber')::integer as hnr + FROM placex, generate_series(1, array_upper(waynodes, 1)) nodeidpos + WHERE osm_type = 'N' and osm_id = waynodes[nodeidpos]::BIGINT + and address is not NULL and address ? 'housenumber' + and address->'housenumber' ~ '^[0-9]{1,6}$' + and ST_Distance(NEW.linegeo, geometry) < 0.0005 + ORDER BY nodeidpos + LOOP + {% if debug %}RAISE WARNING 'processing point % (%)', nextnode.hnr, ST_AsText(nextnode.geometry);{% endif %} + IF linegeo is null THEN + linegeo := NEW.linegeo; ELSE - newstart := startnumber + 1; - moddiff := newstart % NEW.step - stepmod; - IF moddiff < 0 THEN - newstart := newstart + (NEW.step + moddiff); + splitpoint := ST_LineLocatePoint(linegeo, nextnode.geometry); + IF splitpoint = 0 THEN + -- Corner case where the splitpoint falls on the first point + -- and thus would not return a geometry. Skip that section. + sectiongeo := NULL; + ELSEIF splitpoint = 1 THEN + -- Point is at the end of the line. + sectiongeo := linegeo; + linegeo := NULL; ELSE - newstart := newstart + moddiff; + -- Split the line. + sectiongeo := ST_LineSubstring(linegeo, 0, splitpoint); + linegeo := ST_LineSubstring(linegeo, splitpoint, 1); END IF; END IF; - newend := newstart + ((endnumber - 1 - newstart) / NEW.step) * NEW.step; - -- If newstart and newend are the same, then this returns a point. - sectiongeo := ST_LineSubstring(sectiongeo, - (newstart - startnumber)::float / (endnumber - startnumber)::float, - (newend - startnumber)::float / (endnumber - startnumber)::float); - startnumber := newstart; - endnumber := newend; + IF prevnode.hnr is not null + -- Check if there are housenumbers to interpolate between the + -- regularly mapped housenumbers. + -- (Conveniently also fails if one of the house numbers is not a number.) + and abs(prevnode.hnr - nextnode.hnr) > NEW.step + -- If the interpolation geometry is broken or two nodes are at the + -- same place, then splitting might produce a point. Ignore that. + and ST_GeometryType(sectiongeo) = 'ST_LineString' + THEN + IF prevnode.hnr < nextnode.hnr THEN + startnumber := prevnode.hnr; + endnumber := nextnode.hnr; + ELSE + startnumber := nextnode.hnr; + endnumber := prevnode.hnr; + sectiongeo := ST_Reverse(sectiongeo); + END IF; - -- determine postcode - postcode := coalesce(prevnode.postcode, nextnode.postcode, postcode); - IF postcode is NULL and NEW.parent_place_id > 0 THEN - SELECT placex.postcode FROM placex - WHERE place_id = NEW.parent_place_id INTO postcode; - END IF; - IF postcode is NULL THEN - postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry); + -- Adjust the interpolation, so that only inner housenumbers + -- are taken into account. + IF stepmod is null THEN + newstart := startnumber + NEW.step; + ELSE + newstart := startnumber + 1; + moddiff := newstart % NEW.step - stepmod; + IF moddiff < 0 THEN + newstart := newstart + (NEW.step + moddiff); + ELSE + newstart := newstart + moddiff; + END IF; + END IF; + newend := newstart + ((endnumber - 1 - newstart) / NEW.step) * NEW.step; + + -- If newstart and newend are the same, then this returns a point. + sectiongeo := ST_LineSubstring(sectiongeo, + (newstart - startnumber)::float / (endnumber - startnumber)::float, + (newend - startnumber)::float / (endnumber - startnumber)::float); + startnumber := newstart; + endnumber := newend; + + -- determine postcode + postcode := coalesce(prevnode.postcode, nextnode.postcode, postcode); + IF postcode is NULL and NEW.parent_place_id > 0 THEN + SELECT placex.postcode FROM placex + WHERE place_id = NEW.parent_place_id INTO postcode; + END IF; + IF postcode is NULL THEN + postcode := get_nearest_postcode(NEW.country_code, nextnode.geometry); + END IF; + + -- Add the interpolation. If this is the first segment, just modify + -- the interpolation to be inserted, otherwise add an additional one + -- (marking it indexed already). + IF NEW.startnumber IS NULL THEN + NEW.startnumber := startnumber; + NEW.endnumber := endnumber; + NEW.linegeo := ST_ReducePrecision(sectiongeo, 0.0000001); + NEW.postcode := postcode; + ELSE + INSERT INTO location_property_osmline + (linegeo, partition, osm_type, osm_id, parent_place_id, + startnumber, endnumber, step, type, + address, postcode, country_code, + geometry_sector, indexed_status) + VALUES (ST_ReducePrecision(sectiongeo, 0.0000001), + NEW.partition, NEW.osm_type, NEW.osm_id, NEW.parent_place_id, + startnumber, endnumber, NEW.step, NEW.type, + NEW.address, postcode, + NEW.country_code, NEW.geometry_sector, 0); + END IF; END IF; - -- Add the interpolation. If this is the first segment, just modify - -- the interpolation to be inserted, otherwise add an additional one - -- (marking it indexed already). - IF NEW.startnumber IS NULL THEN - NEW.startnumber := startnumber; - NEW.endnumber := endnumber; - NEW.linegeo := ST_ReducePrecision(sectiongeo, 0.0000001); - NEW.postcode := postcode; - ELSE - INSERT INTO location_property_osmline - (linegeo, partition, osm_id, parent_place_id, - startnumber, endnumber, step, - address, postcode, country_code, - geometry_sector, indexed_status) - VALUES (ST_ReducePrecision(sectiongeo, 0.0000001), - NEW.partition, NEW.osm_id, NEW.parent_place_id, - startnumber, endnumber, NEW.step, - NEW.address, postcode, - NEW.country_code, NEW.geometry_sector, 0); + -- early break if we are out of line string, + -- might happen when a line string loops back on itself + IF linegeo is null or ST_GeometryType(linegeo) != 'ST_LineString' THEN + RETURN NEW; END IF; - END IF; - -- early break if we are out of line string, - -- might happen when a line string loops back on itself - IF linegeo is null or ST_GeometryType(linegeo) != 'ST_LineString' THEN - RETURN NEW; - END IF; - - prevnode := nextnode; - END LOOP; + prevnode := nextnode; + END LOOP; + END IF; END IF; RETURN NEW; diff --git a/lib-sql/functions/place_triggers.sql b/lib-sql/functions/place_triggers.sql index e102128a..7ebae7d5 100644 --- a/lib-sql/functions/place_triggers.sql +++ b/lib-sql/functions/place_triggers.sql @@ -14,7 +14,6 @@ DECLARE existing RECORD; existingplacex RECORD; existingline BIGINT[]; - interpol RECORD; BEGIN {% if debug %} RAISE WARNING 'place_insert: % % % % %',NEW.osm_type,NEW.osm_id,NEW.class,NEW.type,st_area(NEW.geometry); @@ -55,41 +54,6 @@ BEGIN DELETE from import_polygon_error where osm_type = NEW.osm_type and osm_id = NEW.osm_id; DELETE from import_polygon_delete where osm_type = NEW.osm_type and osm_id = NEW.osm_id; - -- ---- Interpolation Lines - - IF NEW.class='place' and NEW.type='houses' - and NEW.osm_type='W' and ST_GeometryType(NEW.geometry) = 'ST_LineString' - THEN - PERFORM reinsert_interpolation(NEW.osm_id, NEW.address, NEW.geometry); - - -- Now invalidate all address nodes on the line. - -- They get their parent from the interpolation. - 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) - and indexed_status = 0; - - -- If there is already an entry in place, just update that, if necessary. - IF existing.osm_type is not null THEN - IF coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore) - OR existing.geometry::text != NEW.geometry::text - THEN - UPDATE place - SET name = NEW.name, - address = NEW.address, - extratags = NEW.extratags, - admin_level = NEW.admin_level, - geometry = NEW.geometry - WHERE osm_type = NEW.osm_type and osm_id = NEW.osm_id - and class = NEW.class and type = NEW.type; - END IF; - - RETURN NULL; - END IF; - - RETURN NEW; - END IF; - -- ---- All other place types. -- When an area is changed from large to small: log and discard change @@ -109,29 +73,6 @@ BEGIN RETURN null; END IF; - -- If an address node is part of a interpolation line and changes or is - -- newly inserted (happens when the node already existed but now gets address - -- information), then mark the interpolation line for reparenting. - -- (Already here, because interpolation lines are reindexed before nodes, - -- so in the second call it would be too late.) - IF NEW.osm_type='N' - and coalesce(existing.address, ''::hstore) != coalesce(NEW.address, ''::hstore) - THEN - FOR interpol IN - SELECT DISTINCT osm_id, address, geometry FROM place, planet_osm_ways w - WHERE NEW.geometry && place.geometry - and place.osm_type = 'W' - and place.address ? 'interpolation' - and exists (SELECT * FROM location_property_osmline - WHERE osm_id = place.osm_id - and indexed_status in (0, 2)) - and w.id = place.osm_id and NEW.osm_id = any (w.nodes) - LOOP - PERFORM reinsert_interpolation(interpol.osm_id, interpol.address, - interpol.geometry); - END LOOP; - END IF; - -- Get the existing placex entry. SELECT * INTO existingplacex FROM placex diff --git a/lib-sql/functions/placex_triggers.sql b/lib-sql/functions/placex_triggers.sql index abc4aae9..00868589 100644 --- a/lib-sql/functions/placex_triggers.sql +++ b/lib-sql/functions/placex_triggers.sql @@ -53,12 +53,10 @@ BEGIN -- See if we can inherit additional address tags from an interpolation. -- These will become permanent. FOR location IN - SELECT (address - 'interpolation'::text - 'housenumber'::text) as address - FROM place, planet_osm_ways w - WHERE place.osm_type = 'W' and place.address ? 'interpolation' - and place.geometry && p.geometry - and place.osm_id = w.id - and p.osm_id = any(w.nodes) + SELECT address as address + FROM place_interpolation + WHERE p.osm_id = any(place_interpolation.nodes) + AND address is not NULL AND not address ? 'housenumber' LOOP result.address := location.address || result.address; END LOOP; diff --git a/lib-sql/functions/utils.sql b/lib-sql/functions/utils.sql index bc2dfdca..878301a0 100644 --- a/lib-sql/functions/utils.sql +++ b/lib-sql/functions/utils.sql @@ -624,18 +624,22 @@ BEGIN and placex.type = place_to_be_deleted.type and not deferred; - -- Mark for delete in interpolations - UPDATE location_property_osmline SET indexed_status = 100 FROM place_to_be_deleted - WHERE place_to_be_deleted.osm_type = 'W' - and place_to_be_deleted.class = 'place' - and place_to_be_deleted.type = 'houses' - and location_property_osmline.osm_id = place_to_be_deleted.osm_id - and not deferred; + -- Clear todo list. + TRUNCATE TABLE place_to_be_deleted; - -- Clear todo list. - TRUNCATE TABLE place_to_be_deleted; + -- delete from place_interpolation table + ALTER TABLE place_interpolation DISABLE TRIGGER place_interpolation_before_delete; + DELETE FROM place_interpolation p USING place_interpolation_to_be_deleted d + WHERE p.osm_type = d.osm_type AND p.osm_id = d.osm_id; + ALTER TABLE place_interpolation ENABLE TRIGGER place_interpolation_before_delete; - RETURN NULL; + UPDATE location_property_osmline o SET indexed_status = 100 + FROM place_interpolation_to_be_deleted d + WHERE o.osm_type = d.osm_Type AND o.osm_id = d.osm_id; + + TRUNCATE TABLE place_interpolation_to_be_deleted; + + RETURN NULL; END; $$ LANGUAGE plpgsql; diff --git a/lib-sql/indices.sql b/lib-sql/indices.sql index c23f07ae..f3b79705 100644 --- a/lib-sql/indices.sql +++ b/lib-sql/indices.sql @@ -2,7 +2,7 @@ -- -- This file is part of Nominatim. (https://nominatim.org) -- --- Copyright (C) 2025 by the Nominatim developer community. +-- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- Indices used only during search and update. @@ -67,11 +67,16 @@ CREATE INDEX IF NOT EXISTS idx_osmline_parent_osm_id --- -- Table needed for running updates with osm2pgsql on place. CREATE TABLE IF NOT EXISTS place_to_be_deleted ( - osm_type CHAR(1), - osm_id BIGINT, - class TEXT, - type TEXT, - deferred BOOLEAN + osm_type CHAR(1) NOT NULL, + osm_id BIGINT NOT NULL, + class TEXT NOT NULL, + type TEXT NOT NULL, + deferred BOOLEAN NOT NULL + ); + + CREATE TABLE IF NOT EXISTS place_interpolation_to_be_deleted ( + osm_type CHAR(1) NOT NULL, + osm_id BIGINT NOT NULL ); --- CREATE INDEX IF NOT EXISTS idx_location_postcodes_parent_place_id diff --git a/lib-sql/table-triggers.sql b/lib-sql/table-triggers.sql index f9c3f890..eba6ac0e 100644 --- a/lib-sql/table-triggers.sql +++ b/lib-sql/table-triggers.sql @@ -2,7 +2,7 @@ -- -- This file is part of Nominatim. (https://nominatim.org) -- --- Copyright (C) 2025 by the Nominatim developer community. +-- Copyright (C) 2026 by the Nominatim developer community. -- For a full list of authors see the git log. -- insert creates the location tables, creates location indexes if indexed == true @@ -31,3 +31,8 @@ CREATE TRIGGER location_postcodes_before_delete BEFORE DELETE ON location_postco FOR EACH ROW EXECUTE PROCEDURE postcodes_delete(); CREATE TRIGGER location_postcodes_before_insert BEFORE INSERT ON location_postcodes FOR EACH ROW EXECUTE PROCEDURE postcodes_insert(); + +CREATE TRIGGER place_interpolation_before_insert BEFORE INSERT ON place_interpolation + FOR EACH ROW EXECUTE PROCEDURE place_interpolation_insert(); +CREATE TRIGGER place_interpolation_before_delete BEFORE DELETE ON place_interpolation + FOR EACH ROW EXECUTE PROCEDURE place_interpolation_delete(); diff --git a/lib-sql/tables.sql b/lib-sql/tables.sql index c9ccb032..023486a5 100644 --- a/lib-sql/tables.sql +++ b/lib-sql/tables.sql @@ -32,8 +32,3 @@ CREATE INDEX planet_osm_rels_relation_members_idx ON planet_osm_rels USING gin(p WITH (fastupdate=off) {{db.tablespace.address_index}}; {% endif %} - --- Needed for lookups if a node is part of an interpolation. -CREATE INDEX IF NOT EXISTS idx_place_interpolations - ON place USING gist(geometry) {{db.tablespace.address_index}} - WHERE osm_type = 'W' and address ? 'interpolation';