[SCM] PostGIS branch master updated. 3.6.0rc2-373-g14152529d

git at osgeo.org git at osgeo.org
Sat Mar 7 03:01:57 PST 2026


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, master has been updated
       via  14152529d3f3008be89dbc76505521e819ae78d5 (commit)
      from  88ad88be10d113781a2a8f4e67820a3cdb40638b (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 14152529d3f3008be89dbc76505521e819ae78d5
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Sat Mar 7 15:00:33 2026 +0400

    Fix 32-bit topology and coverage crashes
    
    Two CI failures on i386 came from hard-coded Datum and stack struct assumptions that only break on 32-bit builds.
    
    In coverage_window_calculation(), the simplifyBoundary argument is declared as boolean in SQL, but the C code decoded it with DatumGetFloat8(). On 32-bit PostgreSQL float8 is pass-by-reference, so reading a bool Datum that way can dereference garbage and crash the backend while running coverage.sql. Decode the argument with DatumGetBool() instead.
    
    In the topology SPI backend, several callbacks unconditionally decoded topology primitive IDs with DatumGetInt64() and one face callback always built an INT8[] argument. Topology schemas still default to INT4 IDs unless usesLargeIDs is enabled, so those assumptions are wrong on 32-bit and caused addnode.sql to segfault or raise unsupported byval length errors. Load usesLargeIDs from topology.topology, decode tuple values using their actual column type, and build the face ID array with the matching integer width.
    
    Also zero-initialize temporary LWT_ISO_EDGE stack structs on edge split paths so callback update/select filters do not carry uninitialized fields into backend operations.

diff --git a/liblwgeom/topo/lwgeom_topo.c b/liblwgeom/topo/lwgeom_topo.c
index 07e7ba7cd..d92737705 100644
--- a/liblwgeom/topo/lwgeom_topo.c
+++ b/liblwgeom/topo/lwgeom_topo.c
@@ -1188,8 +1188,8 @@ lwt_ModEdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge,
   LWCOLLECTION *split_col;
   const LWGEOM *oldedge_geom;
   const LWGEOM *newedge_geom;
-  LWT_ISO_EDGE newedge1;
-  LWT_ISO_EDGE seledge, updedge, excedge;
+  LWT_ISO_EDGE newedge1 = {0};
+  LWT_ISO_EDGE seledge = {0}, updedge = {0}, excedge = {0};
   int ret;
 
   split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
@@ -1345,8 +1345,8 @@ lwt_NewEdgesSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge,
   LWCOLLECTION *split_col;
   const LWGEOM *oldedge_geom;
   const LWGEOM *newedge_geom;
-  LWT_ISO_EDGE newedges[2];
-  LWT_ISO_EDGE seledge, updedge;
+  LWT_ISO_EDGE newedges[2] = {{0}};
+  LWT_ISO_EDGE seledge = {0}, updedge = {0};
   int ret;
 
   split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
@@ -5611,10 +5611,9 @@ _lwt_SnapEdgeToExistingNode(
     replacedBy[n] = existingEdgeId;
   }
 
-
-  LWT_ISO_EDGE updatedEdge;
+  LWT_ISO_EDGE updatedEdge = {0};
   int updateFlags;
-  LWT_ISO_EDGE selEdge;
+  LWT_ISO_EDGE selEdge = {0};
 
   if ( ( replacedBy[0] != 0 && replacedBy[1] == 0 ) ||
        ( replacedBy[1] != 0 && replacedBy[0] == 0 ) )
@@ -6129,7 +6128,7 @@ _lwt_SnapEdgeToExistingNode(
     /* Neither sides of the snapped edge collapsed to an existing edge */
 
     /* New edge is the outgoing one, by design */
-    LWT_ISO_EDGE newEdge;
+    LWT_ISO_EDGE newEdge = {0};
     newEdge.edge_id = lwt_be_getNextEdgeId( topo );
     if ( newEdge.edge_id == -1 ) {
       PGTOPO_BE_ERROR();
diff --git a/postgis/lwgeom_window.c b/postgis/lwgeom_window.c
index 409b94503..920f2716d 100644
--- a/postgis/lwgeom_window.c
+++ b/postgis/lwgeom_window.c
@@ -784,7 +784,9 @@ coverage_window_calculation(PG_FUNCTION_ARGS, int mode)
 			if (!isnull) tolerance = DatumGetFloat8(d);
 
 			d = WinGetFuncArgCurrent(winobj, 2, &isnull);
-			if (!isnull) simplifyBoundary = DatumGetFloat8(d);
+			/* The third SQL argument is boolean, which matters on 32-bit builds. */
+			if (!isnull)
+				simplifyBoundary = DatumGetBool(d);
 
 			/* GEOSCoverageSimplifyVW is "preserveBoundary" so we invert simplifyBoundary */
 			output = GEOSCoverageSimplifyVW(input, tolerance, !simplifyBoundary);
diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c
index 7707ece2b..b815cb901 100644
--- a/topology/postgis_topology.c
+++ b/topology/postgis_topology.c
@@ -92,6 +92,7 @@ struct LWT_BE_TOPOLOGY_T
   int32_t srid;
   double precision;
   int hasZ;
+  bool usesLargeIDs;
   Oid geometryOID;
 };
 
@@ -180,8 +181,8 @@ cb_loadTopologyByName(const LWT_BE_DATA* be, const char *name)
 
   argtypes[0] = CSTRINGOID;
   sql =
-    "SELECT id,srid,precision,null::geometry "
-    "FROM topology.topology WHERE name = $1::varchar";
+      "SELECT id,srid,precision,null::geometry,useslargeids "
+      "FROM topology.topology WHERE name = $1::varchar";
   if ( ! plan ) /* prepare on first call */
   {
     plan = SPI_prepare(sql, 1, argtypes);
@@ -226,6 +227,7 @@ cb_loadTopologyByName(const LWT_BE_DATA* be, const char *name)
   topo->be_data = (LWT_BE_DATA *)be; /* const cast.. */
   topo->name = pstrdup(name);
   topo->hasZ = 0;
+  topo->usesLargeIDs = false;
 
   dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
   if ( isnull )
@@ -262,6 +264,12 @@ cb_loadTopologyByName(const LWT_BE_DATA* be, const char *name)
     topo->precision = DatumGetFloat8(dat);
   }
 
+  dat = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 5, &isnull);
+  if (!isnull)
+  {
+	  topo->usesLargeIDs = DatumGetBool(dat);
+  }
+
   /* we're dynamically querying geometry type here */
   topo->geometryOID = TupleDescAttr(SPI_tuptable->tupdesc, 3)->atttypid;
 
@@ -666,6 +674,31 @@ addFaceValues(StringInfo str, LWT_ISO_FACE *face, int32_t srid)
   }
 }
 
