[postgis-tickets] [SCM] PostGIS branch master updated. 3.1.0rc1-325-g13634f9

git at osgeo.org git at osgeo.org
Mon Jul 12 12:44:49 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, master has been updated
       via  13634f91f99e4d8eeddd63f79a25c947cce1f148 (commit)
       via  c5348d44b775d8b23d0d969a6277f2e04fd03ef2 (commit)
       via  de60f59e46462ec03da356f93eadf8c74261d2b3 (commit)
       via  3349c07c24e2c1c39e3a03c681f8ae574ebd0841 (commit)
       via  7d44a0ddf0968f0bb3543805923dff3c54dfaa42 (commit)
       via  d7a7f60ff6db7586a2d7bf17f3a57fa31190e5a1 (commit)
       via  446cfe43844fcedd9a435960388ccb87d1cb0a50 (commit)
       via  b8f3067f6f14024b94f93cc1c50d42adc403537e (commit)
       via  29852831a2e68f599f024af1e56fd9914d6c2036 (commit)
       via  791e686d7d58e78a9c551bad335855357ca2d44d (commit)
       via  35ea0c3957af12e955e48fff16540e9d89ea7402 (commit)
       via  8488dccb895cd6afc7b5113e1c33699b2e48fbf0 (commit)
       via  22c55934836f841931413d810dab2d7f4f93a9b4 (commit)
       via  181d5fb3981393ab40374b62c7685b4673bbea08 (commit)
       via  77cb26589d5cc889b9d427c932b67226ed11a041 (commit)
       via  aa018f66b4938ba60ec825b3f478327d2be93382 (commit)
       via  115203f9b0c4286ec6382c6e1bbe5535772caeb5 (commit)
      from  63b1ea1a04a1bc14cfeeeb3b51a0e3f9b9b90c7b (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 13634f91f99e4d8eeddd63f79a25c947cce1f148
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Mon Jul 12 22:44:19 2021 +0300

    Add ST_DumpSegments to NEWS

diff --git a/NEWS b/NEWS
index 87eb4c1..b45bd41 100644
--- a/NEWS
+++ b/NEWS
@@ -50,6 +50,7 @@ PostGIS 3.2.0
     from a raster (Paul Ramsey)
   - New postgis.gdal_vsi_options GUC allows out-db rasters on VSI network
     services to be accessed with authentication keys, etc. (Paul Ramsey)
+  - ST_DumpSegments returns a set of segments of input geometry (Aliaksandr Kalenik)
 
 
 PostGIS 3.1.0

commit c5348d44b775d8b23d0d969a6277f2e04fd03ef2
Merge: 63b1ea1 de60f59
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Mon Jul 12 22:41:48 2021 +0300

    ST_DumpSegments (Aliaksandr Kalenik)


commit de60f59e46462ec03da356f93eadf8c74261d2b3
Merge: 63b1ea1 3349c07
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Mon Jul 12 22:31:42 2021 +0300

    Merge 3349c07c24e2c1c39e3a03c681f8ae574ebd0841 into 63b1ea1a04a1bc14cfeeeb3b51a0e3f9b9b90c7b


commit 3349c07c24e2c1c39e3a03c681f8ae574ebd0841
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Mon Jul 12 14:35:16 2021 +0300

    merge lwgeom_dumpsegments.c into lwgeom_dumppoints.c

diff --git a/postgis/Makefile.in b/postgis/Makefile.in
index a4e0de7..9e42c2e 100644
--- a/postgis/Makefile.in
+++ b/postgis/Makefile.in
@@ -93,7 +93,6 @@ PG_OBJS= \
 	lwgeom_triggers.o \
 	lwgeom_dump.o \
 	lwgeom_dumppoints.o \
-	lwgeom_dumpsegments.o \
 	lwgeom_functions_lrs.o \
 	lwgeom_functions_temporal.o \
 	lwgeom_rectree.o \
diff --git a/postgis/lwgeom_dumppoints.c b/postgis/lwgeom_dumppoints.c
index 0f9aabe..223a8e3 100644
--- a/postgis/lwgeom_dumppoints.c
+++ b/postgis/lwgeom_dumppoints.c
@@ -45,6 +45,7 @@
  */
 
 Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS);
+Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS);
 
 struct dumpnode {
 	LWGEOM *geom;
@@ -290,4 +291,218 @@ Datum LWGEOM_dumppoints(PG_FUNCTION_ARGS) {
 	}
 }
 
