]> granicus.if.org Git - postgis/commitdiff
Implement ST_GetFaceEdges in C
authorSandro Santilli <strk@keybit.net>
Tue, 4 Aug 2015 21:08:22 +0000 (21:08 +0000)
committerSandro Santilli <strk@keybit.net>
Tue, 4 Aug 2015 21:08:22 +0000 (21:08 +0000)
Wraps SPI_exec calls in callbacks to not force a memory context switch

Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8)

git-svn-id: http://svn.osgeo.org/postgis/trunk@13877 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/TODO
liblwgeom/lwgeom_topo.c
topology/postgis_topology.c
topology/sql/sqlmm.sql.in

index 06f069963a2d737e039be7c39ff08310fdf8d08a..773d677dfa26e433d21652f9ff93b45650600868 100644 (file)
@@ -8,7 +8,7 @@ lwt_NewEdgesSplit     X
 
 lwt_AddIsoEdge        X
 lwt_GetFaceGeometry   X
-lwt_GetFaceEdges
+lwt_GetFaceEdges      X
 
 lwt_ChangeEdgeGeom
 lwt_MoveIsoNode
index 87c1eaef1f8fd02a1bc8dfd69a3f61d84ebf7be5..ac971f0534a7a0c4ba2c136bf2dd90c7c5a1a45b 100644 (file)
@@ -2408,6 +2408,53 @@ lwt_AddEdgeNewFaces( LWT_TOPOLOGY* topo,
   return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 );
 }
 
+static LWGEOM *
+_lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges)
+{
+  LWGEOM *outg;
+  LWCOLLECTION *bounds;
+  LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges );
+  int i, validedges = 0;
+
+  for ( i=0; i<numfaceedges; ++i )
+  {
+    /* NOTE: skipping edges with same face on both sides, although
+     *       correct, results in a failure to build faces from
+     *       invalid topologies as expected by legacy tests.
+     * TODO: update legacy tests expectances/unleash this skipping ?
+     */
+    /* if ( edges[i].face_left == edges[i].face_right ) continue; */
+    geoms[validedges++] = lwline_as_lwgeom(edges[i].geom);
+  }
+  if ( ! validedges )
+  {
+    /* Face has no valid boundary edges, we'll return EMPTY, see
+     * https://trac.osgeo.org/postgis/ticket/3221 */
+    if ( numfaceedges ) lwfree(geoms);
+    LWDEBUG(1, "_lwt_FaceByEdges returning empty polygon");
+    return lwpoly_as_lwgeom(
+            lwpoly_construct_empty(topo->srid, topo->hasZ, 0)
+           );
+  }
+  bounds = lwcollection_construct(MULTILINETYPE,
+                                  topo->srid,
+                                  NULL, /* gbox */
+                                  validedges,
+                                  geoms);
+  outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) );
+  lwcollection_release(bounds);
+  lwfree(geoms);
+#if 0
+  {
+  size_t sz;
+  char *wkt = lwgeom_to_wkt(outg, WKT_ISO, 2, &sz);
+  LWDEBUGF(1, "_lwt_FaceByEdges returning area: %s", wkt);
+  lwfree(wkt);
+  }
+#endif
+  return outg;
+}
+
 LWGEOM*
 lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid)
 {
@@ -2461,51 +2508,306 @@ lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid)
     return lwpoly_as_lwgeom(out);
   }
 
-  /* Linemerge polygon boundaries */
+  outg = _lwt_FaceByEdges( topo, edges, numfaceedges );
+  _lwt_release_edges(edges, numfaceedges);
 
