[postgis-tickets] [SCM] PostGIS branch master updated. 3.3.0beta1-9-g17b7cda0e

git at osgeo.org git at osgeo.org
Sun Jul 3 13:21:03 PDT 2022


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  17b7cda0e4dfb0fc6f637903f0581e5a7eb854b1 (commit)
       via  7e1f0edb8d551df5493f8c34bfb68e0d58467316 (commit)
       via  7e42fcc4c861a00340b6679e597c8fa9bfb3fa84 (commit)
       via  d47dee43c704a2d82fbc0b659dc2024a4acb23ec (commit)
       via  b8e1233fb88ff9dc81e4b0434fc9634fd822f062 (commit)
       via  9a86d336937ba223c29fb68eb13e3991e4163ae2 (commit)
       via  4d4f45d1471289d0d7c38891843321cdefdf66db (commit)
      from  a887aab94446e935112c645ef94027cf28373056 (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 17b7cda0e4dfb0fc6f637903f0581e5a7eb854b1
Merge: a887aab94 7e1f0edb8
Author: Aliaksandr Kalenik <kalenik.aliaksandr at gmail.com>
Date:   Sun Jul 3 23:20:23 2022 +0300

    Merge remote-tracking branch 'konturio/parallel-union'


commit 7e1f0edb8d551df5493f8c34bfb68e0d58467316
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Wed Jun 22 20:19:35 2022 +0300

    parallel ST_Union: dropping pgis_geometry_union_finalfn

diff --git a/postgis/postgis_after_upgrade.sql b/postgis/postgis_after_upgrade.sql
index a2195c1f1..ae5408c28 100644
--- a/postgis/postgis_after_upgrade.sql
+++ b/postgis/postgis_after_upgrade.sql
@@ -228,6 +228,7 @@ DROP FUNCTION IF EXISTS st_distance_sphere(geometry, geometry);
 
 -- dev function 3.0 cycle
 DROP FUNCTION IF EXISTS pgis_geometry_union_transfn(internal, geometry);
+DROP FUNCTION IF EXISTS pgis_geometry_union_finalfn(internal);
 
 -- #4394
 update pg_operator set oprcanhash = true, oprcanmerge = true where oprname = '=' and oprcode = 'geometry_eq'::regproc;

commit 7e42fcc4c861a00340b6679e597c8fa9bfb3fa84
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Tue Jun 21 15:36:00 2022 +0300

    parallel ST_Union pgis_geometry_union_finalfn function removed

diff --git a/postgis/lwgeom_geos.c b/postgis/lwgeom_geos.c
index 8919e20e5..e481008d3 100644
--- a/postgis/lwgeom_geos.c
+++ b/postgis/lwgeom_geos.c
@@ -107,9 +107,7 @@ Datum ST_DelaunayTriangles(PG_FUNCTION_ARGS);
 Datum ST_MaximumInscribedCircle(PG_FUNCTION_ARGS);
 Datum ST_ConcaveHull(PG_FUNCTION_ARGS);
 Datum ST_SimplifyPolygonHull(PG_FUNCTION_ARGS);
-
 Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
-Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS);
 
 /*
 ** Prototypes end
@@ -637,88 +635,6 @@ Datum pgis_union_geometry_array(PG_FUNCTION_ARGS)
 }
 
 
-PG_FUNCTION_INFO_V1(pgis_geometry_union_finalfn);
-Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS)
-{
-	CollectionBuildState *state;
-	ListCell *l;
-	LWGEOM **geoms;
-	GSERIALIZED *gser_out;
-	size_t ngeoms = 0;
-	int empty_type = 0;
-	bool first = true;
-	int32_t srid = SRID_UNKNOWN;
-	int has_z = LW_FALSE;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL(); /* returns null iff no input values */
-
-	state = (CollectionBuildState *)PG_GETARG_POINTER(0);
-	geoms = palloc(list_length(state->geoms) * sizeof(LWGEOM*));
-
-	/* Read contents of list into an array of only non-null values */
-	foreach (l, state->geoms)
-	{
-		LWGEOM *geom = (LWGEOM*)(lfirst(l));
-		if (geom)
-		{
-			if (!lwgeom_is_empty(geom))
-			{
-				geoms[ngeoms++] = geom;
-				if (first)
-				{
-					srid = lwgeom_get_srid(geom);
-					has_z = lwgeom_has_z(geom);
-					first = false;
-				}
-			}
-			else
-			{
-				int type = lwgeom_get_type(geom);
-				empty_type = type > empty_type ? type : empty_type;
-				srid = (srid != SRID_UNKNOWN ? srid : lwgeom_get_srid(geom));
-			}
-		}
-	}
-
-	/*
-	** Take our array of LWGEOM* and turn it into a GEOS collection,
-	** then pass that into cascaded union.
-	*/
-	if (ngeoms > 0)
-	{
-		LWCOLLECTION* col = lwcollection_construct(COLLECTIONTYPE, srid, NULL, ngeoms, geoms);
-		LWGEOM *out = lwgeom_unaryunion_prec(lwcollection_as_lwgeom(col), state->gridSize);
-		if ( ! out )
-		{
-			lwcollection_free(col);
-		}
-		gser_out = geometry_serialize(out);
-	}
-	/* No real geometries in our array, any empties? */
-	else
-	{
-		/* If it was only empties, we'll return the largest type number */
-		if (empty_type > 0)
-			PG_RETURN_POINTER(
-			    geometry_serialize(lwgeom_construct_empty(empty_type, srid, has_z, 0)));
-
-		/* Nothing but NULL, returns NULL */
-		else
-			PG_RETURN_NULL();
-	}
-
-	if (!gser_out)
-	{
-		/* Union returned a NULL geometry */
-		PG_RETURN_NULL();
-	}
-
-	PG_RETURN_POINTER(gser_out);
-}
-
-
-
 /**
  * @example ST_UnaryUnion {@link #geomunion} SELECT ST_UnaryUnion(
  *      'POLYGON((0 0, 10 0, 0 10, 10 10, 0 0))'
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 60a841b7a..24fd63a4c 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -4089,14 +4089,6 @@ CREATE OR REPLACE FUNCTION pgis_geometry_accum_transfn(internal, geometry, float
 	LANGUAGE 'c' PARALLEL SAFE
 	_COST_LOW;
 
--- Availability: 1.4.0
--- Changed: 2.5.0 use 'internal' transfer type
-CREATE OR REPLACE FUNCTION pgis_geometry_union_finalfn(internal)
-	RETURNS geometry
-	AS 'MODULE_PATHNAME'
-	LANGUAGE 'c' PARALLEL SAFE
-	_COST_HIGH;
-
 -- Availability: 1.4.0
 -- Changed: 2.5.0 use 'internal' transfer type
 CREATE OR REPLACE FUNCTION pgis_geometry_collect_finalfn(internal)
diff --git a/postgis/postgis_legacy.c b/postgis/postgis_legacy.c
index 67c748446..32150f5f0 100644
--- a/postgis/postgis_legacy.c
+++ b/postgis/postgis_legacy.c
@@ -102,3 +102,4 @@ POSTGIS_DEPRECATE("3.1.0", postgis_sfcgal_noop);
 
 POSTGIS_DEPRECATE("3.1.0", LWGEOM_locate_between_m);
 POSTGIS_DEPRECATE("3.1.0", postgis_svn_version);
+POSTGIS_DEPRECATE("3.3.0", pgis_geometry_union_finalfn);

commit d47dee43c704a2d82fbc0b659dc2024a4acb23ec
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Tue Jun 21 14:54:32 2022 +0300

    parallel ST_Union: function cost fix

diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 613cba3a1..60a841b7a 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -4149,7 +4149,7 @@ CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_transfn(internal, geomet
 	RETURNS internal
 	AS 'MODULE_PATHNAME'
 	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
-	_COST_DEFAULT;
+	_COST_LOW;
 
 -- Availability: 3.3.0
 CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_combinefn(internal, internal)
@@ -4170,14 +4170,14 @@ CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_deserialfn(bytea, intern
 	RETURNS internal
 	AS 'MODULE_PATHNAME'
 	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE STRICT
-	_COST_DEFAULT;;
+	_COST_DEFAULT;
 
 -- Availability: 3.3.0
 CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_finalfn(internal)
 	RETURNS geometry
 	AS 'MODULE_PATHNAME'
 	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE STRICT
-	_COST_DEFAULT;;
+	_COST_HIGH;
 
 -- Availability: 1.4.0
 CREATE OR REPLACE FUNCTION ST_Union (geometry[])

commit b8e1233fb88ff9dc81e4b0434fc9634fd822f062
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Fri Jun 17 21:58:08 2022 +0300

    parallel ST_Union: NULL argument checks removed from STRICT functions

diff --git a/postgis/lwgeom_union.c b/postgis/lwgeom_union.c
index 6412cc665..3a0098f65 100644
--- a/postgis/lwgeom_union.c
+++ b/postgis/lwgeom_union.c
@@ -123,17 +123,11 @@ PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_serialfn);
 Datum pgis_geometry_union_parallel_serialfn(PG_FUNCTION_ARGS)
 {
 	UnionState *state;
-	bytea *serialized;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-	state = (UnionState*) PG_GETARG_POINTER(0);
 
 	CheckAggContext();
 
-	serialized = state_serialize(state);
-
-	PG_RETURN_BYTEA_P(serialized);
+	state = (UnionState*) PG_GETARG_POINTER(0);
+	PG_RETURN_BYTEA_P(state_serialize(state));
 }
 
 
@@ -144,11 +138,10 @@ Datum pgis_geometry_union_parallel_deserialfn(PG_FUNCTION_ARGS)
 	UnionState *state;
 	bytea *serialized;
 
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
+	GetAggContext(&aggcontext);
+
 	serialized = PG_GETARG_BYTEA_P(0);
 
-	GetAggContext(&aggcontext);
 	old = MemoryContextSwitchTo(aggcontext);
 	state = state_deserialize(serialized);
 	MemoryContextSwitchTo(old);
@@ -163,14 +156,10 @@ Datum pgis_geometry_union_parallel_finalfn(PG_FUNCTION_ARGS)
 	UnionState *state;
 	LWGEOM *geom = NULL;
 
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL();
-	state = (UnionState*)PG_GETARG_POINTER(0);
-
 	CheckAggContext();
 
+	state = (UnionState*)PG_GETARG_POINTER(0);
 	geom = gserialized_list_union(state->list, state->gridSize);
-
 	if (!geom)
 		PG_RETURN_NULL();
 	PG_RETURN_POINTER(geometry_serialize(geom));

commit 9a86d336937ba223c29fb68eb13e3991e4163ae2
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Fri Jun 17 18:58:27 2022 +0300

    parallel ST_Union: serialfn, deserialfn, finalfn marked STRICT
    
    deserialfn must be STRICT for Postgres=13.1 compatibility

diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 865aaf7c6..613cba3a1 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -4162,21 +4162,21 @@ CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_combinefn(internal, inte
 CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_serialfn(internal)
 	RETURNS bytea
 	AS 'MODULE_PATHNAME'
-	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE STRICT
 	_COST_DEFAULT;
 
 -- Availability: 3.3.0
 CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_deserialfn(bytea, internal)
 	RETURNS internal
 	AS 'MODULE_PATHNAME'
-	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE STRICT
 	_COST_DEFAULT;;
 
 -- Availability: 3.3.0
 CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_finalfn(internal)
 	RETURNS geometry
 	AS 'MODULE_PATHNAME'
-	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE STRICT
 	_COST_DEFAULT;;
 
 -- Availability: 1.4.0
diff --git a/regress/core/union.sql b/regress/core/union.sql
index 5b44afea3..5a19602c8 100644
--- a/regress/core/union.sql
+++ b/regress/core/union.sql
@@ -7,7 +7,7 @@ SELECT ST_Square(1.0, x, y, ST_MakePoint(0, 0))
 FROM coords;
 
 WITH u AS (SELECT ST_Union(geom) AS g FROM geoms)
-SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+SELECT ST_Area(g), ST_XMin((g)), ST_YMin(g), ST_XMax(g), ST_YMax(g) from u;
 
 -- NOTE: using gridSize > 0 requires GEOS-3.9 or higher
 WITH u AS (SELECT ST_Union(geom, -1.0) AS g FROM geoms)
@@ -47,7 +47,7 @@ SELECT ST_Square(1.0, x, y, ST_MakePoint(0, 0))
 FROM coords;
 
 WITH u AS (SELECT ST_Union(geom) AS g FROM geoms)
-SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+SELECT ST_Area(g), ST_XMin((g)), ST_YMin(g), ST_XMax(g), ST_YMax(g) from u;
 
 WITH u AS (SELECT ST_Union(geom, -1.0) AS g FROM geoms)
 SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;

commit 4d4f45d1471289d0d7c38891843321cdefdf66db
Author: sergei sh <sshoulbakov at kontur.io>
Date:   Tue Jun 14 20:11:21 2022 +0300

    parallel ST_Union

diff --git a/postgis/Makefile.in b/postgis/Makefile.in
index 5415a538f..839f17be2 100644
--- a/postgis/Makefile.in
+++ b/postgis/Makefile.in
@@ -76,6 +76,7 @@ SQLPP = @SQLPP@
 PG_OBJS= \
 	postgis_module.o \
 	lwgeom_accum.o \
+	lwgeom_union.o \
 	lwgeom_spheroid.o \
 	lwgeom_ogc.o \
 	lwgeom_functions_analytic.o \
diff --git a/postgis/lwgeom_union.c b/postgis/lwgeom_union.c
new file mode 100644
index 000000000..6412cc665
--- /dev/null
+++ b/postgis/lwgeom_union.c
@@ -0,0 +1,340 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/varlena.h"
+
+#include "../postgis_config.h"
+
+#include "liblwgeom.h"
+#include "lwgeom_log.h"
+#include "lwgeom_pg.h"
+#include "lwgeom_union.h"
+
+#define GetAggContext(aggcontext) \
+	if (!AggCheckCallContext(fcinfo, aggcontext)) \
+		elog(ERROR, "%s called in non-aggregate context", __func__)
+
+#define CheckAggContext() GetAggContext(NULL)
+
+
+Datum pgis_geometry_union_parallel_transfn(PG_FUNCTION_ARGS);
+Datum pgis_geometry_union_parallel_combinefn(PG_FUNCTION_ARGS);
+Datum pgis_geometry_union_parallel_serialfn(PG_FUNCTION_ARGS);
+Datum pgis_geometry_union_parallel_deserialfn(PG_FUNCTION_ARGS);
+Datum pgis_geometry_union_parallel_finalfn(PG_FUNCTION_ARGS);
+
+static UnionState* state_create(void);
+static void state_append(UnionState *state, const GSERIALIZED *gser);
+static bytea* state_serialize(const UnionState *state);
+static UnionState* state_deserialize(const bytea* serialized);
+static void state_combine(UnionState *state1, UnionState *state2);
+
+static LWGEOM* gserialized_list_union(List* list, float8 gridSize);
+
+
+PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_transfn);
+Datum pgis_geometry_union_parallel_transfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggcontext, old;
+	UnionState *state;
+	Datum argType;
+	GSERIALIZED *gser = NULL;
+
+	/* Check argument type */
+	argType = get_fn_expr_argtype(fcinfo->flinfo, 1);
+	if (argType == InvalidOid)
+		ereport(ERROR, (
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("%s: could not determine input data type", __func__)));
+
+	/* Get memory context */
+	GetAggContext(&aggcontext);
+
+	/* Get state */
+	if (PG_ARGISNULL(0)) {
+		old = MemoryContextSwitchTo(aggcontext);
+		state = state_create();
+		MemoryContextSwitchTo(old);
+	}
+	else
+	{
+		state = (UnionState*) PG_GETARG_POINTER(0);
+	}
+
+	/* Get value */
+	if (!PG_ARGISNULL(1))
+		gser = PG_GETARG_GSERIALIZED_P(1);
+
+	/* Get grid size */
+	if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
+	{
+		double gridSize = PG_GETARG_FLOAT8(2);
+		if (gridSize > 0)
+			state->gridSize = gridSize;
+	}
+
+	/* Copy serialized geometry into state */
+	if (gser) {
+		old = MemoryContextSwitchTo(aggcontext);
+		state_append(state, gser);
+		MemoryContextSwitchTo(old);
+	}
+
+	PG_RETURN_POINTER(state);
+}
+
+
+PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_combinefn);
+Datum pgis_geometry_union_parallel_combinefn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggcontext, old;
+	UnionState *state1 = NULL;
+	UnionState *state2 = NULL;
+
+	if (!PG_ARGISNULL(0))
+		state1 = (UnionState*) PG_GETARG_POINTER(0);
+	if (!PG_ARGISNULL(1))
+		state2 = (UnionState*) PG_GETARG_POINTER(1);
+
+	GetAggContext(&aggcontext);
+
+	if (state1 && state2)
+	{
+		old = MemoryContextSwitchTo(aggcontext);
+		state_combine(state1, state2);
+		lwfree(state2);
+		MemoryContextSwitchTo(old);
+	}
+	else if (state2)
+	{
+		state1 = state2;
+	}
+
+	if (!state1)
+		PG_RETURN_NULL();
+	PG_RETURN_POINTER(state1);
+}
+
+
+PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_serialfn);
+Datum pgis_geometry_union_parallel_serialfn(PG_FUNCTION_ARGS)
+{
+	UnionState *state;
+	bytea *serialized;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+	state = (UnionState*) PG_GETARG_POINTER(0);
+
+	CheckAggContext();
+
+	serialized = state_serialize(state);
+
+	PG_RETURN_BYTEA_P(serialized);
+}
+
+
+PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_deserialfn);
+Datum pgis_geometry_union_parallel_deserialfn(PG_FUNCTION_ARGS)
+{
+	MemoryContext aggcontext, old;
+	UnionState *state;
+	bytea *serialized;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+	serialized = PG_GETARG_BYTEA_P(0);
+
+	GetAggContext(&aggcontext);
+	old = MemoryContextSwitchTo(aggcontext);
+	state = state_deserialize(serialized);
+	MemoryContextSwitchTo(old);
+
+	PG_RETURN_POINTER(state);
+}
+
+
+PG_FUNCTION_INFO_V1(pgis_geometry_union_parallel_finalfn);
+Datum pgis_geometry_union_parallel_finalfn(PG_FUNCTION_ARGS)
+{
+	UnionState *state;
+	LWGEOM *geom = NULL;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+	state = (UnionState*)PG_GETARG_POINTER(0);
+
+	CheckAggContext();
+
+	geom = gserialized_list_union(state->list, state->gridSize);
+
+	if (!geom)
+		PG_RETURN_NULL();
+	PG_RETURN_POINTER(geometry_serialize(geom));
+}
+
+
+UnionState* state_create(void)
+{
+	UnionState *state = lwalloc(sizeof(UnionState));
+	state->gridSize = -1.0;
+	state->list = NIL;
+	state->size = 0;
+	return state;
+}
+
+
+void state_append(UnionState *state, const GSERIALIZED *gser)
+{
+	GSERIALIZED *gser_copy;
+
+	assert(gser);
+	gser_copy = lwalloc(VARSIZE(gser));
+	memcpy(gser_copy, gser, VARSIZE(gser));
+
+	state->list = lappend(state->list, gser_copy);
+	state->size += VARSIZE(gser);
+}
+
+
+bytea* state_serialize(const UnionState *state)
+{
+	int32 size = VARHDRSZ + sizeof(state->gridSize) + state->size;
+	bytea *serialized = lwalloc(size);
+	uint8 *data;
+	ListCell *cell;
+
+	SET_VARSIZE(serialized, size);
+	data = (uint8*)VARDATA(serialized);
+
+	/* grid size */
+	memcpy(data, &state->gridSize, sizeof(state->gridSize));
+	data += sizeof(state->gridSize);
+
+	/* items */
+	foreach (cell, state->list)
+	{
+		const GSERIALIZED *gser = (const GSERIALIZED*)lfirst(cell);
+		assert(gser);
+		memcpy(data, gser, VARSIZE(gser));
+		data += VARSIZE(gser);
+	}
+
+	return serialized;
+}
+
+
+UnionState* state_deserialize(const bytea* serialized)
+{
+	UnionState *state = state_create();
+	const uint8 *data = (const uint8*)VARDATA(serialized);
+	const uint8 *data_end = (const uint8*)serialized + VARSIZE(serialized);
+
+	/* grid size */
+	memcpy(&state->gridSize, data, sizeof(state->gridSize));
+	data += sizeof(state->gridSize);
+
+	/* items */
+	while (data < data_end)
+	{
+		const GSERIALIZED* gser = (const GSERIALIZED*)data;
+		state_append(state, gser);
+		data += VARSIZE(gser);
+	}
+
+	return state;
+}
+
+
+void state_combine(UnionState *state1, UnionState *state2)
+{
+	List *list1;
+	List *list2;
+
+	assert(state1 && state2);
+
+	list1 = state1->list;
+	list2 = state2->list;
+
+	if (list1 != NIL && list2 != NIL)
+	{
+		state1->list = list_concat(list1, list2);
+		list_free(list2);
+		state1->size += state2->size;
+	}
+	else if (list2 != NIL)
+	{
+		state1->gridSize = state2->gridSize;
+		state1->list = list2;
+		state1->size = state2->size;
+	}
+	state2->list = NIL;
+}
+
+
+LWGEOM* gserialized_list_union(List* list, float8 gridSize)
+{
+	int ngeoms = 0;
+	LWGEOM **geoms;
+	int32_t srid = SRID_UNKNOWN;
+	bool first = true;
+	int empty_type = 0;
+	int has_z = LW_FALSE;
+	ListCell *cell;
+
+	if (list_length(list) == 0)
+		return NULL;
+
+	geoms = lwalloc(list_length(list) * sizeof(LWGEOM*));
+	foreach (cell, list)
+	{
+		GSERIALIZED *gser;
+		LWGEOM *geom;
+
+		gser = (GSERIALIZED*)lfirst(cell);
+		assert(gser);
+		geom = lwgeom_from_gserialized(gser);
+
+		if (!lwgeom_is_empty(geom))
+		{
+			geoms[ngeoms++] = geom; /* no cloning */
+			if (first)
+			{
+				srid = lwgeom_get_srid(geom);
+				has_z = lwgeom_has_z(geom);
+				first = false;
+			}
+		}
+		else
+		{
+			int type = lwgeom_get_type(geom);
+			if (type > empty_type)
+				empty_type = type;
+			if (srid == SRID_UNKNOWN)
+				srid = lwgeom_get_srid(geom);
+		}
+	}
+
+	if (ngeoms > 0)
+	{
+		/*
+		 * Create a collection and pass it into cascaded union
+		 */
+		LWCOLLECTION *col = lwcollection_construct(COLLECTIONTYPE, srid, NULL, ngeoms, geoms);
+		LWGEOM *result = lwgeom_unaryunion_prec(lwcollection_as_lwgeom(col), gridSize);
+		if (!result)
+			lwcollection_free(col);
+		return result;
+	}
+
+	/*
+	 * Only empty geometries in the list,
+	 * create geometry with largest type number or return NULL
+	 */
+	return (empty_type > 0)
+		? lwgeom_construct_empty(empty_type, srid, has_z, 0)
+		: NULL;
+}
diff --git a/postgis/lwgeom_union.h b/postgis/lwgeom_union.h
new file mode 100644
index 000000000..4c48d4cf9
--- /dev/null
+++ b/postgis/lwgeom_union.h
@@ -0,0 +1,14 @@
+#ifndef _LWGEOM_UNION_H
+#define _LWGEOM_UNION_H 1
+
+#include "postgres.h"
+#include "liblwgeom.h"
+
+typedef struct UnionState
+{
+	float8 gridSize; /* gridSize argument */
+	List *list; /* list of GSERIALIZED* */
+	int32 size; /* total size of GSERIAZLIZED values in list in bytes */
+} UnionState;
+
+#endif /* _LWGEOM_UNION_H */
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 49cfcdbdd..865aaf7c6 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -4137,6 +4137,48 @@ CREATE OR REPLACE FUNCTION pgis_geometry_makeline_finalfn(internal)
 	LANGUAGE 'c' PARALLEL SAFE
 	_COST_MEDIUM;
 
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_transfn(internal, geometry)
+	RETURNS internal
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;
+
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_transfn(internal, geometry, float8)
+	RETURNS internal
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;
+
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_combinefn(internal, internal)
+	RETURNS internal
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;
+
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_serialfn(internal)
+	RETURNS bytea
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;
+
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_deserialfn(bytea, internal)
+	RETURNS internal
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;;
+
+-- Availability: 3.3.0
+CREATE OR REPLACE FUNCTION pgis_geometry_union_parallel_finalfn(internal)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME'
+	LANGUAGE 'c' IMMUTABLE PARALLEL SAFE
+	_COST_DEFAULT;;
+
 -- Availability: 1.4.0
 CREATE OR REPLACE FUNCTION ST_Union (geometry[])
 	RETURNS geometry
