[postgis-tickets] r17856 - Restore ST_Union() aggregate signature and re-work

Paul Ramsey pramsey at cleverelephant.ca
Fri Oct 4 11:25:46 PDT 2019


Author: pramsey
Date: 2019-10-04 11:25:46 -0700 (Fri, 04 Oct 2019)
New Revision: 17856

Added:
   trunk/postgis/lwgeom_accum.h
Modified:
   trunk/postgis/lwgeom_accum.c
   trunk/postgis/lwgeom_geos.c
   trunk/postgis/postgis.sql.in
   trunk/postgis/postgis_after_upgrade.sql
   trunk/postgis/postgis_before_upgrade.sql
   trunk/regress/run_test.pl
Log:
Restore ST_Union() aggregate signature and re-work
performance/size enhancement to continue to avoid
using Array type during ST_Union(), hopefully
avoiding Array size limitations.


Modified: trunk/postgis/lwgeom_accum.c
===================================================================
--- trunk/postgis/lwgeom_accum.c	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/postgis/lwgeom_accum.c	2019-10-04 18:25:46 UTC (rev 17856)
@@ -37,12 +37,12 @@
 #include "lwgeom_geos.h"
 #include "lwgeom_pg.h"
 #include "lwgeom_transform.h"
+#include "lwgeom_accum.h"
 
 /* Local prototypes */
 Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1);
 Datum PGISDirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2);
 Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS);
-Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS);
 Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS);
 Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS);
 Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS);
@@ -58,49 +58,22 @@
 Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS);
 
 
-/** @file
-** Versions of PostgreSQL < 8.4 perform array accumulation internally using
-** pass by value, which is very slow working with large/many geometries.
-** Hence PostGIS currently implements its own aggregate for building
-** geometry arrays using pass by reference, which is significantly faster and
-** similar to the method used in PostgreSQL 8.4.
-**
-** Hence we can revert this to the original aggregate functions from 1.3 at
-** whatever point PostgreSQL 8.4 becomes the minimum version we support :)
-*/
-
-
 /**
-** To pass the internal ArrayBuildState pointer between the
-** transfn and finalfn we need to wrap it into a custom type first,
-** the pgis_abs type in our case.  The extra "data" member can optionally
-** be used to pass an additional constant argument to a finalizer function.
+** The transfer function builds a List of LWGEOM* allocated
+** in the aggregate memory context. The pgis_accum_finalfn
+** converts that List into a Pg Array.
 */
-
-typedef struct
-{
-	ArrayBuildState *a;
-	Datum data;
-}
-pgis_abs;
-
-
-/**
-** The transfer function hooks into the PostgreSQL accumArrayResult()
-** function (present since 8.0) to build an array in a side memory
-** context.
-*/
 PG_FUNCTION_INFO_V1(pgis_geometry_accum_transfn);
 Datum
 pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
 {
-	Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
-	MemoryContext aggcontext;
-	ArrayBuildState *state;
-	pgis_abs *p;
-	Datum elem;
+	MemoryContext aggcontext, old;
+	CollectionBuildState *state;
+	LWGEOM *geom = NULL;
+	GSERIALIZED *gser = NULL;
+	Datum argType = get_fn_expr_argtype(fcinfo->flinfo, 1);
 
-	if (arg1_typeid == InvalidOid)
+	if (argType == InvalidOid)
 		ereport(ERROR,
 		        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 		         errmsg("could not determine input data type")));
@@ -114,52 +87,64 @@
 
 	if ( PG_ARGISNULL(0) )
 	{
-		MemoryContext old = MemoryContextSwitchTo(aggcontext);
-		p = (pgis_abs*) palloc(sizeof(pgis_abs));
-		p->a = NULL;
-		p->data = (Datum) NULL;
+		int n = ((PG_NARGS()-2) <= CollectionBuildStateDataSize) ? (PG_NARGS()-2) : CollectionBuildStateDataSize;
 
-		if (PG_NARGS() == 3)
+		state = MemoryContextAlloc(aggcontext, sizeof(CollectionBuildState));
+		state->geoms = NULL;
+		state->geomOid = argType;
+
+		for (int i = 0; i < n; i++)
 		{
-			Datum argument = PG_GETARG_DATUM(2);
-			Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, 2);
-
-			p->data = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
-
+			Datum argument = PG_GETARG_DATUM(i+2);
+			Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, i+2);
+			state->data[i] = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
 		}