-  LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges );
-  int validedges = 0;
-  for ( i=0; i<numfaceedges; ++i )
-  {
-    /* NOTE: skipping edges with same face on both sides, although
-     *       correct, results in a failure to build faces from
-     *       invalid topologies as expected by legacy tests.
-     * TODO: update legacy tests expectances/unleash this skipping ?
-     */
-    /* if ( edges[i].face_left == edges[i].face_right ) continue; */
-    geoms[validedges++] = lwline_as_lwgeom(edges[i].geom);
-  }
-  if ( ! validedges )
+  return outg;
+}
+
+/* Find which edge from the "edges" set defines the next
+ * portion of the given "ring".
+ *
+ * The edge might be either forward or backward.
+ *
+ * @param ring The ring to find definition of.
+ *             It is assumed it does not contain duplicated vertices.
+ * @param from offset of the ring point to start looking from
+ * @param edges array of edges to search into
+ * @param numedges number of edges in the edges array
+ *
+ * @return index of the edge defining the next ring portion or
+ *               -1 if no edge was found to be part of the ring
+ */
+static int
+_lwt_FindNextRingEdge(const POINTARRAY *ring, int from,
+                      const LWT_ISO_EDGE *edges, int numedges)
+{
+  int i;
+  POINT2D p1;
+
+  /* Get starting ring point */
+  getPoint2d_p(ring, from, &p1);
+
+  LWDEBUGF(1, "Ring's 'from' point (%d) is %g,%g", from, p1.x, p1.y);
+
+  /* find the edges defining the next portion of ring starting from
+   * vertex "from" */
+  for ( i=0; i<numedges; ++i )
   {
-    lwfree(geoms);
-    /* Face has no valid boundary edges, we'll return EMPTY, see
-     * https://trac.osgeo.org/postgis/ticket/3221 */
-    out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
-    return lwpoly_as_lwgeom(out);
-  }
-  LWCOLLECTION *bounds = lwcollection_construct(MULTILINETYPE,
-                                                topo->srid,
-                                                NULL, /* gbox */
-                                                validedges,
-                                                geoms);
-#if 0 /* debugging (a leaking one) */
-  lwnotice("Collected face bounds: %s", lwgeom_to_wkt((LWGEOM*)bounds,
-                          WKT_ISO, 2, NULL));
+    const LWT_ISO_EDGE *isoe = &(edges[i]);
+    LWLINE *edge = isoe->geom;
+    POINTARRAY *epa = edge->points;
+    POINT2D p2, pt;
+    int match = 0;
+    int j;
+
+    /* Skip if the edge is a dangling one */
+    if ( isoe->face_left == isoe->face_right )
+    {
+      LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" PRId64
+                  " has same face (%" PRId64
+                  ") on both sides, skipping",
+                  isoe->edge_id, isoe->face_left);
+      continue;
+    }
+
+#if 0
+    size_t sz;
+    LWDEBUGF(1, "Edge %" PRId64 " is %s",
+                isoe->edge_id,
+                lwgeom_to_wkt(lwline_as_lwgeom(edge), WKT_ISO, 2, &sz));
 #endif
 
-  outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) );
+    /* ptarray_remove_repeated_points ? */
 
-  _lwt_release_edges(edges, numfaceedges);
-  lwcollection_release(bounds);
+    getPoint2d_p(epa, 0, &p2);
+    LWDEBUGF(1, "Edges's 'first' point is %g,%g", p2.x, p2.y);
+    LWDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y);
+    if ( p2d_same(&p1, &p2) )
+    {
+      LWDEBUG(1, "p2d_same(p1,p2) returned true");
+      LWDEBUGF(1, "First point of edge %" PRId64
+                  " matches ring vertex %d", isoe->edge_id, from);
+      /* first point matches, let's check next non-equal one */
+      for ( j=1; j<epa->npoints; ++j )
+      {
+        getPoint2d_p(epa, j, &p2);
+        /* we won't check duplicated edge points */
+        if ( p2d_same(&p1, &p2) ) continue;
+        /* we assume there are no duplicated points in ring */
+        getPoint2d_p(ring, from+1, &pt);
+        if ( p2d_same(&pt, &p2) )
+        {
+          match = 1;
+          break; /* no need to check more */
+        }
+      }
+    }
+    else
+    {
+      LWDEBUG(1, "p2d_same(p1,p2) returned false");
+      getPoint2d_p(epa, epa->npoints-1, &p2);
+      LWDEBUGF(1, "Edges's 'last' point is %g,%g", p2.x, p2.y);
+      if ( p2d_same(&p1, &p2) )
+      {
+        LWDEBUGF(1, "Last point of edge %" PRId64
+                    " matches ring vertex %d", isoe->edge_id, from);
+        /* last point matches, let's check next non-equal one */
+        for ( j=epa->npoints-2; j>=0; --j )
+        {
+          getPoint2d_p(epa, j, &p2);
+          /* we won't check duplicated edge points */
+          if ( p2d_same(&p1, &p2) ) continue;
+          /* we assume there are no duplicated points in ring */
+          getPoint2d_p(ring, from+1, &pt);
+          if ( p2d_same(&pt, &p2) )
+          {
+            match = 1;
+            break; /* no need to check more */
+          }
+        }
+      }
+    }
 
-  return outg;
+    if ( match ) return i;
+
+  }
+
+  return -1;
 }
 
+/* Reverse values in array between "from" (inclusive)
+ * and "to" (exclusive) indexes */
+static void
+_lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to)
+{
+  LWT_ELEMID t;
+  while (from < to)
+  {
+    t = ary[from];
+    ary[from++] = ary[to];
+    ary[to--] = t;
+  }
+}
+
+/* Rotate values in array between "from" (inclusive)
+ * and "to" (exclusive) indexes, so that "rotidx" is
+ * the new value at "from" */
+static void
+_lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx)
+{
+  _lwt_ReverseElemidArray(ary, from, rotidx-1);
+  _lwt_ReverseElemidArray(ary, rotidx, to-1);
+  _lwt_ReverseElemidArray(ary, from, to-1);
+}
+
+
 int
-lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWT_ELEMID **edges)
+lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face_id, LWT_ELEMID **out )
 {
-  /* TODO: Construct the face geometry */
-  /* TODO: Then for each ring of the face polygon... */
-  lwerror("lwt_GetFaceEdges not implemented yet");
-  return -1;
+  LWGEOM *face;
+  LWPOLY *facepoly;
+  LWT_ISO_EDGE *edges;
+  int numfaceedges;
+  int fields, i;
+  int nseid = 0; /* number of signed edge ids */
+  int prevseid;
+  LWT_ELEMID *seid; /* signed edge ids */
+
+  /* Get list of face edges */
+  numfaceedges = 1;
+  fields = LWT_COL_EDGE_EDGE_ID |
+           LWT_COL_EDGE_GEOM |
+           LWT_COL_EDGE_FACE_LEFT |
+           LWT_COL_EDGE_FACE_RIGHT
+           ;
+  edges = lwt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields );
+  if ( numfaceedges == -1 ) {
+    lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+    return -1;
+  }
+  if ( ! numfaceedges ) return 0; /* no edges in output */
+
+  /* order edges by occurrence in face */
+
+  face = _lwt_FaceByEdges(topo, edges, numfaceedges);
+  if ( ! face )
+  {
+    /* _lwt_FaceByEdges should have already invoked lwerror in this case */
+    _lwt_release_edges(edges, numfaceedges);
+    return -1;
+  }
+
+  if ( lwgeom_is_empty(face) )
+  {
+    /* no edges in output */
+    _lwt_release_edges(edges, numfaceedges);
+    lwgeom_free(face);
+    return 0;
+  }
+
+  /* force_lhr, if the face is not the universe */
+  /* _lwt_FaceByEdges seems to guaranteed RHR */
+  /* lwgeom_force_clockwise(face); */
+  if ( face_id ) lwgeom_reverse(face);
+
+#if 0
+  {
+  size_t sz;
+  char *wkt = lwgeom_to_wkt(face, WKT_ISO, 2, &sz);
+  LWDEBUGF(1, "Geometry of face %" PRId64 " is: %s",
+              face_id, wkt);
+  lwfree(wkt);
+  }
+#endif
+
+  facepoly = lwgeom_as_lwpoly(face);
+  if ( ! facepoly )
+  {
+    _lwt_release_edges(edges, numfaceedges);
+    lwgeom_free(face);
+    lwerror("Geometry of face %" PRId64 " is not a polygon", face_id);
+    return -1;
+  }
+
+  nseid = prevseid = 0;
+  seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges );
+
+  /* for each ring of the face polygon... */
+  for ( i=0; i<facepoly->nrings; ++i )
+  {
+    const POINTARRAY *ring = facepoly->rings[i];
+    int j = 0;
+    LWT_ISO_EDGE *nextedge;
+    LWLINE *nextline;
+
+    LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints);
+
+    while ( j < ring->npoints-1 )
+    {
+      LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d",
+                  i, j);
+
+      int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges);
+      if ( edgeno == -1 )
+      {
+        /* should never happen */
+        _lwt_release_edges(edges, numfaceedges);
+        lwgeom_free(face);
+        lwfree(seid);
+        lwerror("No edge (among %d) found to be defining geometry of face %"
+                PRId64, face_id);
+        return -1;
+      }
+
+      nextedge = &(edges[edgeno]);
+      nextline = nextedge->geom;
+
+      LWDEBUGF(1, "Edge %" PRId64
+                  " covers ring %d from vertex %d to %d",
+                  nextedge->edge_id, i, j, j + nextline->points->npoints - 1);
+
+#if 0
+      size_t sz;
+      LWDEBUGF(1, "Edge %" PRId64 " is %s",
+                  nextedge->edge_id,
+lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_ISO, 2, &sz));
+#endif
+
+      j += nextline->points->npoints - 1;
+
+      /* Add next edge to the output array */
+      seid[nseid++] = nextedge->face_left == face_id ?
+                          nextedge->edge_id :
+                         -nextedge->edge_id;
+
+      /* avoid checking again on next time turn */
+      nextedge->face_left = nextedge->face_right = -1;
+    }
+
+    /* TODO: now "scroll" the list of edges so that the one
+     * with smaller absolute edge_id is first */
+    /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */
+    if ( (nseid - prevseid) > 1 )
+    {{
+      LWT_ELEMID minid = 0;
+      int minidx = 0;
+      LWDEBUGF(1, "Looking for smallest id among the %d edges "
+                  "composing ring %d", (nseid-prevseid), i);
+      for ( j=prevseid; j<nseid; ++j )
+      {
+        LWT_ELEMID id = llabs(seid[j]);
+        LWDEBUGF(1, "Abs id of edge in pos %d is %" PRId64, j, id);
+        if ( ! minid || id < minid )
+        {
+          minid = id;
+          minidx = j;
+        }
+      }
+      LWDEBUGF(1, "Smallest id is %" PRId64
+                  " at position %d", minid, minidx);
+      if ( minidx != prevseid )
+      {
+        _lwt_RotateElemidArray(seid, prevseid, nseid, minidx);
+      }
+    }}
+
+    prevseid = nseid;
+  }
+
+  lwgeom_free(face);
+  _lwt_release_edges(edges, numfaceedges);
+
+  *out = seid;
+  return nseid;
 }