+PG_FUNCTION_INFO_V1(LWGEOM_dumpsegments);
+Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	MemoryContext oldcontext, newcontext;
+
+	GSERIALIZED *pglwgeom;
+	LWCOLLECTION *lwcoll;
+	LWGEOM *lwgeom;
+	struct dumpstate *state;
+	struct dumpnode *node;
+
+	HeapTuple tuple;
+	Datum pathpt[2];         /* used to construct the composite return value */
+	bool isnull[2] = {0, 0}; /* needed to say neither value is null */
+	Datum result;            /* the actual composite return value */
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		newcontext = funcctx->multi_call_memory_ctx;
+		oldcontext = MemoryContextSwitchTo(newcontext);
+
+		pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
+		lwgeom = lwgeom_from_gserialized(pglwgeom);
+
+		/* return early if nothing to do */
+		if (!lwgeom || lwgeom_is_empty(lwgeom))
+		{
+			MemoryContextSwitchTo(oldcontext);
+			funcctx = SRF_PERCALL_SETUP();
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		/* Create function state */
+		state = lwalloc(sizeof *state);
+		state->root = lwgeom;
+		state->stacklen = 0;
+		state->pathlen = 0;
+		state->pt = 0;
+		state->ring = 0;
+
+		funcctx->user_fctx = state;
+
+		/*
+		 * Push a struct dumpnode on the state stack
+		 */
+		state->stack[0].idx = 0;
+		state->stack[0].geom = lwgeom;
+		state->stacklen++;
+
+		/*
+		 * get tuple description for return type
+		 */
+		if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
+		{
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+		}
+
+		BlessTupleDesc(funcctx->tuple_desc);
+
+		/* get and cache data for constructing int4 arrays */
+		get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
 
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	newcontext = funcctx->multi_call_memory_ctx;
+
+	/* get state */
+	state = funcctx->user_fctx;
+
+	while (1)
+	{
+		POINTARRAY *points = NULL;
+		LWLINE *line;
+		LWTRIANGLE *tri;
+		LWPOLY *poly;
+		POINT4D pt_start, pt_end;
+		POINTARRAY *segment_pa;
+		LWLINE *segment;
+
+		node = &state->stack[state->stacklen - 1];
+		lwgeom = node->geom;
+
+		if (lwgeom->type == LINETYPE || lwgeom->type == TRIANGLETYPE || lwgeom->type == POLYGONTYPE)
+		{
+			if (lwgeom->type == LINETYPE)
+			{
+				line = (LWLINE *)lwgeom;
+
+				if (state->pt < line->points->npoints - 1)
+				{
+					points = line->points;
+				}
+			}
+			if (lwgeom->type == TRIANGLETYPE)
+			{
+				tri = (LWTRIANGLE *)lwgeom;
+
+				if (state->pt == 0)
+				{
+					state->path[state->pathlen++] = Int32GetDatum(state->ring + 1);
+				}
+
+				if (state->pt < 3)
+				{
+					points = tri->points;
+				}
+				else
+				{
+					state->pathlen--;
+				}
+			}
+			if (lwgeom->type == POLYGONTYPE)
+			{
+				poly = (LWPOLY *)lwgeom;
+
+				if (state->pt == poly->rings[state->ring]->npoints)
+				{
+					state->pt = 0;
+					state->ring++;
+					state->pathlen--;
+				}
+
+				if (state->ring < poly->nrings)
+				{
+					if (state->pt == 0)
+					{
+						state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
+						state->pathlen++;
+					}
+
+					if (state->pt < poly->rings[state->ring]->npoints - 1)
+					{
+						points = poly->rings[state->ring];
+					}
+					else
+					{
+						state->pt++;
+						continue;
+					}
+				}
+			}
+
+			if (points)
+			{
+				getPoint4d_p(points, state->pt, &pt_start);
+				getPoint4d_p(points, state->pt + 1, &pt_end);
+
+				segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
+				ptarray_set_point4d(segment_pa, 0, &pt_start);
+				ptarray_set_point4d(segment_pa, 1, &pt_end);
+
+				segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
+
+				state->pt++;
+
+				state->path[state->pathlen] = Int32GetDatum(state->pt);
+				pathpt[0] = PointerGetDatum(construct_array(state->path,
+									    state->pathlen + 1,
+									    INT4OID,
+									    state->typlen,
+									    state->byval,
+									    state->align));
+				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
+
+				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
+				result = HeapTupleGetDatum(tuple);
+				SRF_RETURN_NEXT(funcctx, result);
+			}
+			else
+			{
+				if (--state->stacklen == 0)
+					SRF_RETURN_DONE(funcctx);
+				state->pathlen--;
+				continue;
+			}
+		}
+
+		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE ||
+		    lwgeom->type == MULTIPOLYGONTYPE || lwgeom->type == TINTYPE)
+		{
+			lwcoll = (LWCOLLECTION *)node->geom;
+
+			/* if a collection and we have more geoms */
+			if (node->idx < lwcoll->ngeoms)
+			{
+				/* push the next geom on the path and the stack */
+				lwgeom = lwcoll->geoms[node->idx++];
+				state->path[state->pathlen++] = Int32GetDatum(node->idx);
+
+				node = &state->stack[state->stacklen++];
+				node->idx = 0;
+				node->geom = lwgeom;
+
+				state->pt = 0;
+				state->ring = 0;
+
+				/* loop back to beginning, which will then check whatever node we just pushed */
+				continue;
+			}
+		}
+
+		/* no more geometries in the current collection */
+		if (--state->stacklen == 0)
+			SRF_RETURN_DONE(funcctx);
+		state->pathlen--;
+	}
+}
diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
deleted file mode 100644
index 9ff3ba1..0000000
--- a/postgis/lwgeom_dumpsegments.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/**********************************************************************
- *
- * PostGIS - Spatial Types for PostgreSQL
- * http://postgis.net
- *
- * PostGIS is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * PostGIS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
- *
- **********************************************************************/
-
-#include "postgres.h"
-#include "fmgr.h"
-#include "utils/elog.h"
-#include "utils/array.h"
-#include "utils/geo_decls.h"
-#include "utils/lsyscache.h"
-#include "catalog/pg_type.h"
-#include "funcapi.h"
-
-#include "../postgis_config.h"
-#include "lwgeom_pg.h"
-
-#include "access/htup_details.h"
-
-#include "liblwgeom.h"
-
-Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS);
-
-struct dumpnode {
-	LWGEOM *geom;
-	uint32_t idx; /* which member geom we're working on */
-};
-
-/* 32 is the max depth for st_dump, so it seems reasonable
- * to use the same here
- */
-#define MAXDEPTH 32
-struct dumpstate {
-	LWGEOM *root;
-	int stacklen; /* collections/geoms on stack */
-	int pathlen;  /* polygon rings and such need extra path info */
-	struct dumpnode stack[MAXDEPTH];
-	Datum path[34]; /* two more than max depth, for ring and point */
-
-	/* used to cache the type attributes for integer arrays */
-	int16 typlen;
-	bool byval;
-	char align;
-
-	uint32_t ring; /* ring of top polygon */
-	uint32_t pt;   /* point of top geom or current ring */
-};
-
-PG_FUNCTION_INFO_V1(LWGEOM_dumpsegments);
-Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
-{
-	FuncCallContext *funcctx;
-	MemoryContext oldcontext, newcontext;
-
-	GSERIALIZED *pglwgeom;
-	LWCOLLECTION *lwcoll;
-	LWGEOM *lwgeom;
-	struct dumpstate *state;
-	struct dumpnode *node;
-
-	HeapTuple tuple;
-	Datum pathpt[2];         /* used to construct the composite return value */
-	bool isnull[2] = {0, 0}; /* needed to say neither value is null */
-	Datum result;            /* the actual composite return value */
-
-	if (SRF_IS_FIRSTCALL())
-	{
-		funcctx = SRF_FIRSTCALL_INIT();
-
-		newcontext = funcctx->multi_call_memory_ctx;
-		oldcontext = MemoryContextSwitchTo(newcontext);
-
-		pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
-		lwgeom = lwgeom_from_gserialized(pglwgeom);
-
-		/* return early if nothing to do */
-		if (!lwgeom || lwgeom_is_empty(lwgeom))
-		{
-			MemoryContextSwitchTo(oldcontext);
-			funcctx = SRF_PERCALL_SETUP();
-			SRF_RETURN_DONE(funcctx);
-		}
-
-		/* Create function state */
-		state = lwalloc(sizeof *state);
-		state->root = lwgeom;
-		state->stacklen = 0;
-		state->pathlen = 0;
-		state->pt = 0;
-		state->ring = 0;
-
-		funcctx->user_fctx = state;
-
-		/*
-		 * Push a struct dumpnode on the state stack
-		 */
-		state->stack[0].idx = 0;
-		state->stack[0].geom = lwgeom;
-		state->stacklen++;
-
-		/*
-		 * get tuple description for return type
-		 */
-		if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
-		{
-			ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-		}
-
-		BlessTupleDesc(funcctx->tuple_desc);
-
-		/* get and cache data for constructing int4 arrays */
-		get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
-
-		MemoryContextSwitchTo(oldcontext);
-	}
-
-	/* stuff done on every call of the function */
-	funcctx = SRF_PERCALL_SETUP();
-	newcontext = funcctx->multi_call_memory_ctx;
-
-	/* get state */
-	state = funcctx->user_fctx;
-
-	while (1)
-	{
-		POINTARRAY *points = NULL;
-		LWLINE *line;
-		LWTRIANGLE *tri;
-		LWPOLY *poly;
-		POINT4D pt_start, pt_end;
-		POINTARRAY *segment_pa;
-		LWLINE *segment;
-
-		node = &state->stack[state->stacklen - 1];
-		lwgeom = node->geom;
-
-		if (lwgeom->type == LINETYPE || lwgeom->type == TRIANGLETYPE || lwgeom->type == POLYGONTYPE)
-		{
-			if (lwgeom->type == LINETYPE)
-			{
-				line = (LWLINE *)lwgeom;
-
-				if (state->pt < line->points->npoints - 1)
-				{
-					points = line->points;
-				}
-			}
-			if (lwgeom->type == TRIANGLETYPE)
-			{
-				tri = (LWTRIANGLE *)lwgeom;
-
-				if (state->pt == 0)
-				{
-					state->path[state->pathlen++] = Int32GetDatum(state->ring + 1);
-				}
-
-				if (state->pt < 3)
-				{
-					points = tri->points;
-				}
-				else
-				{
-					state->pathlen--;
-				}
-			}
-			if (lwgeom->type == POLYGONTYPE)
-			{
-				poly = (LWPOLY *)lwgeom;
-
-				if (state->pt == poly->rings[state->ring]->npoints)
-				{
-					state->pt = 0;
-					state->ring++;
-					state->pathlen--;
-				}
-
-				if (state->ring < poly->nrings)
-				{
-					if (state->pt == 0)
-					{
-						state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
-						state->pathlen++;
-					}
-
-					if (state->pt < poly->rings[state->ring]->npoints - 1)
-					{
-						points = poly->rings[state->ring];
-					}
-					else
-					{
-						state->pt++;
-						continue;
-					}
-				}
-			}
-
-			if (points)
-			{
-				getPoint4d_p(points, state->pt, &pt_start);
-				getPoint4d_p(points, state->pt + 1, &pt_end);
-
-				segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
-				ptarray_set_point4d(segment_pa, 0, &pt_start);
-				ptarray_set_point4d(segment_pa, 1, &pt_end);
-
-				segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
-
-				state->pt++;
-
-				state->path[state->pathlen] = Int32GetDatum(state->pt);
-				pathpt[0] = PointerGetDatum(construct_array(state->path,
-									    state->pathlen + 1,
-									    INT4OID,
-									    state->typlen,
-									    state->byval,
-									    state->align));
-				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
-
-				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
-				result = HeapTupleGetDatum(tuple);
-				SRF_RETURN_NEXT(funcctx, result);
-			}
-			else
-			{
-				if (--state->stacklen == 0)
-					SRF_RETURN_DONE(funcctx);
-				state->pathlen--;
-				continue;
-			}
-		}
-
-		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE ||
-		    lwgeom->type == MULTIPOLYGONTYPE || lwgeom->type == TINTYPE)
-		{
-			lwcoll = (LWCOLLECTION *)node->geom;
-
-			/* if a collection and we have more geoms */
-			if (node->idx < lwcoll->ngeoms)
-			{
-				/* push the next geom on the path and the stack */
-				lwgeom = lwcoll->geoms[node->idx++];
-				state->path[state->pathlen++] = Int32GetDatum(node->idx);
-
-				node = &state->stack[state->stacklen++];
-				node->idx = 0;
-				node->geom = lwgeom;
-
-				state->pt = 0;
-				state->ring = 0;
-
-				/* loop back to beginning, which will then check whatever node we just pushed */
-				continue;
-			}
-		}
-
-		/* no more geometries in the current collection */
-		if (--state->stacklen == 0)
-			SRF_RETURN_DONE(funcctx);
-		state->pathlen--;
-	}
-}