+static int64
+tuple_get_int64(TupleDesc desc, int col, Datum dat)
+{
+	Oid typid = TupleDescAttr(desc, col - 1)->atttypid;
+
+	/*
+	 * Topology still defaults to INT4 primitive IDs unless usesLargeIDs is
+	 * requested, so SPI tuple readers must honor the actual column type here.
+	 */
+	switch (typid)
+	{
+	case INT2OID:
+		return DatumGetInt16(dat);
+	case INT4OID:
+		return DatumGetInt32(dat);
+	case INT8OID:
+		return DatumGetInt64(dat);
+	case OIDOID:
+		return DatumGetObjectId(dat);
+	default:
+		lwpgerror("Unexpected integer type %u for column %d", typid, col);
+		return -1;
+	}
+}
+
 static void
 fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
 {
@@ -689,11 +722,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (edge_id)"
-                     " has int64 val of %" LWTFMT_ELEMID,
-                     colno, val);
-      edge->edge_id = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (edge_id)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->edge_id = val;
     }
 
   }
@@ -707,10 +742,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (start_node)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->start_node = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (start_node)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->start_node = val;
     }
   }
   if ( fields & LWT_COL_EDGE_END_NODE )
@@ -723,10 +761,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (end_node)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->end_node = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (end_node)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->end_node = val;
     }
   }
   if ( fields & LWT_COL_EDGE_FACE_LEFT )
@@ -739,10 +780,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (face_left)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->face_left = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (face_left)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->face_left = val;
     }
   }
   if ( fields & LWT_COL_EDGE_FACE_RIGHT )
@@ -755,10 +799,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (face_right)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->face_right = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (face_right)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->face_right = val;
     }
   }
   if ( fields & LWT_COL_EDGE_NEXT_LEFT )
@@ -771,10 +818,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (next_left)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->next_left = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (next_left)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->next_left = val;
     }
   }
   if ( fields & LWT_COL_EDGE_NEXT_RIGHT )
@@ -787,10 +837,13 @@ fillEdgeFields(LWT_ISO_EDGE* edge, HeapTuple row, TupleDesc rowdesc, int fields)
     }
     else
     {
-      val = DatumGetInt64(dat);
-      POSTGIS_DEBUGF(2, "fillEdgeFields: colno%d (next_right)"
-                     " has int64 val of %" LWTFMT_ELEMID, colno, val);
-      edge->next_right = val;
+	    val = tuple_get_int64(rowdesc, colno, dat);
+	    POSTGIS_DEBUGF(2,
+			   "fillEdgeFields: colno%d (next_right)"
+			   " has int64 val of %" LWTFMT_ELEMID,
+			   colno,
+			   val);
+	    edge->next_right = val;
     }
   }
   if ( fields & LWT_COL_EDGE_GEOM )