index 2a300cffe859be47cc3f2d25465a9067559ed576..5bc0933eb35813189c1b0b3e9e0adad6ef026551 100644 (file)
@@ -15,7 +15,7 @@
 #include "utils/elog.h"
 #include "utils/memutils.h" /* for TopMemoryContext */
 #include "lib/stringinfo.h"
-//#include "funcapi.h"
+#include "funcapi.h" /* for FuncCallContext */
 #include "executor/spi.h" /* this is what you need to work with SPI */
 #include "inttypes.h" /* for PRId64 */
 
@@ -23,7 +23,7 @@
 
 #include "liblwgeom_internal.h" /* for gbox_clone */
 #include "liblwgeom_topo.h"
-/*#define POSTGIS_DEBUG_LEVEL 1*/
+#define POSTGIS_DEBUG_LEVEL 1
 #include "lwgeom_log.h"
 #include "lwgeom_pg.h"
 
@@ -108,11 +108,13 @@ cb_loadTopologyByName(const LWT_BE_DATA* be, const char *name)
   Datum dat;
   bool isnull;
   LWT_BE_TOPOLOGY *topo;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   initStringInfo(sql);
   appendStringInfo(sql, "SELECT id,srid FROM topology.topology "
                         "WHERE name = '%s'", name);
   spi_result = SPI_execute(sql->data, !be->data_changed, 0);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_SELECT ) {
     pfree(sqldata.data);
                cberror(be, "unexpected return (%d) from query execution: %s", spi_result, sql->data);
@@ -718,7 +720,7 @@ cb_getEdgeById(const LWT_BE_TOPOLOGY* topo,
 {
   LWT_ISO_EDGE *edges;
        int spi_result;
-
+  MemoryContext oldcontext = CurrentMemoryContext;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
@@ -736,6 +738,7 @@ cb_getEdgeById(const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_getEdgeById query: %s", sql->data);
 
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems);
+  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);
          *numelems = -1; return NULL;
@@ -768,6 +771,7 @@ cb_getEdgeByNode(const LWT_BE_TOPOLOGY* topo,
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   initStringInfo(sql);
   appendStringInfoString(sql, "SELECT ");
@@ -789,6 +793,7 @@ cb_getEdgeByNode(const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
 
   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);
          *numelems = -1; return NULL;
@@ -817,6 +822,7 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY* topo,
 {
   LWT_ISO_EDGE *edges;
        int spi_result;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -842,6 +848,7 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
 
   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);
          *numelems = -1; return NULL;
@@ -870,10 +877,10 @@ cb_getFacesById(const LWT_BE_TOPOLOGY* topo,
 {
   LWT_ISO_FACE *faces;
        int spi_result;
-
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   initStringInfo(sql);
   appendStringInfoString(sql, "SELECT ");
@@ -890,6 +897,7 @@ cb_getFacesById(const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
 
   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);
          *numelems = -1; return NULL;
@@ -922,6 +930,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo,
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   initStringInfo(sql);
   appendStringInfo(sql, "WITH RECURSIVE edgering AS ( "
@@ -943,6 +952,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo,
 
   POSTGIS_DEBUGF(1, "cb_getRingEdges query (limit %d): %s", limit, sql->data);
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit);
+  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);
          *numelems = -1; return NULL;
@@ -976,8 +986,8 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo,
     }
     val = DatumGetInt32(dat);
     edges[i] = val;
-    POSTGIS_DEBUGF(1, "Component %d in ring of edge %" PRId64 " is edge %d",
-                   i, edge, val);
+    POSTGIS_DEBUGF(1, "Component %d in ring of edge " INT64_FORMAT
+                      " is edge %d", i, edge, val);
   }
 
   return edges;
@@ -993,6 +1003,7 @@ cb_getNodeById(const LWT_BE_TOPOLOGY* topo,
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
+  MemoryContext oldcontext = CurrentMemoryContext;
 
   initStringInfo(sql);
   appendStringInfoString(sql, "SELECT ");
@@ -1006,6 +1017,7 @@ cb_getNodeById(const LWT_BE_TOPOLOGY* topo,
   appendStringInfoString(sql, ")");
   POSTGIS_DEBUGF(1, "cb_getNodeById query: %s", sql->data);
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems);
+  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);
          *numelems = -1; return NULL;
@@ -1034,7 +1046,7 @@ cb_getNodeByFace(const LWT_BE_TOPOLOGY* topo,
 {
   LWT_ISO_NODE *nodes;
        int spi_result;
-
+  MemoryContext oldcontext = CurrentMemoryContext;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
@@ -1052,6 +1064,7 @@ cb_getNodeByFace(const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_getNodeByFace query: %s", sql->data);
   POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed);
   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);
          *numelems = -1; return NULL;
@@ -1084,7 +1097,7 @@ cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
   int elems_requested = limit;
   size_t hexewkb_size;
   char *hexewkb;
-
+  MemoryContext oldcontext = CurrentMemoryContext;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
   int i;
@@ -1112,6 +1125,7 @@ cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
   }
   lwpgnotice("cb_getEdgeWithinDistance2D: query is: %s", sql->data);
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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);
          *numelems = -1; return NULL;
@@ -1155,6 +1169,7 @@ cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
       const LWPOINT* pt, double dist, int* numelems,
       int fields, int limit)
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
   LWT_ISO_NODE *nodes;
        int spi_result;
   size_t hexewkb_size;