commit 7d44a0ddf0968f0bb3543805923dff3c54dfaa42
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Wed May 19 14:25:56 2021 +0300

    doc

diff --git a/doc/reference_accessor.xml b/doc/reference_accessor.xml
index 227b810..8d23ce1 100644
--- a/doc/reference_accessor.xml
+++ b/doc/reference_accessor.xml
@@ -739,6 +739,137 @@ SELECT (g.gdump).path, ST_AsEWKT((g.gdump).geom) as wkt
             <xref linkend="ST_Dump" />, <xref linkend="ST_DumpRings" />, <xref linkend="ST_Points" /></para>
 		</refsection>
 	</refentry>
+
+	<refentry id="ST_DumpSegments">
+		<refnamediv>
+			<refname>ST_DumpSegments</refname>
+			<refpurpose>Returns a set of <varname>geometry_dump</varname> rows for the segments in a geometry.</refpurpose>
+		</refnamediv>
+
+		<refsynopsisdiv>
+			<funcsynopsis>
+				<funcprototype>
+				<funcdef>geometry_dump[] <function>ST_DumpSegments</function></funcdef>
+				<paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
+			</funcprototype>
+		</funcsynopsis>
+		</refsynopsisdiv>
+
+		<refsection>
+			<title>Description</title>
+			<para>A set-returning function (SRF) that extracts the segments of a geometry.
+            It returns a set of
+			    <xref linkend="geometry_dump" /> rows,
+                each containing a geometry (<parameter>geom</parameter> field)
+                and an array of integers (<parameter>path</parameter> field).
+            </para>
+
+            <itemizedlist>
+            <listitem>
+                <para>the <parameter>geom</parameter> field
+                    <varname>LINESTRING</varname>s represent the segments of the supplied geometry.
+                </para>
+            </listitem>
+            <listitem>
+                <para>the <parameter>path</parameter> field (an <varname>integer[]</varname>)
+                    is an index enumerating the segment start point positions in the elements of the supplied geometry.
+                    The indices are 1-based.
+                    For example, for a <varname>LINESTRING</varname> the paths are <varname>{i}</varname>
+                    where <varname>i</varname> is the <varname>nth</varname>
+                    segment start point in the <varname>LINESTRING</varname>.
+                    For a <varname>POLYGON</varname> the paths are <varname>{i,j}</varname> where
+                    <varname>i</varname> is the ring number (1 is outer; inner rings follow)
+                    and <varname>j</varname> is the segment start point position in the ring.
+                </para>
+            </listitem>
+            </itemizedlist>
+
+            <para>Availability: 3.2.0</para>
+            <para>&T_support;</para>
+            <para>&Z_support;</para>
+		</refsection>
+
+		<refsection>
+			<title>Standard Geometry Examples</title>
+
+			<programlisting>SELECT path, ST_AsText(geom)
+FROM (
+    SELECT (ST_DumpSegments(g.geom)).*
+    FROM (SELECT 'GEOMETRYCOLLECTION(
+    LINESTRING(1 1, 3 3, 4 4),
+    POLYGON((5 5, 6 6, 7 7, 5 5))
+)'::geometry AS geom
+        ) AS g
+) j;
+
+  path   │      st_astext
+---------------------------------
+ {1,1}   │ LINESTRING(1 1,3 3)
+ {1,2}   │ LINESTRING(3 3,4 4)
+ {2,1,1} │ LINESTRING(5 5,6 6)
+ {2,1,2} │ LINESTRING(6 6,7 7)
+ {2,1,3} │ LINESTRING(7 7,5 5)
+(5 rows)</programlisting>
+		</refsection>
+	<refsection>
+			<title>TIN and Triangle Examples</title>
+<programlisting>-- Triangle --
+SELECT path, ST_AsText(geom)
+FROM (
+    SELECT (ST_DumpSegments(g.geom)).*
+    FROM (SELECT 'TRIANGLE((
+        0 0,
+        0 9,
+        9 0,
+        0 0
+    ))'::geometry AS geom
+        ) AS g
+) j;
+
+ path  │      st_astext
+ ---------------------------------
+ {1,1} │ LINESTRING(0 0,0 9)
+ {1,2} │ LINESTRING(0 9,9 0)
+ {1,3} │ LINESTRING(9 0,0 0)
+(3 rows)
+</programlisting>
+<programlisting>-- TIN --
+SELECT path, ST_AsEWKT(geom)
+FROM (
+    SELECT (ST_DumpSegments(g.geom)).*
+    FROM (SELECT 'TIN(((
+        0 0 0,
+        0 0 1,
+        0 1 0,
+        0 0 0
+    )), ((
+        0 0 0,
+        0 1 0,
+        1 1 0,
+        0 0 0
+    ))
+    )'::geometry AS geom
+        ) AS g
+) j;
+
+  path   │        st_asewkt
+  ---------------------------------
+ {1,1,1} │ LINESTRING(0 0 0,0 0 1)
+ {1,1,2} │ LINESTRING(0 0 1,0 1 0)
+ {1,1,3} │ LINESTRING(0 1 0,0 0 0)
+ {2,1,1} │ LINESTRING(0 0 0,0 1 0)
+ {2,1,2} │ LINESTRING(0 1 0,1 1 0)
+ {2,1,3} │ LINESTRING(1 1 0,0 0 0)
+(6 rows)
+</programlisting>
+		</refsection>
+			<refsection>
+			<title>See Also</title>
+			<para><xref linkend="geometry_dump" />, <xref linkend="PostGIS_Geometry_DumpFunctions" />,
+            <xref linkend="ST_Dump" />, <xref linkend="ST_DumpRings" /></para>
+		</refsection>
+	</refentry>
+
 	<refentry id="ST_DumpRings">
 	  <refnamediv>
 		<refname>ST_DumpRings</refname>

commit d7a7f60ff6db7586a2d7bf17f3a57fa31190e5a1
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Wed May 19 12:09:42 2021 +0300

    remove copyright

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index 22854dc..9ff3ba1 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -16,10 +16,6 @@
  * You should have received a copy of the GNU General Public License
  * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
  *
- **********************************************************************
- *
- * ^copyright^
- *
  **********************************************************************/
 
 #include "postgres.h"