-		MemoryContextSwitchTo(old);
 	}
 	else
 	{
-		p = (pgis_abs*) PG_GETARG_POINTER(0);
+		state = (CollectionBuildState*) PG_GETARG_POINTER(0);
 	}
-	state = p->a;
-	elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
-	state = accumArrayResult(state,
-	                         elem,
-	                         PG_ARGISNULL(1),
-	                         arg1_typeid,
-	                         aggcontext);
-	p->a = state;
 
-	PG_RETURN_POINTER(p);
+	if (!PG_ARGISNULL(1))
+		gser = PG_GETARG_GSERIALIZED_P(1);
+
+	/* Take a copy of the geometry into the aggregate context */
+	old = MemoryContextSwitchTo(aggcontext);
+	if (gser)
+		geom = lwgeom_clone_deep(lwgeom_from_gserialized(gser));
+
+	/* Initialize or append to list as necessary */
+	if (state->geoms)
+		state->geoms = lappend(state->geoms, geom);
+	else
+		state->geoms = list_make1(geom);
+
+	MemoryContextSwitchTo(old);
+
+	PG_RETURN_POINTER(state);
 }
 
 
+Datum pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, FunctionCallInfo fcinfo);
 
-Datum pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, FunctionCallInfo fcinfo);
-
 /**
-** The final function rescues the built array from the side memory context
-** using the PostgreSQL built-in function makeMdArrayResult
+** The final function reads the List of LWGEOM* from the aggregate
+** memory context and constructs an Array using construct_md_array()
 */
 Datum
-pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, __attribute__((__unused__)) FunctionCallInfo fcinfo)
+pgis_accum_finalfn(CollectionBuildState *state, MemoryContext mctx, __attribute__((__unused__)) FunctionCallInfo fcinfo)
 {
+	ListCell *l;
+	size_t nelems = 0;
+	Datum *elems;
+	bool *nulls;
+	int16 elmlen;
+	bool elmbyval;
+	char elmalign;
+	size_t i = 0;
+	ArrayType *arr;
 	int dims[1];
-	int lbs[1];
-	ArrayBuildState *state;
-	Datum result;
+	int lbs[1] = {1};
 
 	/* cannot be called directly because of internal-type argument */
 	Assert(fcinfo->context &&
@@ -167,11 +152,40 @@
 	        IsA(fcinfo->context, WindowAggState))
 	       );
 
-	state = p->a;
-	dims[0] = state->nelems;
-	lbs[0] = 1;
-	result = makeMdArrayResult(state, 1, dims, lbs, mctx, false);
-	return result;
+	/* Retrieve geometry type metadata */
+	get_typlenbyvalalign(state->geomOid, &elmlen, &elmbyval, &elmalign);
+	nelems = list_length(state->geoms);
+
+	/* Build up an array, because that's what we pass to all the */
+	/* specific final functions */
+	elems = palloc(nelems * sizeof(Datum));
+	nulls = palloc(nelems * sizeof(bool));
+
+	foreach (l, state->geoms)
+	{
+		LWGEOM *geom = (LWGEOM*)(lfirst(l));
+		Datum elem = (Datum)0;
+		bool isNull = true;
+		if (geom)
+		{
+			GSERIALIZED *gser = geometry_serialize(geom);
+			elem = PointerGetDatum(gser);
+			isNull = false;
+		}
+		elems[i] = elem;
+		nulls[i] = isNull;
+		i++;
+
+		if (i >= nelems)
+			break;
+	}
+
+	/* Turn element array into PgSQL array */
+	dims[0] = nelems;
+	arr = construct_md_array(elems, nulls, 1, dims, lbs, state->geomOid,
+	                         elmlen, elmbyval, elmalign);
+
+	return PointerGetDatum(arr);
 }
 
 /**
@@ -182,7 +196,7 @@
 Datum
 pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS)
 {
-	pgis_abs *p;
+	CollectionBuildState *p;
 	Datum result = 0;
 	Datum geometry_array = 0;
 
@@ -189,7 +203,7 @@
 	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();   /* returns null iff no input values */
 