@@ -1193,6 +1208,7 @@ cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY* topo,
     appendStringInfo(sql, " LIMIT %d", elems_requested);
   }
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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);
@@ -1236,6 +1252,7 @@ static int
 cb_insertNodes( const LWT_BE_TOPOLOGY* topo,
       LWT_ISO_NODE* nodes, int numelems )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1255,6 +1272,7 @@ cb_insertNodes( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_insertNodes query: %s", sql->data);
 
   spi_result = SPI_execute(sql->data, false, numelems);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_INSERT_RETURNING ) {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
             spi_result, sql->data);
@@ -1286,6 +1304,7 @@ static int
 cb_insertEdges( const LWT_BE_TOPOLOGY* topo,
       LWT_ISO_EDGE* edges, int numelems )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1307,6 +1326,7 @@ cb_insertEdges( const LWT_BE_TOPOLOGY* topo,
 
   POSTGIS_DEBUGF(1, "cb_insertEdges query (%d elems): %s", numelems, sql->data);
   spi_result = SPI_execute(sql->data, false, numelems);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != ( needsEdgeIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1340,6 +1360,7 @@ static int
 cb_insertFaces( const LWT_BE_TOPOLOGY* topo,
       LWT_ISO_FACE* faces, int numelems )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1360,6 +1381,7 @@ cb_insertFaces( const LWT_BE_TOPOLOGY* topo,
 
   POSTGIS_DEBUGF(1, "cb_insertFaces query (%d elems): %s", numelems, sql->data);
   spi_result = SPI_execute(sql->data, false, numelems);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != ( needsFaceIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1395,6 +1417,7 @@ cb_updateEdges( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_EDGE* upd_edge, int upd_fields,
       const LWT_ISO_EDGE* exc_edge, int exc_fields )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1414,6 +1437,7 @@ cb_updateEdges( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateEdges query: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_UPDATE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1435,6 +1459,7 @@ cb_updateNodes( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_NODE* upd_node, int upd_fields,
       const LWT_ISO_NODE* exc_node, int exc_fields )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1454,6 +1479,7 @@ cb_updateNodes( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateNodes: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_UPDATE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1473,6 +1499,7 @@ static int
 cb_updateNodesById( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_NODE* nodes, int numnodes, int fields )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
   int i;
        int spi_result;
   StringInfoData sqldata;
@@ -1519,6 +1546,7 @@ cb_updateNodesById( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateNodesById query: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_UPDATE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1538,6 +1566,7 @@ static int
 cb_updateFacesById( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_FACE* faces, int numfaces )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
   int i;
        int spi_result;
   StringInfoData sqldata;
@@ -1559,6 +1588,7 @@ cb_updateFacesById( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateFacesById query: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_UPDATE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1578,6 +1608,7 @@ static int
 cb_updateEdgesById( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_EDGE* edges, int numedges, int fields )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
   int i;
        int spi_result;
   StringInfoData sqldata;
@@ -1640,6 +1671,7 @@ cb_updateEdgesById( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateEdgesById query: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_UPDATE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1659,6 +1691,7 @@ static int
 cb_deleteEdges( const LWT_BE_TOPOLOGY* topo,
       const LWT_ISO_EDGE* sel_edge, int sel_fields )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1670,6 +1703,7 @@ cb_deleteEdges( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_deleteEdges: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_DELETE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -1688,6 +1722,7 @@ cb_deleteEdges( const LWT_BE_TOPOLOGY* topo,
 static LWT_ELEMID
 cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1699,6 +1734,7 @@ cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo )
   appendStringInfo(sql, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
     topo->name);
   spi_result = SPI_execute(sql->data, false, 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);
@@ -1727,6 +1763,7 @@ static int
 cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
   LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2 )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1750,6 +1787,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit query: %s", sql->data);
 
   spi_result = SPI_execute(sql->data, new_edge2 == -1 ? !topo->be_data->data_changed : false, 0);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != ( new_edge2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) ) {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
             spi_result, sql->data);
@@ -1808,6 +1846,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
       "%d,%d," INT64_FORMAT ",%d)", topo->name,
       topogeo_id, layer_id, negate ? -new_edge1 : new_edge1, element_type);
     spi_result = SPI_execute(sql->data, false, 0);
+    MemoryContextSwitchTo( oldcontext ); /* switch back */
     if ( spi_result != SPI_OK_INSERT ) {
       cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
               spi_result, sql->data);
@@ -1821,6 +1860,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
         "%d,%d," INT64_FORMAT ",%d", topo->name,
         topogeo_id, layer_id, negate ? -new_edge2 : new_edge2, element_type);
       spi_result = SPI_execute(sql->data, false, 0);
+      MemoryContextSwitchTo( oldcontext ); /* switch back */
       if ( spi_result != SPI_OK_INSERT ) {
         cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
                 spi_result, sql->data);
@@ -1830,6 +1870,8 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
     }
   }
 
+  /* TODO: release string info ! */
+
   POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit: updated %d topogeoms", ntopogeoms);
 
   return 1;
@@ -1839,6 +1881,7 @@ static int
 cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
   LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2 )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1846,8 +1889,8 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
   const char *proj = "r.element_id, r.topogeo_id, r.layer_id, r.element_type";
 
   POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit signalled "
-                    "split of face %" PRId64 " into %" PRId64
-                    " and %" PRId64,
+                    "split of face " INT64_FORMAT " into "
+                    INT64_FORMAT " and " INT64_FORMAT,
                     split_face, new_face1, new_face2);
 
   initStringInfo(sql);
@@ -1867,6 +1910,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data);
 
   spi_result = SPI_execute(sql->data, new_face2 == -1 ? !topo->be_data->data_changed : false, 0);
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != ( new_face2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) ) {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
             spi_result, sql->data);
