[postgis-tickets] [SCM] PostGIS branch main updated. 3.1.0rc1-341-g168f3f4

git at osgeo.org git at osgeo.org
Wed Jul 14 14:51:32 PDT 2021


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, main has been updated
       via  168f3f431a9349d83728e7623f9388362eb6e169 (commit)
      from  1ea1cb69940be217f3a98412b1621c5b887c566e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 168f3f431a9349d83728e7623f9388362eb6e169
Author: Sandro Santilli <strk at kbt.io>
Date:   Wed Jul 14 23:32:04 2021 +0200

    Prevent removing isolated node used in TopoGeometry definition
    
    Closes #3239 in main branch (3.2.0dev)

diff --git a/NEWS b/NEWS
index d100a21..0412f35 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ PostGIS 3.2.0
   - #4933, topology.GetFaceByPoint will not work with topologies having invalid edge linking.
 
  * Enhancements *
+  - #3231, Prevent removing isolated nodes if used in a TopoGeometry
+           (Sandro Santilli)
   - #3239, Prevent headling topology edges if the connecting node is
            used in the definition of a TopoGeometry (Sandro Santilli)
   - #4950, Speed up checking containing_face for nodes in ValidateTopology
diff --git a/liblwgeom/liblwgeom_topo.h b/liblwgeom/liblwgeom_topo.h
index 901b778..cb56f12 100644
--- a/liblwgeom/liblwgeom_topo.h
+++ b/liblwgeom/liblwgeom_topo.h
@@ -755,7 +755,7 @@ typedef struct LWT_BE_CALLBACKS_T {
    *
    * The operation should also be forbidden if the removed node
    * takes part in the definition of a TopoGeometry, although
-   * this wasn't the case yet as of PostGIS version 2.1.8:
+   * this wasn't the case yet as of PostGIS version 3.1:
    * https://trac.osgeo.org/postgis/ticket/3239
    *
    * @return 1 to allow, 0 to forbid the operation
@@ -814,6 +814,24 @@ typedef struct LWT_BE_CALLBACKS_T {
 				      int fields,
 				      int limit);
 
+  /**
+   * Check TopoGeometry objects before an isolated node removal event
+   *
+   * @param topo the topology to act upon
+   * @param rem_node identifier of the isolated node that's been removed
+   *
+   * The operation should be forbidden if the removed node
+   * takes part in the definition of a TopoGeometry.
+   *
+   * @return 1 to allow, 0 to forbid the operation
+   *         (reporting reason via lastErrorMessage)
+   *
+   */
+  int (*checkTopoGeomRemIsoNode) (
+      const LWT_BE_TOPOLOGY* topo,
+      LWT_ELEMID rem_node
+  );
+
 } LWT_BE_CALLBACKS;
 
 
diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c
index 3c21d1c..60df671 100644
--- a/liblwgeom/lwgeom_topo.c
+++ b/liblwgeom/lwgeom_topo.c
@@ -18,7 +18,7 @@
  *
  **********************************************************************
  *
- * Copyright (C) 2015-2020 Sandro Santilli <strk at kbt.io>
+ * Copyright (C) 2015-2021 Sandro Santilli <strk at kbt.io>
  *
  **********************************************************************/
 
@@ -354,6 +354,12 @@ lwt_be_checkTopoGeomRemNode(LWT_TOPOLOGY* topo, LWT_ELEMID node_id,
 }
 
 static int
+lwt_be_checkTopoGeomRemIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node_id)
+{
+  CBT1(topo, checkTopoGeomRemIsoNode, node_id);
+}
+
+static int
 lwt_be_updateTopoGeomFaceHeal(LWT_TOPOLOGY* topo,
                              LWT_ELEMID face1, LWT_ELEMID face2,
                              LWT_ELEMID newface)
@@ -3658,9 +3664,12 @@ lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid)
     return -1;
   }
 
-  /* TODO: notify to caller about node being removed ?
-   * See https://trac.osgeo.org/postgis/ticket/3231
-   */
+  if ( ! lwt_be_checkTopoGeomRemIsoNode(topo, nid) )
+  {
+    lwfree(node);
+    lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface));
+    return -1;
+  }
 
   lwfree(node);
   return 0; /* success */
diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c
index 1874b23..b5e6f5b 100644
--- a/topology/postgis_topology.c
+++ b/topology/postgis_topology.c
@@ -2668,6 +2668,71 @@ cb_checkTopoGeomRemNode ( const LWT_BE_TOPOLOGY* topo,
 }
 
 static int