-	p = (pgis_abs*) PG_GETARG_POINTER(0);
+	p = (CollectionBuildState*) PG_GETARG_POINTER(0);
 
 	geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
 	result = PGISDirectFunctionCall1( LWGEOM_collect_garray, geometry_array );
@@ -208,7 +222,7 @@
 Datum
 pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
 {
-	pgis_abs *p;
+	CollectionBuildState *p;
 	Datum result = 0;
 	Datum geometry_array = 0;
 
@@ -215,7 +229,7 @@
 	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();   /* returns null iff no input values */
 
-	p = (pgis_abs*) PG_GETARG_POINTER(0);
+	p = (CollectionBuildState*) PG_GETARG_POINTER(0);
 
 	geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
 	result = PGISDirectFunctionCall1( polygonize_garray, geometry_array );
@@ -233,7 +247,7 @@
 Datum
 pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS)
 {
-	pgis_abs *p;
+	CollectionBuildState *p;
 	Datum result = 0;
 	Datum geometry_array = 0;
 
@@ -240,7 +254,7 @@
 	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();   /* returns null iff no input values */
 
-	p = (pgis_abs*) PG_GETARG_POINTER(0);
+	p = (CollectionBuildState*) PG_GETARG_POINTER(0);
 
 	geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
 	result = PGISDirectFunctionCall1( LWGEOM_makeline_garray, geometry_array );
@@ -258,7 +272,7 @@
 Datum
 pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
 {
-	pgis_abs *p;
+	CollectionBuildState *p;
 	Datum result = 0;
 	Datum geometry_array = 0;
 
@@ -265,7 +279,7 @@
 	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();
 
-	p = (pgis_abs*) PG_GETARG_POINTER(0);
+	p = (CollectionBuildState*) PG_GETARG_POINTER(0);
 	geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
 	result = PGISDirectFunctionCall1( clusterintersecting_garray, geometry_array );
 	if (!result)
@@ -282,7 +296,7 @@
 Datum
 pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
 {
-	pgis_abs *p;
+	CollectionBuildState *p;
 	Datum result = 0;
 	Datum geometry_array = 0;
 
@@ -289,9 +303,9 @@
 	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();
 
-	p = (pgis_abs*) PG_GETARG_POINTER(0);
+	p = (CollectionBuildState*) PG_GETARG_POINTER(0);
 
-	if (!p->data)
+	if (!p->data[0])
 	{
 		elog(ERROR, "Tolerance not defined");
 		PG_RETURN_NULL();
@@ -298,7 +312,7 @@
 	}
 
 	geometry_array = pgis_accum_finalfn(p, CurrentMemoryContext, fcinfo);
-	result = PGISDirectFunctionCall2( cluster_within_distance_garray, geometry_array, p->data);
+	result = PGISDirectFunctionCall2( cluster_within_distance_garray, geometry_array, p->data[0]);
 	if (!result)
 		PG_RETURN_NULL();
 

Added: trunk/postgis/lwgeom_accum.h
===================================================================
--- trunk/postgis/lwgeom_accum.h	                        (rev 0)
+++ trunk/postgis/lwgeom_accum.h	2019-10-04 18:25:46 UTC (rev 17856)
@@ -0,0 +1,44 @@
+/**********************************************************************
+ *
+ * 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 (c) 2019, Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ **********************************************************************/
+
+#ifndef _LWGEOM_ACCUM_H
+#define _LWGEOM_ACCUM_H 1
+
+/**
+** To pass the internal state of our collection between the
+** transfn and finalfn we need to wrap it into a custom type first,
+** the CollectionBuildState type in our case.  The extra "data" member
+** can optionally be used to pass additional constant
+** arguments to a finalizer function.
+*/
+#define CollectionBuildStateDataSize 2
+typedef struct CollectionBuildState
+{
+	List *geoms;  /* collected geometries */
+	Datum data[CollectionBuildStateDataSize];
+	Oid geomOid;
+} CollectionBuildState;
+
+
+#endif /* _LWGEOM_ACCUM_H */

Modified: trunk/postgis/lwgeom_geos.c
===================================================================
--- trunk/postgis/lwgeom_geos.c	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/postgis/lwgeom_geos.c	2019-10-04 18:25:46 UTC (rev 17856)
@@ -27,6 +27,8 @@
 
 #include "../postgis_config.h"
 
+#include "float.h" /* for DBL_DIG */
+
 /* PostgreSQL */
 #include "postgres.h"
 #include "funcapi.h"
@@ -34,10 +36,8 @@
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/numeric.h"
-
 #include "access/htup_details.h"
 
-
 /* PostGIS */
 #include "lwgeom_functions_analytic.h" /* for point_in_polygon */
 #include "lwgeom_geos.h"
@@ -44,7 +44,7 @@
 #include "liblwgeom.h"
 #include "lwgeom_rtree.h"
 #include "lwgeom_geos_prepared.h"
-#include "float.h" /* for DBL_DIG */
+#include "lwgeom_accum.h"
 
 
 /* Return NULL on GEOS error
@@ -107,6 +107,7 @@
 Datum ST_DelaunayTriangles(PG_FUNCTION_ARGS);
 
 Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
+Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS);
 
 /*
 ** Prototypes end
@@ -505,135 +506,62 @@
 	PG_RETURN_POINTER(gser_out);
 }
 
-typedef struct UnionBuildState
-{
-	MemoryContext mcontext; /* where all the temp stuff is kept */
-	GEOSGeometry **geoms;   /* collected GEOS geometries*/
-	int empty_type;
-	uint32_t alen;   /* allocated length of above arrays */
-	uint32_t ngeoms; /* number of valid entries in above arrays */
-	int32_t srid;
-	bool is3d;
-} UnionBuildState;
 
-PG_FUNCTION_INFO_V1(pgis_geometry_union_transfn);
-Datum pgis_geometry_union_transfn(PG_FUNCTION_ARGS)
+PG_FUNCTION_INFO_V1(pgis_geometry_union_finalfn);
+Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS)
 {
-	MemoryContext aggcontext;
-	MemoryContext old;
-	UnionBuildState *state;
-	GSERIALIZED *gser_in;
-	uint32_t curgeom;
-	GEOSGeometry *g;
+	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 (!AggCheckCallContext(fcinfo, &aggcontext))
-	{
-		/* cannot be called directly because of dummy-type argument */
-		elog(ERROR, "%s called in non-aggregate context", __func__);
-		aggcontext = NULL; /* keep compiler quiet */
-	}
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL(); /* returns null iff no input values */
 
-	if (!PG_ARGISNULL(0))
-	{
-		state = (UnionBuildState *)PG_GETARG_POINTER(0);
-	}
-	else
-	{
-		old = MemoryContextSwitchTo(aggcontext);
-		state = (UnionBuildState *)palloc(sizeof(UnionBuildState));
+	state = (CollectionBuildState *)PG_GETARG_POINTER(0);
+	geoms = palloc(list_length(state->geoms) * sizeof(LWGEOM*));
 
-		state->mcontext = aggcontext;
-		state->alen = 10;
-		state->ngeoms = 0;
-		state->geoms = palloc(sizeof(GEOSGeometry *) * state->alen);
-		state->is3d = false;
-		state->srid = 0;
-		state->empty_type = 0;
-
-		initGEOS(lwpgnotice, lwgeom_geos_error);
-
-		MemoryContextSwitchTo(old);
-	};
-
-	/* do we have geometry to push? */
-	if (!PG_ARGISNULL(1))
+	/* Read contents of list into an array of only non-null values */
+	foreach (l, state->geoms)
 	{
-		old = MemoryContextSwitchTo(state->mcontext);
-		gser_in = PG_GETARG_GSERIALIZED_P_COPY(1);
-		MemoryContextSwitchTo(old);
-
-		if (state->ngeoms > 0)
+		LWGEOM *geom = (LWGEOM*)(lfirst(l));
+		if (geom)
 		{
-			if (state->srid != gserialized_get_srid(gser_in))
-				for (curgeom = 0; curgeom < state->ngeoms; curgeom++)
-					GEOSGeom_destroy(state->geoms[curgeom]);
-
-			gserialized_error_if_srid_mismatch_reference(gser_in, state->srid, __func__);
-		}
-
-		if (!gserialized_is_empty(gser_in))
-		{
-			if (state->ngeoms == 0)
+			if (!lwgeom_is_empty(geom))
 			{
-				state->srid = gserialized_get_srid(gser_in);
-				state->is3d = gserialized_has_z(gser_in);
+				geoms[ngeoms++] = geom;
+				if (first)
+				{
+					srid = lwgeom_get_srid(geom);
+					has_z = lwgeom_has_z(geom);
+					first = false;
+				}
 			}
-
-			old = MemoryContextSwitchTo(state->mcontext);
-			g = POSTGIS2GEOS(gser_in);
-			MemoryContextSwitchTo(old);
-
-			if (!g)
+			else
 			{
-				for (curgeom = 0; curgeom < state->ngeoms; curgeom++)
-					GEOSGeom_destroy(state->geoms[curgeom]);
-				HANDLE_GEOS_ERROR("One of the geometries in the set could not be converted to GEOS");
+				int type = lwgeom_get_type(geom);
+				empty_type = type > empty_type ? type : empty_type;
 			}
-
-			curgeom = state->ngeoms;
-			state->ngeoms++;
-
-			if (state->ngeoms > state->alen)
-			{
-				old = MemoryContextSwitchTo(state->mcontext);
-				state->alen *= 2;
-				state->geoms = repalloc(state->geoms, sizeof(GEOSGeometry *) * state->alen);
-				MemoryContextSwitchTo(old);
-			}
-
-			state->geoms[curgeom] = g;
 		}
-		else
-		{
-			int gser_type = gserialized_get_type(gser_in);
-			if (gser_type > state->empty_type)
-				state->empty_type = gser_type;
-		}
 	}
 
-	PG_RETURN_POINTER(state);
-}
-
-PG_FUNCTION_INFO_V1(pgis_geometry_union_finalfn);
-Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS)
-{
-	UnionBuildState *state;
-	GSERIALIZED *gser_out = NULL;
-	GEOSGeometry *g = NULL;
-	GEOSGeometry *g_union = NULL;
-
-	if (PG_ARGISNULL(0))
-		PG_RETURN_NULL(); /* returns null iff no input values */
-
-	state = (UnionBuildState *)PG_GETARG_POINTER(0);
-
 	/*
-	** Take our GEOS geometries and turn them into a GEOS collection,
+	** Take our array of LWGEOM* and turn it into a GEOS collection,
 	** then pass that into cascaded union.
 	*/
-	if (state->ngeoms > 0)
+	if (ngeoms > 0)
 	{
-		g = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, state->geoms, state->ngeoms);
+		GEOSGeometry *g = NULL;
+		GEOSGeometry *g_union = NULL;
+		LWCOLLECTION* col = lwcollection_construct(COLLECTIONTYPE, srid, NULL, ngeoms, geoms);
+
+		initGEOS(lwpgnotice, lwgeom_geos_error);
+		g = LWGEOM2GEOS((LWGEOM*)col, LW_FALSE);
 		if (!g)
 			HANDLE_GEOS_ERROR("Could not create GEOS COLLECTION from geometry array");
 
@@ -642,8 +570,8 @@
 		if (!g_union)
 			HANDLE_GEOS_ERROR("GEOSUnaryUnion");
 
-		GEOSSetSRID(g_union, state->srid);
-		gser_out = GEOS2POSTGIS(g_union, state->is3d);
+		GEOSSetSRID(g_union, srid);
+		gser_out = GEOS2POSTGIS(g_union, has_z);
 		GEOSGeom_destroy(g_union);
 	}
 	/* No real geometries in our array, any empties? */