@@ -1927,6 +1971,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
     POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data);
 
     spi_result = SPI_execute(sql->data, false, 0);
+    MemoryContextSwitchTo( oldcontext ); /* switch back */
     if ( spi_result != SPI_OK_INSERT ) {
       cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
               spi_result, sql->data);
@@ -1943,6 +1988,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
       POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data);
 
       spi_result = SPI_execute(sql->data, false, 0);
+      MemoryContextSwitchTo( oldcontext ); /* switch back */
       if ( spi_result != SPI_OK_INSERT ) {
         cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
                 spi_result, sql->data);
@@ -1952,6 +1998,8 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
     }
   }
 
+  /* TODO: release string info */
+
   POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit: updated %d topogeoms", ntopogeoms);
 
   return 1;
@@ -1960,6 +2008,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo,
 static LWT_ELEMID
 cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -1981,6 +2030,7 @@ cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt )
   lwfree(hexewkb);
 
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 1);
+  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);
@@ -2006,6 +2056,7 @@ static int
 cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo,
       const LWT_ELEMID* ids, int numelems )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result, i;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -2020,6 +2071,7 @@ cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo,
   POSTGIS_DEBUGF(1, "cb_deleteFacesById query: %s", sql->data);
 
   spi_result = SPI_execute( sql->data, false, 0 );
+  MemoryContextSwitchTo( oldcontext ); /* switch back */
   if ( spi_result != SPI_OK_DELETE )
   {
                cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
@@ -2040,6 +2092,7 @@ static LWT_ISO_NODE*
 cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
                      int* numelems, int fields, int limit )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -2066,6 +2119,7 @@ cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
   }
   lwpgnotice("cb_getNodeWithinBox2D: query is: %s", sql->data);
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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);
          *numelems = -1; return NULL;
@@ -2108,6 +2162,7 @@ static LWT_ISO_EDGE*
 cb_getEdgeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
                      int* numelems, int fields, int limit )
 {
+  MemoryContext oldcontext = CurrentMemoryContext;
        int spi_result;
   StringInfoData sqldata;
   StringInfo sql = &sqldata;
@@ -2134,6 +2189,7 @@ cb_getEdgeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box,
   }
   lwpgnotice("cb_getEdgeWithinBox2D: query is: %s", sql->data);
   spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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);
          *numelems = -1; return NULL;