commit 446cfe43844fcedd9a435960388ccb87d1cb0a50
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Wed May 19 12:06:02 2021 +0300

    triange + tin

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index d6e7b9c..22854dc 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -146,6 +146,7 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 	{
 		POINTARRAY *points = NULL;
 		LWLINE *line;
+		LWTRIANGLE *tri;
 		LWPOLY *poly;
 		POINT4D pt_start, pt_end;
 		POINTARRAY *segment_pa;
@@ -154,20 +155,38 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		node = &state->stack[state->stacklen - 1];
 		lwgeom = node->geom;
 
-		if (lwgeom->type == LINETYPE || lwgeom->type == POLYGONTYPE)
+		if (lwgeom->type == LINETYPE || lwgeom->type == TRIANGLETYPE || lwgeom->type == POLYGONTYPE)
 		{
 			if (lwgeom->type == LINETYPE)
 			{
-				line = lwgeom_as_lwline(lwgeom);
+				line = (LWLINE *)lwgeom;
 
 				if (state->pt < line->points->npoints - 1)
 				{
 					points = line->points;
 				}
 			}
+			if (lwgeom->type == TRIANGLETYPE)
+			{
+				tri = (LWTRIANGLE *)lwgeom;
+
+				if (state->pt == 0)
+				{
+					state->path[state->pathlen++] = Int32GetDatum(state->ring + 1);
+				}
+
+				if (state->pt < 3)
+				{
+					points = tri->points;
+				}
+				else
+				{
+					state->pathlen--;
+				}
+			}
 			if (lwgeom->type == POLYGONTYPE)
 			{
-				poly = lwgeom_as_lwpoly(lwgeom);
+				poly = (LWPOLY *)lwgeom;
 
 				if (state->pt == poly->rings[state->ring]->npoints)
 				{
@@ -175,13 +194,15 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 					state->ring++;
 					state->pathlen--;
 				}
-				if (state->pt == 0 && state->ring < poly->nrings)
-				{
-					state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
-					state->pathlen++;
-				}
+
 				if (state->ring < poly->nrings)
 				{
+					if (state->pt == 0)
+					{
+						state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
+						state->pathlen++;
+					}
+
 					if (state->pt < poly->rings[state->ring]->npoints - 1)
 					{
 						points = poly->rings[state->ring];
@@ -229,7 +250,8 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 			}
 		}
 
-		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE || lwgeom->type == MULTIPOLYGONTYPE)
+		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE ||
+		    lwgeom->type == MULTIPOLYGONTYPE || lwgeom->type == TINTYPE)
 		{
 			lwcoll = (LWCOLLECTION *)node->geom;
 
diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
index 652f204..3991021 100644
--- a/regress/core/dumpsegments.sql
+++ b/regress/core/dumpsegments.sql
@@ -41,3 +41,33 @@ FROM (
         )'::geometry AS geom
               ) AS g
      ) j;
+
+SELECT 'dumpsegments11', path, ST_AsText(geom)
+FROM (
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'TRIANGLE((
+                0 0,
+                0 9,
+                9 0,
+                0 0
+            ))'::geometry AS geom
+              ) AS g
+     ) j;
+
+SELECT 'dumpsegments12', path, ST_AsEWKT(geom)
+FROM (
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'TIN(((
+                0 0 0,
+                0 0 1,
+                0 1 0,
+                0 0 0
+            )), ((
+                0 0 0,
+                0 1 0,
+                1 1 0,
+                0 0 0
+            ))
+            )'::geometry AS geom
+              ) AS g
+     ) j;
diff --git a/regress/core/dumpsegments_expected b/regress/core/dumpsegments_expected
index 5abbbe9..5d1c8d3 100644
--- a/regress/core/dumpsegments_expected
+++ b/regress/core/dumpsegments_expected
@@ -24,3 +24,12 @@ dumpsegments10|{1,1}|LINESTRING(1 1,3 3)
 dumpsegments10|{2,1,1}|LINESTRING(4 4,5 5)
 dumpsegments10|{2,1,2}|LINESTRING(5 5,6 6)
 dumpsegments10|{2,1,3}|LINESTRING(6 6,4 4)
+dumpsegments11|{1,1}|LINESTRING(0 0,0 9)
+dumpsegments11|{1,2}|LINESTRING(0 9,9 0)
+dumpsegments11|{1,3}|LINESTRING(9 0,0 0)
+dumpsegments12|{1,1,1}|LINESTRING(0 0 0,0 0 1)
+dumpsegments12|{1,1,2}|LINESTRING(0 0 1,0 1 0)
+dumpsegments12|{1,1,3}|LINESTRING(0 1 0,0 0 0)
+dumpsegments12|{2,1,1}|LINESTRING(0 0 0,0 1 0)
+dumpsegments12|{2,1,2}|LINESTRING(0 1 0,1 1 0)
+dumpsegments12|{2,1,3}|LINESTRING(1 1 0,0 0 0)

commit b8f3067f6f14024b94f93cc1c50d42adc403537e
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 20:39:27 2021 +0300

    add availability

diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 98e2e78..41ac569 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -1960,6 +1960,7 @@ CREATE OR REPLACE FUNCTION ST_DumpPoints(geometry)
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;
 
+-- Availability: 3.2.0
 CREATE OR REPLACE FUNCTION ST_DumpSegments(geometry)
 	RETURNS SETOF geometry_dump
 	AS 'MODULE_PATHNAME', 'LWGEOM_dumpsegments'

commit 29852831a2e68f599f024af1e56fd9914d6c2036
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 03:57:42 2021 +0300

    fix

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index 7490cd9..d6e7b9c 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -256,6 +256,5 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		if (--state->stacklen == 0)
 			SRF_RETURN_DONE(funcctx);
 		state->pathlen--;
-		state->stack[state->stacklen - 1].idx++;
 	}
 }

commit 791e686d7d58e78a9c551bad335855357ca2d44d
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 02:23:16 2021 +0300

    refactoring

diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
index 4112627..652f204 100644
--- a/regress/core/dumpsegments.sql
+++ b/regress/core/dumpsegments.sql
@@ -1,21 +1,18 @@
 SELECT 'dumpsegments01', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpSegments(g.geom)).*
-  FROM
-    (SELECT 'LINESTRING(0 0, 0 9, 9 9, 9 0, 0 0)'::geometry AS geom) AS g
-  ) j;
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'LINESTRING(0 0, 0 9, 9 9, 9 0, 0 0)'::geometry AS geom) AS g
+     ) j;
 
 SELECT 'dumpsegments02', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpSegments(g.geom)).*
-  FROM
-    (SELECT
-       'GEOMETRYCOLLECTION(
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'GEOMETRYCOLLECTION(
           LINESTRING(1 1, 3 3),
           MULTILINESTRING((4 4, 5 5, 6 6, 7 7), (8 8, 9 9, 10 10))
         )'::geometry AS geom
-    ) AS g
-  ) j;
+              ) AS g
+     ) j;
 
 SELECT 'dumpsegments03', ST_DumpSegments('POLYGON EMPTY'::geometry);
 SELECT 'dumpsegments04', ST_DumpSegments('MULTIPOLYGON EMPTY'::geometry);
@@ -25,26 +22,22 @@ SELECT 'dumpsegments07', ST_DumpSegments('GEOMETRYCOLLECTION EMPTY'::geometry);
 
 SELECT 'dumpsegments08', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpSegments(g.geom)).*
-  FROM
-    (SELECT 'POLYGON((4 4, 5 5, 6 6, 4 4), (8 8, 9 9, 10 10, 8 8))'::geometry AS geom) AS g
-  ) j;
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'POLYGON((4 4, 5 5, 6 6, 4 4), (8 8, 9 9, 10 10, 8 8))'::geometry AS geom) AS g
+     ) j;
 
 SELECT 'dumpsegments09', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpSegments(g.geom)).*
-  FROM
-    (SELECT 'MULTIPOLYGON(((4 4, 5 5, 6 6, 4 4)), ((8 8, 9 9, 10 10, 8 8)))'::geometry AS geom) AS g
-  ) j;
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'MULTIPOLYGON(((4 4, 5 5, 6 6, 4 4)), ((8 8, 9 9, 10 10, 8 8)))'::geometry AS geom) AS g
+     ) j;
 
 SELECT 'dumpsegments10', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpSegments(g.geom)).*
-  FROM
-    (SELECT
-       'GEOMETRYCOLLECTION(
+         SELECT (ST_DumpSegments(g.geom)).*
+         FROM (SELECT 'GEOMETRYCOLLECTION(
           LINESTRING(1 1, 3 3),
           POLYGON((4 4, 5 5, 6 6, 4 4))
         )'::geometry AS geom
-    ) AS g
-  ) j;
+              ) AS g
+     ) j;

commit 35ea0c3957af12e955e48fff16540e9d89ea7402
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 02:20:18 2021 +0300

    refactoring

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index 585cde3..7490cd9 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -113,7 +113,6 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		/*
 		 * Push a struct dumpnode on the state stack
 		 */
-
 		state->stack[0].idx = 0;
 		state->stack[0].geom = lwgeom;
 		state->stacklen++;
@@ -145,7 +144,7 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 
 	while (1)
 	{
-		POINTARRAY *points;
+		POINTARRAY *points = NULL;
 		LWLINE *line;
 		LWPOLY *poly;
 		POINT4D pt_start, pt_end;
@@ -165,13 +164,6 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 				{
 					points = line->points;
 				}
-				else
-				{
-					if (--state->stacklen == 0)
-						SRF_RETURN_DONE(funcctx);
-					state->pathlen--;
-					continue;
-				}
 			}
 			if (lwgeom->type == POLYGONTYPE)
 			{
@@ -188,43 +180,53 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 					state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
 					state->pathlen++;
 				}