@@ -650,9 +578,9 @@
 	else
 	{
 		/* If it was only empties, we'll return the largest type number */
-		if (state->empty_type > 0)
+		if (empty_type > 0)
 			PG_RETURN_POINTER(
-			    geometry_serialize(lwgeom_construct_empty(state->empty_type, state->srid, state->is3d, 0)));
+			    geometry_serialize(lwgeom_construct_empty(empty_type, srid, has_z, 0)));
 
 		/* Nothing but NULL, returns NULL */
 		else
@@ -668,6 +596,8 @@
 	PG_RETURN_POINTER(gser_out);
 }
 
+
+
 /**
  * @example ST_UnaryUnion {@link #geomunion} SELECT ST_UnaryUnion(
  *      'POLYGON((0 0, 10 0, 0 10, 10 10, 0 0))'

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/postgis/postgis.sql.in	2019-10-04 18:25:46 UTC (rev 17856)
@@ -3835,12 +3835,6 @@
 	AS 'MODULE_PATHNAME'
 	LANGUAGE 'c' _PARALLEL;
 
--- Availability: 3.0.0
-CREATE OR REPLACE FUNCTION pgis_geometry_union_transfn(internal, geometry)
-	RETURNS internal
-	AS 'MODULE_PATHNAME'
-	LANGUAGE 'c' _PARALLEL;
-
 -- Availability: 1.4.0
 -- Changed: 2.5.0 use 'internal' transfer type
 CREATE OR REPLACE FUNCTION pgis_geometry_union_finalfn(internal)
@@ -3894,9 +3888,8 @@
 -- 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
--- Changed: 3.0.0 transfn now converts to GEOS
 CREATE AGGREGATE ST_Union (geometry) (
-	sfunc = pgis_geometry_union_transfn,
+	sfunc = pgis_geometry_accum_transfn,
 	stype = internal,
 #if POSTGIS_PGSQL_VERSION >= 96
 	parallel = safe,

Modified: trunk/postgis/postgis_after_upgrade.sql
===================================================================
--- trunk/postgis/postgis_after_upgrade.sql	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/postgis/postgis_after_upgrade.sql	2019-10-04 18:25:46 UTC (rev 17856)
@@ -227,6 +227,8 @@
 DROP FUNCTION IF EXISTS st_combine_bbox(box2d, geometry);
 DROP FUNCTION IF EXISTS st_distance_sphere(geometry, geometry);
 
+-- dev function 3.0 cycle
+DROP FUNCTION IF EXISTS pgis_geometry_union_transfn(internal, geometry);
 
 -- pgis_abs type was increased from 8 bytes in 2.1 to 16 bytes in 2.2
 -- See #3460

Modified: trunk/postgis/postgis_before_upgrade.sql
===================================================================
--- trunk/postgis/postgis_before_upgrade.sql	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/postgis/postgis_before_upgrade.sql	2019-10-04 18:25:46 UTC (rev 17856)
@@ -198,7 +198,6 @@
 -- This signature was superseeded
 DROP FUNCTION IF EXISTS st_buffer(geometry, double precision); -- Does not conflict
 
-
 -- FUNCTION ST_CurveToLine changed to add defaults in 2.5
 -- These signatures were superseeded
 DROP FUNCTION IF EXISTS ST_CurveToLine(geometry, integer); -- Does not conflict

Modified: trunk/regress/run_test.pl
===================================================================
--- trunk/regress/run_test.pl	2019-10-03 09:49:41 UTC (rev 17855)
+++ trunk/regress/run_test.pl	2019-10-04 18:25:46 UTC (rev 17856)
@@ -299,6 +299,25 @@
     exit(1);
   }
 
+  $query = "insert into upgrade_test(g1,g2) values ";
+	$query .= "('POINT(0 0)', 'LINESTRING(0 0, 1 1)'), ";
+	$query .= "('POINT(1 0)', 'LINESTRING(0 1, 1 1)');";
+  my $ret = sql($query);
+  unless ( $ret =~ /^INSERT/ ) {
+    `dropdb $DB`;
+    print "\nSomething went wrong populating upgrade_test table: $ret.\n";
+    exit(1);
+  }
+
+  my $query = "create view upgrade_view_test as ";
+  $query .= "select st_union(g1) from upgrade_test;";
+  my $ret = sql($query);
+  unless ( $ret =~ /^CREATE/ ) {
+    `dropdb $DB`;
+    print "\nSomething went wrong creating upgrade_view_test view: $ret.\n";
+    exit(1);
+  }
+
   if ( $OPT_WITH_RASTER )
   {
     $query = "insert into upgrade_test(r) ";
@@ -328,6 +347,13 @@
 {
   # TODO: allow passing the "upgrade-cleanup" script via commandline
 
+  my $ret = sql("drop view upgrade_view_test;");
+  unless ( $ret =~ /^DROP/ ) {
+    `dropdb $DB`;
+    print "\nSomething went wrong dropping spatial view: $ret.\n";
+    exit(1);
+  }
+
   my $ret = sql("drop table upgrade_test;");
   unless ( $ret =~ /^DROP/ ) {
     `dropdb $DB`;



More information about the postgis-tickets mailing list