@@ -2726,3 +2782,138 @@ Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS)
 
   PG_RETURN_POINTER(geom);
 }
+
+typedef struct FACEEDGESSTATE
+{
+  LWT_ELEMID *elems;
+  int nelems;
+  int curr;
+}
+FACEEDGESSTATE;
+
+/* ST_GetFaceEdges(atopology, aface) */
+Datum ST_GetFaceEdges(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(ST_GetFaceEdges);
+Datum ST_GetFaceEdges(PG_FUNCTION_ARGS)
+{
+  text* toponame_text;
+  char* toponame;
+  LWT_ELEMID face_id;
+  int nelems;
+  LWT_ELEMID *elems;
+  LWT_TOPOLOGY *topo;
+  FuncCallContext *funcctx;
+  MemoryContext oldcontext, newcontext;
+       TupleDesc tupdesc;
+       HeapTuple tuple;
+       AttInMetadata *attinmeta;
+  FACEEDGESSTATE *state;
+  char buf[64];
+       char *values[2];
+       Datum result;
+
+  values[0] = buf;
+  values[1] = &(buf[32]);
+
+  if (SRF_IS_FIRSTCALL())
+  {
+
+    POSTGIS_DEBUG(1, "ST_GetFaceEdges first call");
+    funcctx = SRF_FIRSTCALL_INIT();
+    newcontext = funcctx->multi_call_memory_ctx;
+
+    if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ) {
+      lwpgerror("SQL/MM Spatial exception - null argument");
+      PG_RETURN_NULL();
+    }
+
+    toponame_text = PG_GETARG_TEXT_P(0);
+    toponame = text2cstring(toponame_text);
+    PG_FREE_IF_COPY(toponame_text, 0);
+
+    face_id = PG_GETARG_INT32(1) ;
+
+    if ( SPI_OK_CONNECT != SPI_connect() ) {
+      lwpgerror("Could not connect to SPI");
+      PG_RETURN_NULL();
+    }
+    be_data.data_changed = false;
+
+    topo = lwt_LoadTopology(be_iface, toponame);
+    oldcontext = MemoryContextSwitchTo( newcontext );
+    pfree(toponame);
+    if ( ! topo ) {
+      /* should never reach this point, as lwerror would raise an exception */
+      SPI_finish();
+      PG_RETURN_NULL();
+    }
+
+    POSTGIS_DEBUG(1, "Calling lwt_GetFaceEdges");
+    nelems = lwt_GetFaceEdges(topo, face_id, &elems);
+    POSTGIS_DEBUGF(1, "lwt_GetFaceEdges returned %d", nelems);
+    lwt_FreeTopology(topo);
+
+    if ( nelems < 0 ) {
+      /* should never reach this point, as lwerror would raise an exception */
+      SPI_finish();
+      PG_RETURN_NULL();
+    }
+
+    state = lwalloc(sizeof(FACEEDGESSTATE));
+    state->elems = elems;
+    state->nelems = nelems;
+    state->curr = 0;
+    funcctx->user_fctx = state;
+
+    /*
+     * Build a tuple description for an
+     * geometry_dump tuple
+     */
+    tupdesc = RelationNameGetTupleDesc("topology.getfaceedges_returntype");
+
+    /*
+     * generate attribute metadata needed later to produce
+     * tuples from raw C strings
+     */
+    attinmeta = TupleDescGetAttInMetadata(tupdesc);
+    funcctx->attinmeta = attinmeta;
+
+    POSTGIS_DEBUG(1, "lwt_GetFaceEdges calling SPI_finish");
+
+    MemoryContextSwitchTo(oldcontext);
+
+    SPI_finish();
+  }
+
+  /* stuff done on every call of the function */
+  funcctx = SRF_PERCALL_SETUP();
+
+  /* get state */
+  state = funcctx->user_fctx;
+
+  if ( state->curr == state->nelems )
+  {
+    SRF_RETURN_DONE(funcctx);
+  }
+
+  if ( snprintf(values[0], 32, "%d", state->curr+1) >= 32 )
+  {
+    lwerror("Face edge sequence number does not fit 32 chars ?!: %d",
+            state->curr+1);
+  }
+  if ( snprintf(values[1], 32, INT64_FORMAT,
+              state->elems[state->curr]) >= 32 )
+  {
+    lwerror("Signed edge identifier does not fit 32 chars ?!: "
+            INT64_FORMAT, state->elems[state->curr]);
+  }
+
+  POSTGIS_DEBUGF(1, "ST_GetFaceEdges: cur:%d, val0:%s, val1:%s",
+                    state->curr, values[0], values[1]);
+
+  tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+  result = HeapTupleGetDatum(tuple);
+  state->curr++;
+
+  SRF_RETURN_NEXT(funcctx, result);
+}
index c426f931a5c984b9e798c8511aebc91dfdd7d9ae..5d7236d707a1af7789f73e84cc0b95d8f60f32d6 100644 (file)
@@ -40,104 +40,9 @@ CREATE TYPE topology.GetFaceEdges_ReturnType AS (
 --
 -- 
 CREATE OR REPLACE FUNCTION topology.ST_GetFaceEdges(toponame varchar, face_id integer)
-  RETURNS SETOF topology.GetFaceEdges_ReturnType
-AS
-$$
-DECLARE
-  rec RECORD;
-  bounds geometry;
-  retrec topology.GetFaceEdges_ReturnType;
-  n int;
-  sql TEXT;
-BEGIN
-  --
-  -- toponame and face_id are required
-  -- 
-  IF toponame IS NULL OR face_id IS NULL THEN
-    RAISE EXCEPTION 'SQL/MM Spatial exception - null argument';
-  END IF;
-
-  IF NOT EXISTS(SELECT name FROM topology.topology WHERE name = toponame)  THEN
-    RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
-  END IF;
-
-  n := 1;
-
-  -- Construct the face geometry, then for each ring of each polygon:
-  sql := 'SELECT (ST_DumpRings((ST_Dump(ST_ForceRHR('
-    || 'ST_BuildArea(ST_Collect(geom))))).geom)).geom FROM '
-    || quote_ident(toponame) || '.edge_data WHERE left_face = '
-    || face_id || ' OR right_face = ' || face_id;
-  FOR rec IN EXECUTE sql 
-  LOOP -- {
-
-    -- Find the edges constituting its boundary
-    bounds = ST_Boundary(rec.geom);
-
-
-    sql := 'WITH er2 AS ( ' 
-      || 'WITH er AS ( SELECT ' 
-      || 'min(e.edge_id) over (), count(*) over () as cnt, e.edge_id, '
-      || 'ST_LineLocatePoint($1'
-      || ', ST_LineInterpolatePoint(e.geom, 0.2)) as pos'
-      || ', ST_LineLocatePoint($1'
-      || ', ST_LineInterpolatePoint(e.geom, 0.8)) as pos2 FROM '
-      || quote_ident(toponame)
-      || '.edge e WHERE ( e.left_face = ' || face_id
-      || ' OR e.right_face = ' || face_id
-      || ') AND ST_Covers($1, e.geom)';
-    IF face_id = 0 THEN
-      sql := sql || ' ORDER BY POS ASC) ';
-    ELSE
-      sql := sql || ' ORDER BY POS DESC) ';
-    END IF;
-
-    -- Reorder rows so to start with the one with smaller edge_id
-    sql := sql || 'SELECT row_number() over () - 1 as rn, * FROM er ) '
-               || 'SELECT *, ( rn + cnt - ( select rn FROM er2 WHERE edge_id = min ) ) % cnt AS reord FROM er2 ORDER BY reord';
-
-
-    --RAISE DEBUG 'SQL: %', sql;
-
-    FOR rec IN EXECUTE sql USING bounds
-    LOOP
-
-#ifdef POSTGIS_TOPOLOGY_DEBUG
-      RAISE DEBUG 'rn:%, n:%, edg:%, cnt:%, min:%, reord:%',
-         rec.rn, n, rec.edge_id, rec.cnt, rec.min, rec.reord;
-#endif
-
-      retrec.sequence = n;
-      retrec.edge = rec.edge_id;
-
-      IF face_id = 0 THEN
-        -- if this edge goes in opposite direction to the
-        --       ring bounds, make it with negative orientation
-        IF rec.pos2 < rec.pos THEN -- edge goes in opposite direction
-          retrec.edge = -retrec.edge;
-        END IF;
-      ELSE
-        -- if this edge goes in same direction to the
-        --       ring bounds, make it with negative orientation
-        IF rec.pos2 > rec.pos THEN -- edge goes in same direction
-          retrec.edge = -retrec.edge;
-        END IF;
-      END IF;
-
-      RETURN NEXT retrec;
-
-      n = n+1;
-
-    END LOOP;
-  END LOOP; -- }
-
-  RETURN;
-EXCEPTION
-  WHEN INVALID_SCHEMA_NAME THEN
-    RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
-END
-$$
-LANGUAGE 'plpgsql' STABLE;
+  RETURNS SETOF topology.GetFaceEdges_ReturnType AS
+       'MODULE_PATHNAME', 'ST_GetFaceEdges'
+  LANGUAGE 'c' STABLE;
 --} ST_GetFaceEdges
 
 --{