-				if (state->ring == poly->nrings)
-				{
-					if (--state->stacklen == 0)
-						SRF_RETURN_DONE(funcctx);
-					state->pathlen--;
-					continue;
-				}
-				else if (state->pt < poly->rings[state->ring]->npoints - 1)
+				if (state->ring < poly->nrings)
 				{
-					points = poly->rings[state->ring];
-				}
-				else
-				{
-					state->pt++;
-					continue;
+					if (state->pt < poly->rings[state->ring]->npoints - 1)
+					{
+						points = poly->rings[state->ring];
+					}
+					else
+					{
+						state->pt++;
+						continue;
+					}
 				}
 			}
 
-			getPoint4d_p(points, state->pt, &pt_start);
-			getPoint4d_p(points, state->pt + 1, &pt_end);
+			if (points)
+			{
+				getPoint4d_p(points, state->pt, &pt_start);
+				getPoint4d_p(points, state->pt + 1, &pt_end);
 
-			segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
-			ptarray_set_point4d(segment_pa, 0, &pt_start);
-			ptarray_set_point4d(segment_pa, 1, &pt_end);
+				segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
+				ptarray_set_point4d(segment_pa, 0, &pt_start);
+				ptarray_set_point4d(segment_pa, 1, &pt_end);
 
-			segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
+				segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
 
-			state->pt++;
+				state->pt++;
 
-			state->path[state->pathlen] = Int32GetDatum(state->pt);
-			pathpt[0] = PointerGetDatum(construct_array(
-			    state->path, state->pathlen + 1, INT4OID, state->typlen, state->byval, state->align));
-			pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
+				state->path[state->pathlen] = Int32GetDatum(state->pt);
+				pathpt[0] = PointerGetDatum(construct_array(state->path,
+									    state->pathlen + 1,
+									    INT4OID,
+									    state->typlen,
+									    state->byval,
+									    state->align));
+				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
 
-			tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
-			result = HeapTupleGetDatum(tuple);
-			SRF_RETURN_NEXT(funcctx, result);
+				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
+				result = HeapTupleGetDatum(tuple);
+				SRF_RETURN_NEXT(funcctx, result);
+			}
+			else
+			{
+				if (--state->stacklen == 0)
+					SRF_RETURN_DONE(funcctx);
+				state->pathlen--;
+				continue;
+			}
 		}
 
 		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE || lwgeom->type == MULTIPOLYGONTYPE)
diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
index 3e0259e..4112627 100644
--- a/regress/core/dumpsegments.sql
+++ b/regress/core/dumpsegments.sql
@@ -2,15 +2,7 @@ SELECT 'dumpsegments01', path, ST_AsText(geom)
 FROM (
   SELECT (ST_DumpSegments(g.geom)).*
   FROM
-    (SELECT
-       'LINESTRING(
-                0 0,
-                0 9,
-                9 9,
-                9 0,
-                0 0
-            )'::geometry AS geom
-    ) AS g
+    (SELECT 'LINESTRING(0 0, 0 9, 9 9, 9 0, 0 0)'::geometry AS geom) AS g
   ) j;
 
 SELECT 'dumpsegments02', path, ST_AsText(geom)
@@ -35,38 +27,14 @@ SELECT 'dumpsegments08', path, ST_AsText(geom)
 FROM (
   SELECT (ST_DumpSegments(g.geom)).*
   FROM
-    (SELECT
-       'POLYGON((
-                4 4,
-                5 5,
-                6 6,
-                4 4
-            ), (
-                8 8,
-                9 9,
-                10 10,
-                8 8
-            ))'::geometry AS geom
-    ) AS g
+    (SELECT 'POLYGON((4 4, 5 5, 6 6, 4 4), (8 8, 9 9, 10 10, 8 8))'::geometry AS geom) AS g
   ) j;
 
 SELECT 'dumpsegments09', path, ST_AsText(geom)
 FROM (
   SELECT (ST_DumpSegments(g.geom)).*
   FROM
-    (SELECT
-       'MULTIPOLYGON(((
-                4 4,
-                5 5,
-                6 6,
-                4 4
-            )), ((
-                8 8,
-                9 9,
-                10 10,
-                8 8
-            )))'::geometry AS geom
-    ) AS g
+    (SELECT 'MULTIPOLYGON(((4 4, 5 5, 6 6, 4 4)), ((8 8, 9 9, 10 10, 8 8)))'::geometry AS geom) AS g
   ) j;
 
 SELECT 'dumpsegments10', path, ST_AsText(geom)

commit 8488dccb895cd6afc7b5113e1c33699b2e48fbf0
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 00:52:03 2021 +0300

    fix

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index 5471a5d..585cde3 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -145,9 +145,6 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 
 	while (1)
 	{
-		node = &state->stack[state->stacklen - 1];
-		lwgeom = node->geom;
-
 		POINTARRAY *points;
 		LWLINE *line;
 		LWPOLY *poly;
@@ -155,6 +152,9 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		POINTARRAY *segment_pa;
 		LWLINE *segment;
 
+		node = &state->stack[state->stacklen - 1];
+		lwgeom = node->geom;
+
 		if (lwgeom->type == LINETYPE || lwgeom->type == POLYGONTYPE)
 		{
 			if (lwgeom->type == LINETYPE)

commit 22c55934836f841931413d810dab2d7f4f93a9b4
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 00:47:45 2021 +0300

    fix

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index 30c5e63..5471a5d 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -149,12 +149,17 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		lwgeom = node->geom;
 
 		POINTARRAY *points;
+		LWLINE *line;
+		LWPOLY *poly;
+		POINT4D pt_start, pt_end;
+		POINTARRAY *segment_pa;
+		LWLINE *segment;
 
-		if (!lwgeom_is_collection(lwgeom))
+		if (lwgeom->type == LINETYPE || lwgeom->type == POLYGONTYPE)
 		{
 			if (lwgeom->type == LINETYPE)
 			{
-				LWLINE *line = lwgeom_as_lwline(lwgeom);
+				line = lwgeom_as_lwline(lwgeom);
 
 				if (state->pt < line->points->npoints - 1)
 				{
@@ -170,7 +175,7 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 			}
 			if (lwgeom->type == POLYGONTYPE)
 			{
-				LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
+				poly = lwgeom_as_lwpoly(lwgeom);
 
 				if (state->pt == poly->rings[state->ring]->npoints)
 				{
@@ -201,15 +206,14 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 				}
 			}
 
-			POINT4D pt_start, pt_end;
 			getPoint4d_p(points, state->pt, &pt_start);
 			getPoint4d_p(points, state->pt + 1, &pt_end);
 
-			POINTARRAY *segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
+			segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
 			ptarray_set_point4d(segment_pa, 0, &pt_start);
 			ptarray_set_point4d(segment_pa, 1, &pt_end);
 
-			LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
+			segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
 
 			state->pt++;
 

commit 181d5fb3981393ab40374b62c7685b4673bbea08
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 00:32:41 2021 +0300

    more tests

diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
index 21621a1..3e0259e 100644
--- a/regress/core/dumpsegments.sql
+++ b/regress/core/dumpsegments.sql
@@ -49,3 +49,34 @@ FROM (
             ))'::geometry AS geom
     ) AS g
   ) j;
+
+SELECT 'dumpsegments09', path, ST_AsText(geom)
+FROM (
+  SELECT (ST_DumpSegments(g.geom)).*
+  FROM
+    (SELECT
+       'MULTIPOLYGON(((
+                4 4,
+                5 5,
+                6 6,
+                4 4
+            )), ((
+                8 8,
+                9 9,
+                10 10,
+                8 8
+            )))'::geometry AS geom
+    ) AS g
+  ) j;
+
+SELECT 'dumpsegments10', path, ST_AsText(geom)
+FROM (
+  SELECT (ST_DumpSegments(g.geom)).*
+  FROM
+    (SELECT
+       'GEOMETRYCOLLECTION(
+          LINESTRING(1 1, 3 3),
+          POLYGON((4 4, 5 5, 6 6, 4 4))
+        )'::geometry AS geom
+    ) AS g
+  ) j;
diff --git a/regress/core/dumpsegments_expected b/regress/core/dumpsegments_expected
index ff06d45..5abbbe9 100644
--- a/regress/core/dumpsegments_expected
+++ b/regress/core/dumpsegments_expected
@@ -14,3 +14,13 @@ dumpsegments08|{1,3}|LINESTRING(6 6,4 4)
 dumpsegments08|{2,1}|LINESTRING(8 8,9 9)
 dumpsegments08|{2,2}|LINESTRING(9 9,10 10)
 dumpsegments08|{2,3}|LINESTRING(10 10,8 8)