@@ -4149,22 +4191,29 @@ CREATE OR REPLACE FUNCTION ST_Union (geometry[])
 -- we don't want to force drop of this agg since its often used in views
 -- parallel handling dealt with in postgis_after_upgrade.sql
 -- Changed: 2.5.0 use 'internal' stype
-CREATE AGGREGATE ST_Union (geometry) (
-	sfunc = pgis_geometry_accum_transfn,
+-- Changed: 3.3.0 parallel scan support
+CREATE AGGREGATE ST_Union(geometry) (
+	sfunc = pgis_geometry_union_parallel_transfn,
 	stype = internal,
 	parallel = safe,
-	finalfunc = pgis_geometry_union_finalfn
-	);
+	serialfunc = pgis_geometry_union_parallel_serialfn,
+	deserialfunc = pgis_geometry_union_parallel_deserialfn,
+	combinefunc = pgis_geometry_union_parallel_combinefn,
+	finalfunc = pgis_geometry_union_parallel_finalfn
+);
 
 -- Availability: 3.1.0
+-- Changed: 3.3.0 parallel scan support
 CREATE AGGREGATE ST_Union (geometry, gridSize float8) (
-	sfunc = pgis_geometry_accum_transfn,
+	sfunc = pgis_geometry_union_parallel_transfn,
 	stype = internal,
 	parallel = safe,
-	finalfunc = pgis_geometry_union_finalfn
+	serialfunc = pgis_geometry_union_parallel_serialfn,
+	deserialfunc = pgis_geometry_union_parallel_deserialfn,
+	combinefunc = pgis_geometry_union_parallel_combinefn,
+	finalfunc = pgis_geometry_union_parallel_finalfn
 	);
 