+cb_checkTopoGeomRemIsoNode ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID rem_node )
+{
+  MemoryContext oldcontext = CurrentMemoryContext;
+  int spi_result;
+  StringInfoData sqldata;
+  StringInfo sql = &sqldata;
+  const char *tg_id, *layer_id;
+  const char *schema_name, *table_name, *col_name;
+  HeapTuple row;
+  TupleDesc tdesc;
+
+  initStringInfo(sql);
+
+  /* Check for puntual TopoGeometry objects being defined by the common
+   * node, see https://trac.osgeo.org/postgis/ticket/3231 */
+  resetStringInfo(sql);
+  appendStringInfo( sql, "SELECT t.* FROM ( SELECT r.topogeo_id, "
+                    "r.layer_id, l.schema_name, l.table_name, l.feature_column, "
+                    "array_agg(abs(r.element_id)) as elems FROM topology.layer l "
+                    " INNER JOIN \"%s\".relation r ON (l.layer_id = r.layer_id) "
+                    "WHERE l.level = 0 and l.feature_type in ( 1, 4 ) "
+                    "AND l.topology_id = %d"
+                    " AND r.element_id = %" LWTFMT_ELEMID
+                    " group by r.topogeo_id, r.layer_id, l.schema_name, "
+                    "l.table_name, l.feature_column ) t LIMIT 1",
+                    topo->name, topo->id,
+                    rem_node );
+
+  POSTGIS_DEBUGF(1, "cb_checkTopoGeomRemIsoNode query 2: %s", sql->data);
+
+  spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
+  if ( spi_result != SPI_OK_SELECT )
+  {
+    cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
+            spi_result, sql->data);
+    pfree(sqldata.data);
+    return 0;
+  }
+
+  if ( SPI_processed )
+  {
+    row = SPI_tuptable->vals[0];
+    tdesc = SPI_tuptable->tupdesc;
+
+    tg_id = SPI_getvalue(row, tdesc, 1);
+    layer_id = SPI_getvalue(row, tdesc, 2);
+    schema_name = SPI_getvalue(row, tdesc, 3);
+    table_name = SPI_getvalue(row, tdesc, 4);
+    col_name = SPI_getvalue(row, tdesc, 5);
+
+    SPI_freetuptable(SPI_tuptable);
+
+    cberror(topo->be_data, "TopoGeom %s in layer %s "
+            "(%s.%s.%s) cannot be represented "
+            "removing node %" LWTFMT_ELEMID,
+            tg_id, layer_id, schema_name, table_name,
+            col_name, rem_node);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
 cb_updateTopoGeomFaceHeal ( const LWT_BE_TOPOLOGY* topo,
                             LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface )
 {
@@ -3269,7 +3334,8 @@ static LWT_BE_CALLBACKS be_callbacks =
   cb_updateTopoGeomFaceHeal,
   cb_checkTopoGeomRemNode,
   cb_updateTopoGeomEdgeHeal,
-  cb_getFaceWithinBox2D
+  cb_getFaceWithinBox2D,
+  cb_checkTopoGeomRemIsoNode
 };
 
 static void
diff --git a/topology/test/regress/sqlmm.sql b/topology/test/regress/sqlmm.sql
index 9ef25b5..ec2881a 100644
--- a/topology/test/regress/sqlmm.sql
+++ b/topology/test/regress/sqlmm.sql
@@ -96,17 +96,6 @@ SELECT '-- ST_AddIsoNode(2) ------------------------';
 -- ST_AddIsoNode edge-crossing node
 SELECT topology.ST_AddIsoNode('sqlmm_topology', NULL, 'POINT(5 9.5)');
 
--------------------------------------------------------------
--- ST_RemoveIsoNode
--------------------------------------------------------------
-
-SELECT '-- ST_RemoveIsoNode  ------------------------';
-
--- Isolated node
-SELECT topology.ST_RemoveIsoNode('sqlmm_topology', 3);
-
--- Non isolated node (is used by an edge);
-SELECT topology.ST_RemoveIsoNode('sqlmm_topology', 4);
 
 -------------------------------------------------------------
 -- ST_MoveIsoNode
diff --git a/topology/test/regress/sqlmm_expected b/topology/test/regress/sqlmm_expected
index ea665fe..0b22ddc 100644
--- a/topology/test/regress/sqlmm_expected
+++ b/topology/test/regress/sqlmm_expected
@@ -36,9 +36,6 @@ ERROR:  SQL/MM Spatial exception - not isolated node
 ERROR:  SQL/MM Spatial exception - not isolated node
 -- ST_AddIsoNode(2) ------------------------
 ERROR:  SQL/MM Spatial exception - edge crosses node.
--- ST_RemoveIsoNode  ------------------------
-Isolated node 3 removed
-ERROR:  SQL/MM Spatial exception - not isolated node
 -- ST_MoveIsoNode  ------------------------
 ERROR:  SQL/MM Spatial exception - coincident node
 ERROR:  SQL/MM Spatial exception - edge crosses node.
diff --git a/topology/test/regress/st_removeisonode.sql b/topology/test/regress/st_removeisonode.sql
new file mode 100644
index 0000000..ea25c67
--- /dev/null
+++ b/topology/test/regress/st_removeisonode.sql
@@ -0,0 +1,59 @@
+\set VERBOSITY terse
+set client_min_messages to ERROR;
+
+-- Import city_data
+\i ../load_topology.sql
+
+-- Non isolated node (is used by an edge);
+SELECT 'non-iso', topology.ST_RemoveIsoNode('city_data', 1);
+
+-- Isolated node
+SELECT 'iso', topology.ST_RemoveIsoNode('city_data',
+  topology.TopoGeo_addPoint('city_data', 'POINT(100 100)')
+);
+
+
+-- See https://trac.osgeo.org/postgis/ticket/3231
+CREATE TABLE city_data.poi(id serial primary key);
+SELECT NULL FROM AddTopoGeometryColumn('city_data', 'city_data', 'poi', 'tg', 'POINT');
+CREATE TABLE city_data.mix(id serial primary key);
+SELECT NULL FROM AddTopoGeometryColumn('city_data', 'city_data', 'mix', 'tg', 'COLLECTION');
+BEGIN;
+INSERT INTO city_data.poi(tg) VALUES(
+  topology.CreateTopoGeom(
+    'city_data', -- Topology name
+    1, -- Topology geometry type (point)
+    1, -- TG_LAYER_ID for this topology (from topology.layer)
+    ARRAY[
+      ARRAY[
+        topology.TopoGeo_addPoint('city_data', 'POINT(100 100)'),
+        1 -- node
+      ]
+    ]
+  )
+);
+SELECT '#3231.point', 'remove', ST_RemoveIsoNode('city_data',
+  topology.TopoGeo_addPoint('city_data', 'POINT(100 100)')
+);
+ROLLBACK;
+BEGIN;
+INSERT INTO city_data.mix(tg) VALUES(
+  topology.CreateTopoGeom(
+    'city_data', -- Topology name
+    4, -- Topology geometry type (point)
+    2, -- TG_LAYER_ID for this topology (from topology.layer)
+    ARRAY[
+      ARRAY[
+        topology.TopoGeo_addPoint('city_data', 'POINT(100 100)'),
+        1 -- node
+      ]
+    ]
+  )
+);
+SELECT '#3231.collection', 'remove', ST_RemoveIsoNode('city_data',
+  topology.TopoGeo_addPoint('city_data', 'POINT(100 100)')
+);
+ROLLBACK;
+
+
+SELECT NULL FROM DropTopology('city_data');
diff --git a/topology/test/regress/st_removeisonode_expected b/topology/test/regress/st_removeisonode_expected
new file mode 100644
index 0000000..5adf4e9
--- /dev/null
+++ b/topology/test/regress/st_removeisonode_expected
@@ -0,0 +1,4 @@
+ERROR:  SQL/MM Spatial exception - not isolated node
+iso|Isolated node 23 removed
+ERROR:  TopoGeom 1 in layer 1 (city_data.poi.tg) cannot be represented removing node 24
+ERROR:  TopoGeom 1 in layer 2 (city_data.mix.tg) cannot be represented removing node 25
diff --git a/topology/test/tests.mk b/topology/test/tests.mk
index 8a7566e..f98afe4 100644
--- a/topology/test/tests.mk
+++ b/topology/test/tests.mk
@@ -59,6 +59,7 @@ TESTS += \
 	$(topsrcdir)/topology/test/regress/st_newedgessplit.sql \
 	$(topsrcdir)/topology/test/regress/st_remedgemodface.sql \
 	$(topsrcdir)/topology/test/regress/st_remedgenewface.sql \
+	$(topsrcdir)/topology/test/regress/st_removeisonode.sql \
 	$(topsrcdir)/topology/test/regress/st_simplify.sql \
 	$(topsrcdir)/topology/test/regress/topo2.5d.sql \
 	$(topsrcdir)/topology/test/regress/topoelementarray_agg.sql \

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                            |  2 +
 liblwgeom/liblwgeom_topo.h                      | 20 +++++++-
 liblwgeom/lwgeom_topo.c                         | 17 +++++--
 topology/postgis_topology.c                     | 68 ++++++++++++++++++++++++-
 topology/test/regress/sqlmm.sql                 | 11 ----
 topology/test/regress/sqlmm_expected            |  3 --
 topology/test/regress/st_removeisonode.sql      | 59 +++++++++++++++++++++
 topology/test/regress/st_removeisonode_expected |  4 ++
 topology/test/tests.mk                          |  1 +
 9 files changed, 165 insertions(+), 20 deletions(-)
 create mode 100644 topology/test/regress/st_removeisonode.sql
 create mode 100644 topology/test/regress/st_removeisonode_expected


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list