+dumpsegments09|{1,1,1}|LINESTRING(4 4,5 5)
+dumpsegments09|{1,1,2}|LINESTRING(5 5,6 6)
+dumpsegments09|{1,1,3}|LINESTRING(6 6,4 4)
+dumpsegments09|{2,1,1}|LINESTRING(8 8,9 9)
+dumpsegments09|{2,1,2}|LINESTRING(9 9,10 10)
+dumpsegments09|{2,1,3}|LINESTRING(10 10,8 8)
+dumpsegments10|{1,1}|LINESTRING(1 1,3 3)
+dumpsegments10|{2,1,1}|LINESTRING(4 4,5 5)
+dumpsegments10|{2,1,2}|LINESTRING(5 5,6 6)
+dumpsegments10|{2,1,3}|LINESTRING(6 6,4 4)

commit 77cb26589d5cc889b9d427c932b67226ed11a041
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Tue May 18 00:22:54 2021 +0300

    refactoring

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index daad231..30c5e63 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -148,103 +148,79 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 		node = &state->stack[state->stacklen - 1];
 		lwgeom = node->geom;
 
-		if (lwgeom->type == LINETYPE)
-		{
-			LWLINE *line = lwgeom_as_lwline(lwgeom);
+		POINTARRAY *points;
 
-			if (state->pt < line->points->npoints - 1)
-			{
-				POINT4D pt_start, pt_end;
-				getPoint4d_p(line->points, state->pt, &pt_start);
-				getPoint4d_p(line->points, state->pt + 1, &pt_end);
-
-				POINTARRAY *segment_pa =
-				    ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
-				ptarray_set_point4d(segment_pa, 0, &pt_start);
-				ptarray_set_point4d(segment_pa, 1, &pt_end);
-
-				LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
-
-				state->pt++;
-
-				state->path[state->pathlen] = Int32GetDatum(state->pt);
-				pathpt[0] = PointerGetDatum(construct_array(state->path,
-									    state->pathlen + 1,
-									    INT4OID,
-									    state->typlen,
-									    state->byval,
-									    state->align));
-				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
-
-				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
-				result = HeapTupleGetDatum(tuple);
-				SRF_RETURN_NEXT(funcctx, result);
-			}
-			else
-			{
-				if (--state->stacklen == 0)
-					SRF_RETURN_DONE(funcctx);
-				state->pathlen--;
-				continue;
-			}
-		}
-		if (lwgeom->type == POLYGONTYPE)
+		if (!lwgeom_is_collection(lwgeom))
 		{
-			LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
-
-			if (state->pt == poly->rings[state->ring]->npoints)
+			if (lwgeom->type == LINETYPE)
 			{
-				state->pt = 0;
-				state->ring++;
-				state->pathlen--;
+				LWLINE *line = lwgeom_as_lwline(lwgeom);
+
+				if (state->pt < line->points->npoints - 1)
+				{
+					points = line->points;
+				}
+				else
+				{
+					if (--state->stacklen == 0)
+						SRF_RETURN_DONE(funcctx);
+					state->pathlen--;
+					continue;
+				}
 			}
-			if (state->pt == 0 && state->ring < poly->nrings)
+			if (lwgeom->type == POLYGONTYPE)
 			{
-				state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
-				state->pathlen++;
+				LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
+
+				if (state->pt == poly->rings[state->ring]->npoints)
+				{
+					state->pt = 0;
+					state->ring++;
+					state->pathlen--;
+				}
+				if (state->pt == 0 && state->ring < poly->nrings)
+				{
+					state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
+					state->pathlen++;
+				}
+				if (state->ring == poly->nrings)
+				{
+					if (--state->stacklen == 0)
+						SRF_RETURN_DONE(funcctx);
+					state->pathlen--;
+					continue;
+				}
+				else if (state->pt < poly->rings[state->ring]->npoints - 1)
+				{
+					points = poly->rings[state->ring];
+				}
+				else
+				{
+					state->pt++;
+					continue;
+				}
 			}
-			if (state->ring == poly->nrings)
-			{
-				if (--state->stacklen == 0)
-					SRF_RETURN_DONE(funcctx);
-				state->pathlen--;
-				continue;
-			}
-			else if (state->pt < poly->rings[state->ring]->npoints - 1)
-			{
-				POINTARRAY *points = poly->rings[state->ring];
 
-				POINT4D pt_start, pt_end;
-				getPoint4d_p(points, state->pt, &pt_start);
-				getPoint4d_p(points, state->pt + 1, &pt_end);
+			POINT4D pt_start, pt_end;
+			getPoint4d_p(points, state->pt, &pt_start);
+			getPoint4d_p(points, state->pt + 1, &pt_end);
 
-				POINTARRAY *segment_pa =
-				    ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
-				ptarray_set_point4d(segment_pa, 0, &pt_start);
-				ptarray_set_point4d(segment_pa, 1, &pt_end);
+			POINTARRAY *segment_pa = ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
+			ptarray_set_point4d(segment_pa, 0, &pt_start);
+			ptarray_set_point4d(segment_pa, 1, &pt_end);
 
-				LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
+			LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
 
-				state->pt++;
+			state->pt++;
 
-				state->path[state->pathlen] = Int32GetDatum(state->pt);
-				pathpt[0] = PointerGetDatum(construct_array(state->path,
-									    state->pathlen + 1,
-									    INT4OID,
-									    state->typlen,
-									    state->byval,
-									    state->align));
-				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
+			state->path[state->pathlen] = Int32GetDatum(state->pt);
+			pathpt[0] = PointerGetDatum(construct_array(
+			    state->path, state->pathlen + 1, INT4OID, state->typlen, state->byval, state->align));
+			pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
 
-				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
-				result = HeapTupleGetDatum(tuple);
-				SRF_RETURN_NEXT(funcctx, result);
-			}
-			else
-			{
-				state->pt++;
-				continue;
-			}
+			tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
+			result = HeapTupleGetDatum(tuple);
+			SRF_RETURN_NEXT(funcctx, result);
 		}
 
 		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE || lwgeom->type == MULTIPOLYGONTYPE)

commit aa018f66b4938ba60ec825b3f478327d2be93382
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Mon May 17 23:57:05 2021 +0300

    dumpsegments for polygon

diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
index c8b26b7..daad231 100644
--- a/postgis/lwgeom_dumpsegments.c
+++ b/postgis/lwgeom_dumpsegments.c
@@ -158,11 +158,12 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 				getPoint4d_p(line->points, state->pt, &pt_start);
 				getPoint4d_p(line->points, state->pt + 1, &pt_end);
 
-				POINTARRAY *segment_pa = ptarray_construct(lwgeom_has_z(line), lwgeom_has_m(line), 2);
+				POINTARRAY *segment_pa =
+				    ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
 				ptarray_set_point4d(segment_pa, 0, &pt_start);
 				ptarray_set_point4d(segment_pa, 1, &pt_end);
 
-				LWLINE *segment = lwline_construct(line->srid, NULL, segment_pa);
+				LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
 
 				state->pt++;
 
@@ -187,8 +188,66 @@ Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
 				continue;
 			}
 		}
+		if (lwgeom->type == POLYGONTYPE)
+		{
+			LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
+
+			if (state->pt == poly->rings[state->ring]->npoints)
+			{
+				state->pt = 0;
+				state->ring++;
+				state->pathlen--;
+			}
+			if (state->pt == 0 && state->ring < poly->nrings)
+			{
+				state->path[state->pathlen] = Int32GetDatum(state->ring + 1);
+				state->pathlen++;
+			}
+			if (state->ring == poly->nrings)
+			{
+				if (--state->stacklen == 0)
+					SRF_RETURN_DONE(funcctx);
+				state->pathlen--;
+				continue;
+			}
+			else if (state->pt < poly->rings[state->ring]->npoints - 1)
+			{
+				POINTARRAY *points = poly->rings[state->ring];
+
+				POINT4D pt_start, pt_end;
+				getPoint4d_p(points, state->pt, &pt_start);
+				getPoint4d_p(points, state->pt + 1, &pt_end);
+
+				POINTARRAY *segment_pa =
+				    ptarray_construct(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2);
+				ptarray_set_point4d(segment_pa, 0, &pt_start);
+				ptarray_set_point4d(segment_pa, 1, &pt_end);
+
+				LWLINE *segment = lwline_construct(lwgeom->srid, NULL, segment_pa);
+
+				state->pt++;
+
+				state->path[state->pathlen] = Int32GetDatum(state->pt);
+				pathpt[0] = PointerGetDatum(construct_array(state->path,
+									    state->pathlen + 1,
+									    INT4OID,
+									    state->typlen,
+									    state->byval,
+									    state->align));
+				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
+
+				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
+				result = HeapTupleGetDatum(tuple);
+				SRF_RETURN_NEXT(funcctx, result);
+			}
+			else
+			{
+				state->pt++;
+				continue;
+			}
+		}
 