-
 -- Availability: 1.2.2
 -- Changed: 2.4.0: marked parallel safe
 -- Changed: 2.5.0 use 'internal' stype
diff --git a/regress/core/tests.mk.in b/regress/core/tests.mk.in
index bf1fe5643..70366505a 100644
--- a/regress/core/tests.mk.in
+++ b/regress/core/tests.mk.in
@@ -122,6 +122,7 @@ TESTS += \
 	$(top_srcdir)/regress/core/snap \
 	$(top_srcdir)/regress/core/node \
 	$(top_srcdir)/regress/core/unaryunion \
+	$(top_srcdir)/regress/core/union \
 	$(top_srcdir)/regress/core/clean \
 	$(top_srcdir)/regress/core/relate_bnr \
 	$(top_srcdir)/regress/core/delaunaytriangles \
diff --git a/regress/core/union.sql b/regress/core/union.sql
new file mode 100644
index 000000000..5b44afea3
--- /dev/null
+++ b/regress/core/union.sql
@@ -0,0 +1,69 @@
+CREATE TABLE geoms (geom geometry);
+
+-- Create 20x20 square polygons
+WITH coords AS (SELECT * FROM generate_series(0, 19) AS x, generate_series(0, 19) AS y)
+INSERT INTO geoms
+SELECT ST_Square(1.0, x, y, ST_MakePoint(0, 0))
+FROM coords;
+
+WITH u AS (SELECT ST_Union(geom) AS g FROM geoms)
+SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+
+-- NOTE: using gridSize > 0 requires GEOS-3.9 or higher
+WITH u AS (SELECT ST_Union(geom, -1.0) AS g FROM geoms)
+SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+
+TRUNCATE TABLE geoms;
+
+-- Empty table
+SELECT ST_Union(geom) IS NULL FROM geoms;
+
+-- NULLs
+INSERT INTO geoms (geom) VALUES (NULL), (NULL), (NULL), (NULL), (NULL);
+SELECT ST_Union(geom) IS NULL FROM geoms;
+
+-- Add empty point
+INSERT INTO geoms SELECT ST_GeomFromText('POINT EMPTY') AS geom;
+SELECT ST_AsText(ST_Union(geom)) FROM geoms;
+
+-- Add empty polygon
+INSERT INTO geoms SELECT ST_GeomFromText('POLYGON EMPTY') AS geom;
+SELECT ST_AsText(ST_Union(geom)) FROM geoms;
+
+
+-- Test in parallel mode
+
+TRUNCATE TABLE geoms;
+
+SET force_parallel_mode = on;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers_per_gather = 4;
+
+WITH coords AS (SELECT * FROM generate_series(0, 19) AS x, generate_series(0, 19) AS y)
+INSERT INTO geoms
+SELECT ST_Square(1.0, x, y, ST_MakePoint(0, 0))
+FROM coords;
+
+WITH u AS (SELECT ST_Union(geom) AS g FROM geoms)
+SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+
+WITH u AS (SELECT ST_Union(geom, -1.0) AS g FROM geoms)
+SELECT ST_Area(g), ST_XMin((g)), sT_YMin(g), ST_XMax(g), ST_YMax(g) from u;
+
+TRUNCATE TABLE geoms;
+
+SELECT ST_Union(geom) IS NULL FROM geoms;
+
+INSERT INTO geoms (geom) VALUES (NULL), (NULL), (NULL), (NULL), (NULL);
+SELECT ST_Union(geom) IS NULL FROM geoms;
+
+INSERT INTO geoms SELECT ST_GeomFromText('POINT EMPTY') AS geom;
+SELECT ST_AsText(ST_Union(geom)) FROM geoms;
+
+INSERT INTO geoms SELECT ST_GeomFromText('POLYGON EMPTY') AS geom;
+SELECT ST_AsText(ST_Union(geom)) FROM geoms;
+
+
+DROP TABLE geoms;
diff --git a/regress/core/union_expected b/regress/core/union_expected
new file mode 100644
index 000000000..9404fcc16
--- /dev/null
+++ b/regress/core/union_expected
@@ -0,0 +1,12 @@
+400|0|0|20|20
+400|0|0|20|20
+t
+t
+POINT EMPTY
+POLYGON EMPTY
+400|0|0|20|20
+400|0|0|20|20
+t
+t
+POINT EMPTY
+POLYGON EMPTY

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

Summary of changes:
 postgis/Makefile.in               |   1 +
 postgis/lwgeom_geos.c             |  84 ----------
 postgis/lwgeom_union.c            | 329 ++++++++++++++++++++++++++++++++++++++
 postgis/lwgeom_union.h            |  14 ++
 postgis/postgis.sql.in            |  71 ++++++--
 postgis/postgis_after_upgrade.sql |   1 +
 postgis/postgis_legacy.c          |   1 +
 regress/core/tests.mk.in          |   1 +
 regress/core/union.sql            |  69 ++++++++
 regress/core/union_expected       |  12 ++
 10 files changed, 484 insertions(+), 99 deletions(-)
 create mode 100644 postgis/lwgeom_union.c
 create mode 100644 postgis/lwgeom_union.h
 create mode 100644 regress/core/union.sql
 create mode 100644 regress/core/union_expected


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list