@@ -830,13 +883,14 @@ fillNodeFields(LWT_ISO_NODE* node, HeapTuple row, TupleDesc rowdesc, int fields)
   if ( fields & LWT_COL_NODE_NODE_ID )
   {
     dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
-    node->node_id = DatumGetInt64(dat);
+    node->node_id = tuple_get_int64(rowdesc, colno, dat);
   }
   if ( fields & LWT_COL_NODE_CONTAINING_FACE )
   {
     dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
     if ( isnull ) node->containing_face = -1;
-    else node->containing_face = DatumGetInt64(dat);
+    else
+	    node->containing_face = tuple_get_int64(rowdesc, colno, dat);
   }
   if ( fields & LWT_COL_NODE_GEOM )
   {
@@ -870,7 +924,7 @@ fillFaceFields(LWT_ISO_FACE* face, HeapTuple row, TupleDesc rowdesc, int fields)
   if ( fields & LWT_COL_FACE_FACE_ID )
   {
     dat = SPI_getbinval(row, rowdesc, ++colno, &isnull);
-    face->face_id = DatumGetInt64(dat);
+    face->face_id = tuple_get_int64(rowdesc, colno, dat);
   }
   if ( fields & LWT_COL_FACE_MBR )
   {
@@ -923,7 +977,7 @@ getNotNullInt64( HeapTuple row, TupleDesc desc, int col, int64 *val )
   bool isnull;
   Datum dat = SPI_getbinval( row, desc, col, &isnull );
   if ( isnull ) return 0;
-  *val = DatumGetInt64(dat);
+  *val = tuple_get_int64(desc, col, dat);
   return 1;
 }
 
@@ -1058,12 +1112,24 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *n
   Datum *datum_ids;
   Datum values[2];
   Oid argtypes[2];
+  Oid idtype;
+  Oid idarraytype;
   int nargs = 1;
   GSERIALIZED *gser = NULL;
+  int16 typlen;
+  bool typbyval;
+  char typalign;
 
+  /* Match the callback argument type to the topology schema's chosen ID width. */
+  idtype = topo->usesLargeIDs ? INT8OID : INT4OID;
+  idarraytype = topo->usesLargeIDs ? INT8ARRAYOID : INT4ARRAYOID;
+  get_typlenbyvalalign(idtype, &typlen, &typbyval, &typalign);
   datum_ids = palloc(sizeof(Datum)*(*numelems));
-  for (i=0; i<*numelems; ++i) datum_ids[i] = Int64GetDatum(ids[i]);
-  array_ids = construct_array(datum_ids, *numelems, INT8OID, 8, true, 's');
+  for (i = 0; i < *numelems; ++i)
+  {
+	  datum_ids[i] = topo->usesLargeIDs ? Int64GetDatum(ids[i]) : Int32GetDatum(ids[i]);
+  }
+  array_ids = construct_array(datum_ids, *numelems, idtype, typlen, typbyval, typalign);
 
   initStringInfo(sql);
   appendStringInfoString(sql, "SELECT ");
@@ -1074,7 +1140,7 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *n
                    topo->name);
 
   values[0] = PointerGetDatum(array_ids);
-  argtypes[0] = INT8ARRAYOID;
+  argtypes[0] = idarraytype;
 
   if ( box )
   {
@@ -1256,7 +1322,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numelems
       *numelems = UINT64_MAX;
       return NULL;
     }
-    val = DatumGetInt64(dat);
+    val = tuple_get_int64(rowdesc, 1, dat);
     edges[i] = val;
     POSTGIS_DEBUGF(1, "Component " UINT64_FORMAT " in ring of edge %" LWTFMT_ELEMID " is edge %d", i, edge, val);
 
@@ -1278,7 +1344,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numelems
         *numelems = UINT64_MAX;
         return NULL;
       }
-      nextedge = DatumGetInt64(dat);
+      nextedge = tuple_get_int64(rowdesc, sidecol, dat);
       POSTGIS_DEBUGF(1, "Last component in ring of edge %"
                         LWTFMT_ELEMID " (%d) has next_%s_edge %d",
                         edge, val, sidetext, nextedge);

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

Summary of changes:
 liblwgeom/topo/lwgeom_topo.c |  15 ++---
 postgis/lwgeom_window.c      |   4 +-
 topology/postgis_topology.c  | 148 +++++++++++++++++++++++++++++++------------
 3 files changed, 117 insertions(+), 50 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list