-		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE)
+		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE || lwgeom->type == MULTIPOLYGONTYPE)
 		{
 			lwcoll = (LWCOLLECTION *)node->geom;
 
diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
index 8aaefd5..21621a1 100644
--- a/regress/core/dumpsegments.sql
+++ b/regress/core/dumpsegments.sql
@@ -1,9 +1,9 @@
 SELECT 'dumpsegments01', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpPoints(g.geom)).*
+  SELECT (ST_DumpSegments(g.geom)).*
   FROM
     (SELECT
-       'LINESTRING (
+       'LINESTRING(
                 0 0,
                 0 9,
                 9 9,
@@ -15,7 +15,7 @@ FROM (
 
 SELECT 'dumpsegments02', path, ST_AsText(geom)
 FROM (
-  SELECT (ST_DumpPoints(g.geom)).*
+  SELECT (ST_DumpSegments(g.geom)).*
   FROM
     (SELECT
        'GEOMETRYCOLLECTION(
@@ -25,8 +25,27 @@ FROM (
     ) AS g
   ) j;
 
-SELECT 'dumpsegments03', ST_DumpPoints('POLYGON EMPTY'::geometry);
-SELECT 'dumpsegments04', ST_DumpPoints('MULTIPOLYGON EMPTY'::geometry);
-SELECT 'dumpsegments05', ST_DumpPoints('MULTILINESTRING EMPTY'::geometry);
-SELECT 'dumpsegments06', ST_DumpPoints('LINESTRING EMPTY'::geometry);
-SELECT 'dumpsegments07', ST_DumpPoints('GEOMETRYCOLLECTION EMPTY'::geometry);
+SELECT 'dumpsegments03', ST_DumpSegments('POLYGON EMPTY'::geometry);
+SELECT 'dumpsegments04', ST_DumpSegments('MULTIPOLYGON EMPTY'::geometry);
+SELECT 'dumpsegments05', ST_DumpSegments('MULTILINESTRING EMPTY'::geometry);
+SELECT 'dumpsegments06', ST_DumpSegments('LINESTRING EMPTY'::geometry);
+SELECT 'dumpsegments07', ST_DumpSegments('GEOMETRYCOLLECTION EMPTY'::geometry);
+
+SELECT 'dumpsegments08', path, ST_AsText(geom)
+FROM (
+  SELECT (ST_DumpSegments(g.geom)).*
+  FROM
+    (SELECT
+       'POLYGON((
+                4 4,
+                5 5,
+                6 6,
+                4 4
+            ), (
+                8 8,
+                9 9,
+                10 10,
+                8 8
+            ))'::geometry AS geom
+    ) AS g
+  ) j;
diff --git a/regress/core/dumpsegments_expected b/regress/core/dumpsegments_expected
index 5971f54..ff06d45 100644
--- a/regress/core/dumpsegments_expected
+++ b/regress/core/dumpsegments_expected
@@ -8,3 +8,9 @@ dumpsegments02|{2,1,2}|LINESTRING(5 5,6 6)
 dumpsegments02|{2,1,3}|LINESTRING(6 6,7 7)
 dumpsegments02|{2,2,1}|LINESTRING(8 8,9 9)
 dumpsegments02|{2,2,2}|LINESTRING(9 9,10 10)
+dumpsegments08|{1,1}|LINESTRING(4 4,5 5)
+dumpsegments08|{1,2}|LINESTRING(5 5,6 6)
+dumpsegments08|{1,3}|LINESTRING(6 6,4 4)
+dumpsegments08|{2,1}|LINESTRING(8 8,9 9)
+dumpsegments08|{2,2}|LINESTRING(9 9,10 10)
+dumpsegments08|{2,3}|LINESTRING(10 10,8 8)

commit 115203f9b0c4286ec6382c6e1bbe5535772caeb5
Author: kalenikaliaksandr <kalenik.aliaksandr at gmail.com>
Date:   Mon May 17 20:18:51 2021 +0300

    dumpsegments

diff --git a/postgis/Makefile.in b/postgis/Makefile.in
index 9e42c2e..a4e0de7 100644
--- a/postgis/Makefile.in
+++ b/postgis/Makefile.in
@@ -93,6 +93,7 @@ PG_OBJS= \
 	lwgeom_triggers.o \
 	lwgeom_dump.o \
 	lwgeom_dumppoints.o \
+	lwgeom_dumpsegments.o \
 	lwgeom_functions_lrs.o \
 	lwgeom_functions_temporal.o \
 	lwgeom_rectree.o \
diff --git a/postgis/lwgeom_dumpsegments.c b/postgis/lwgeom_dumpsegments.c
new file mode 100644
index 0000000..c8b26b7
--- /dev/null
+++ b/postgis/lwgeom_dumpsegments.c
@@ -0,0 +1,220 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.net
+ *
+ * PostGIS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * PostGIS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ **********************************************************************
+ *
+ * ^copyright^
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/array.h"
+#include "utils/geo_decls.h"
+#include "utils/lsyscache.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
+
+#include "../postgis_config.h"
+#include "lwgeom_pg.h"
+
+#include "access/htup_details.h"
+
+#include "liblwgeom.h"
+
+Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS);
+
+struct dumpnode {
+	LWGEOM *geom;
+	uint32_t idx; /* which member geom we're working on */
+};
+
+/* 32 is the max depth for st_dump, so it seems reasonable
+ * to use the same here
+ */
+#define MAXDEPTH 32
+struct dumpstate {
+	LWGEOM *root;
+	int stacklen; /* collections/geoms on stack */
+	int pathlen;  /* polygon rings and such need extra path info */
+	struct dumpnode stack[MAXDEPTH];
+	Datum path[34]; /* two more than max depth, for ring and point */
+
+	/* used to cache the type attributes for integer arrays */
+	int16 typlen;
+	bool byval;
+	char align;
+
+	uint32_t ring; /* ring of top polygon */
+	uint32_t pt;   /* point of top geom or current ring */
+};
+
+PG_FUNCTION_INFO_V1(LWGEOM_dumpsegments);
+Datum LWGEOM_dumpsegments(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	MemoryContext oldcontext, newcontext;
+
+	GSERIALIZED *pglwgeom;
+	LWCOLLECTION *lwcoll;
+	LWGEOM *lwgeom;
+	struct dumpstate *state;
+	struct dumpnode *node;
+
+	HeapTuple tuple;
+	Datum pathpt[2];         /* used to construct the composite return value */
+	bool isnull[2] = {0, 0}; /* needed to say neither value is null */
+	Datum result;            /* the actual composite return value */
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		newcontext = funcctx->multi_call_memory_ctx;
+		oldcontext = MemoryContextSwitchTo(newcontext);
+
+		pglwgeom = PG_GETARG_GSERIALIZED_P_COPY(0);
+		lwgeom = lwgeom_from_gserialized(pglwgeom);
+
+		/* return early if nothing to do */
+		if (!lwgeom || lwgeom_is_empty(lwgeom))
+		{
+			MemoryContextSwitchTo(oldcontext);
+			funcctx = SRF_PERCALL_SETUP();
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		/* Create function state */
+		state = lwalloc(sizeof *state);
+		state->root = lwgeom;
+		state->stacklen = 0;
+		state->pathlen = 0;
+		state->pt = 0;
+		state->ring = 0;
+
+		funcctx->user_fctx = state;
+
+		/*
+		 * Push a struct dumpnode on the state stack
+		 */
+
+		state->stack[0].idx = 0;
+		state->stack[0].geom = lwgeom;
+		state->stacklen++;
+
+		/*
+		 * get tuple description for return type
+		 */
+		if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
+		{
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+		}
+
+		BlessTupleDesc(funcctx->tuple_desc);
+
+		/* get and cache data for constructing int4 arrays */
+		get_typlenbyvalalign(INT4OID, &state->typlen, &state->byval, &state->align);
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	newcontext = funcctx->multi_call_memory_ctx;
+
+	/* get state */
+	state = funcctx->user_fctx;
+
+	while (1)
+	{
+		node = &state->stack[state->stacklen - 1];
+		lwgeom = node->geom;
+
+		if (lwgeom->type == LINETYPE)
+		{
+			LWLINE *line = lwgeom_as_lwline(lwgeom);
+
+			if (state->pt < line->points->npoints - 1)
+			{
+				POINT4D pt_start, pt_end;
+				getPoint4d_p(line->points, state->pt, &pt_start);
+				getPoint4d_p(line->points, state->pt + 1, &pt_end);
+
+				POINTARRAY *segment_pa = ptarray_construct(lwgeom_has_z(line), lwgeom_has_m(line), 2);
+				ptarray_set_point4d(segment_pa, 0, &pt_start);
+				ptarray_set_point4d(segment_pa, 1, &pt_end);
+
+				LWLINE *segment = lwline_construct(line->srid, NULL, segment_pa);
+
+				state->pt++;
+
+				state->path[state->pathlen] = Int32GetDatum(state->pt);
+				pathpt[0] = PointerGetDatum(construct_array(state->path,
+									    state->pathlen + 1,
+									    INT4OID,
+									    state->typlen,
+									    state->byval,
+									    state->align));
+				pathpt[1] = PointerGetDatum(geometry_serialize((LWGEOM *)segment));
+
+				tuple = heap_form_tuple(funcctx->tuple_desc, pathpt, isnull);
+				result = HeapTupleGetDatum(tuple);
+				SRF_RETURN_NEXT(funcctx, result);
+			}
+			else
+			{
+				if (--state->stacklen == 0)
+					SRF_RETURN_DONE(funcctx);
+				state->pathlen--;
+				continue;
+			}
+		}
+
+		if (lwgeom->type == COLLECTIONTYPE || lwgeom->type == MULTILINETYPE)
+		{
+			lwcoll = (LWCOLLECTION *)node->geom;
+
+			/* if a collection and we have more geoms */
+			if (node->idx < lwcoll->ngeoms)
+			{
+				/* push the next geom on the path and the stack */
+				lwgeom = lwcoll->geoms[node->idx++];
+				state->path[state->pathlen++] = Int32GetDatum(node->idx);
+
+				node = &state->stack[state->stacklen++];
+				node->idx = 0;
+				node->geom = lwgeom;
+
+				state->pt = 0;
+				state->ring = 0;
+
+				/* loop back to beginning, which will then check whatever node we just pushed */
+				continue;
+			}
+		}
+
+		/* no more geometries in the current collection */
+		if (--state->stacklen == 0)
+			SRF_RETURN_DONE(funcctx);
+		state->pathlen--;
+		state->stack[state->stacklen - 1].idx++;
+	}
+}
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 1ef5d29..98e2e78 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -1960,6 +1960,12 @@ CREATE OR REPLACE FUNCTION ST_DumpPoints(geometry)
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_HIGH;
 
+CREATE OR REPLACE FUNCTION ST_DumpSegments(geometry)
+	RETURNS SETOF geometry_dump
+	AS 'MODULE_PATHNAME', 'LWGEOM_dumpsegments'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_HIGH;
+
 -------------------------------------------------------------------
 -- SPATIAL_REF_SYS
 -------------------------------------------------------------------
diff --git a/regress/core/dumpsegments.sql b/regress/core/dumpsegments.sql
new file mode 100644
index 0000000..8aaefd5
--- /dev/null
+++ b/regress/core/dumpsegments.sql
@@ -0,0 +1,32 @@
+SELECT 'dumpsegments01', path, ST_AsText(geom)
+FROM (
+  SELECT (ST_DumpPoints(g.geom)).*
+  FROM
+    (SELECT
+       'LINESTRING (
+                0 0,
+                0 9,
+                9 9,
+                9 0,
+                0 0
+            )'::geometry AS geom
+    ) AS g
+  ) j;
+
+SELECT 'dumpsegments02', path, ST_AsText(geom)
+FROM (
+  SELECT (ST_DumpPoints(g.geom)).*
+  FROM
+    (SELECT
+       'GEOMETRYCOLLECTION(
+          LINESTRING(1 1, 3 3),
+          MULTILINESTRING((4 4, 5 5, 6 6, 7 7), (8 8, 9 9, 10 10))
+        )'::geometry AS geom
+    ) AS g
+  ) j;
+
+SELECT 'dumpsegments03', ST_DumpPoints('POLYGON EMPTY'::geometry);
+SELECT 'dumpsegments04', ST_DumpPoints('MULTIPOLYGON EMPTY'::geometry);
+SELECT 'dumpsegments05', ST_DumpPoints('MULTILINESTRING EMPTY'::geometry);
+SELECT 'dumpsegments06', ST_DumpPoints('LINESTRING EMPTY'::geometry);
+SELECT 'dumpsegments07', ST_DumpPoints('GEOMETRYCOLLECTION EMPTY'::geometry);
diff --git a/regress/core/dumpsegments_expected b/regress/core/dumpsegments_expected
new file mode 100644
index 0000000..5971f54
--- /dev/null
+++ b/regress/core/dumpsegments_expected
@@ -0,0 +1,10 @@
+dumpsegments01|{1}|LINESTRING(0 0,0 9)
+dumpsegments01|{2}|LINESTRING(0 9,9 9)
+dumpsegments01|{3}|LINESTRING(9 9,9 0)
+dumpsegments01|{4}|LINESTRING(9 0,0 0)
+dumpsegments02|{1,1}|LINESTRING(1 1,3 3)
+dumpsegments02|{2,1,1}|LINESTRING(4 4,5 5)
+dumpsegments02|{2,1,2}|LINESTRING(5 5,6 6)
+dumpsegments02|{2,1,3}|LINESTRING(6 6,7 7)
+dumpsegments02|{2,2,1}|LINESTRING(8 8,9 9)
+dumpsegments02|{2,2,2}|LINESTRING(9 9,10 10)
diff --git a/regress/core/tests.mk.in b/regress/core/tests.mk.in
index 28e6fe8..0948190 100644
--- a/regress/core/tests.mk.in
+++ b/regress/core/tests.mk.in
@@ -39,6 +39,7 @@ TESTS += \
 	$(topsrcdir)/regress/core/curvetoline \
 	$(topsrcdir)/regress/core/dump \
 	$(topsrcdir)/regress/core/dumppoints \
+	$(topsrcdir)/regress/core/dumpsegments \
 	$(topsrcdir)/regress/core/empty \
 	$(topsrcdir)/regress/core/estimatedextent \
 	$(topsrcdir)/regress/core/forcecurve \
diff --git a/utils/postgis_restore.pl.in b/utils/postgis_restore.pl.in
index 8498c54..93577b9 100644
--- a/utils/postgis_restore.pl.in
+++ b/utils/postgis_restore.pl.in
@@ -2232,6 +2232,7 @@ FUNCTION st_dump(geometry)
 FUNCTION st_dumppoints(geometry)
 FUNCTION _st_dumppoints(geometry,integer[])
 FUNCTION st_dumprings(geometry)
+FUNCTION st_dumpsegments(geometry)
 FUNCTION st_dwithin(geography,geography,double precision)
 FUNCTION _st_dwithin(geography,geography,double precision,boolean)
 FUNCTION st_dwithin(geography,geography,double precision,boolean)

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

Summary of changes:
 NEWS                               |   1 +
 doc/reference_accessor.xml         | 131 ++++++++++++++++++++++
 postgis/lwgeom_dumppoints.c        | 215 +++++++++++++++++++++++++++++++++++++
 postgis/postgis.sql.in             |   7 ++
 regress/core/dumpsegments.sql      |  73 +++++++++++++
 regress/core/dumpsegments_expected |  35 ++++++
 regress/core/tests.mk.in           |   1 +
 utils/postgis_restore.pl.in        |   1 +
 8 files changed, 464 insertions(+)
 create mode 100644 regress/core/dumpsegments.sql
 create mode 100644 regress/core/dumpsegments_expected


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list