]> granicus.if.org Git - postgis/commitdiff
Restore ST_Union() aggregate signature and re-work
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 4 Oct 2019 18:25:46 +0000 (18:25 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 4 Oct 2019 18:25:46 +0000 (18:25 +0000)
performance/size enhancement to continue to avoid
using Array type during ST_Union(), hopefully
avoiding Array size limitations.

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

postgis/lwgeom_accum.c
postgis/lwgeom_accum.h [new file with mode: 0644]
postgis/lwgeom_geos.c
postgis/postgis.sql.in
postgis/postgis_after_upgrade.sql
postgis/postgis_before_upgrade.sql
regress/run_test.pl

index ddd5ed3f6baa75a5ddbae7c9e47ef38eae5b4364..950274375c1798d01f70866fe92e9e1a775f617d 100644 (file)
 #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 cluster_within_distance_garray(PG_FUNCTION_ARGS);
 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.
-*/
-
-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.
+** 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.
 */
 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 @@ pgis_geometry_accum_transfn(PG_FUNCTION_ARGS)
 
        if ( PG_ARGISNULL(0) )
        {
-               MemoryContext old = MemoryContextSwitchTo(aggcontext);
-               p = (pgis_abs*) palloc(sizeof(pgis_abs));
-               p->a = NULL;
-               p->data = (Datum) NULL;
-
-               if (PG_NARGS() == 3)
-               {
-                       Datum argument = PG_GETARG_DATUM(2);
-                       Oid dataOid = get_fn_expr_argtype(fcinfo->flinfo, 2);
+               int n = ((PG_NARGS()-2) <= CollectionBuildStateDataSize) ? (PG_NARGS()-2) : CollectionBuildStateDataSize;
 
-                       p->data = datumCopy(argument, get_typbyval(dataOid), get_typlen(dataOid));
+               state = MemoryContextAlloc(aggcontext, sizeof(CollectionBuildState));
+               state->geoms = NULL;
+               state->geomOid = argType;
 
+               for (int i = 0; i < n; i++)
+               {
+                       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);
 
-Datum pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, FunctionCallInfo fcinfo);
+       MemoryContextSwitchTo(old);
+
+       PG_RETURN_POINTER(state);
+}
+
+
+Datum pgis_accum_finalfn(CollectionBuildState *state, 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 @@ pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, __attribute__((__unused__))
                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,14 +196,14 @@ PG_FUNCTION_INFO_V1(pgis_geometry_collect_finalfn);
 Datum
 pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS)
 {
-       pgis_abs *p;
+       CollectionBuildState *p;
        Datum result = 0;
        Datum geometry_array = 0;
 
        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,14 +222,14 @@ PG_FUNCTION_INFO_V1(pgis_geometry_polygonize_finalfn);
 Datum
 pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS)
 {
-       pgis_abs *p;
+       CollectionBuildState *p;
        Datum result = 0;
        Datum geometry_array = 0;
 
        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,14 +247,14 @@ PG_FUNCTION_INFO_V1(pgis_geometry_makeline_finalfn);
 Datum
 pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS)
 {
-       pgis_abs *p;
+       CollectionBuildState *p;
        Datum result = 0;
        Datum geometry_array = 0;
 
        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,14 +272,14 @@ PG_FUNCTION_INFO_V1(pgis_geometry_clusterintersecting_finalfn);
 Datum
 pgis_geometry_clusterintersecting_finalfn(PG_FUNCTION_ARGS)
 {
-       pgis_abs *p;
+       CollectionBuildState *p;
        Datum result = 0;
        Datum geometry_array = 0;
 
        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,23 +296,23 @@ PG_FUNCTION_INFO_V1(pgis_geometry_clusterwithin_finalfn);
 Datum
 pgis_geometry_clusterwithin_finalfn(PG_FUNCTION_ARGS)
 {
-       pgis_abs *p;
+       CollectionBuildState *p;
        Datum result = 0;
        Datum geometry_array = 0;
 
        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();
        }
 
        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();
 
diff --git a/postgis/lwgeom_accum.h b/postgis/lwgeom_accum.h
new file mode 100644 (file)
index 0000000..c500d67
--- /dev/null
@@ -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@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 */
index f91f7054daa9976db84c97619cedbf118bcd8d36..8284216254df7e014ce66884ac40b42a34d93c7b 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "../postgis_config.h"
 
+#include "float.h" /* for DBL_DIG */
+
 /* PostgreSQL */
 #include "postgres.h"
 #include "funcapi.h"
 #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"
 #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_BuildArea(PG_FUNCTION_ARGS);
 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 @@ Datum pgis_union_geometry_array(PG_FUNCTION_ARGS)
        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;
-
-       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))
-       {
-               state = (UnionBuildState *)PG_GETARG_POINTER(0);
-       }
-       else
-       {
-               old = MemoryContextSwitchTo(aggcontext);
-               state = (UnionBuildState *)palloc(sizeof(UnionBuildState));
-
-               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;
+       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;
 
-               initGEOS(lwpgnotice, lwgeom_geos_error);
+       if (PG_ARGISNULL(0))
+               PG_RETURN_NULL(); /* returns null iff no input values */
 
-               MemoryContextSwitchTo(old);
-       };
+       state = (CollectionBuildState *)PG_GETARG_POINTER(0);
+       geoms = palloc(list_length(state->geoms) * sizeof(LWGEOM*));
 
-       /* 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)
-               {
-                       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))
+               LWGEOM *geom = (LWGEOM*)(lfirst(l));
+               if (geom)
                {
-                       if (state->ngeoms == 0)
-                       {
-                               state->srid = gserialized_get_srid(gser_in);
-                               state->is3d = gserialized_has_z(gser_in);
-                       }
-
-                       old = MemoryContextSwitchTo(state->mcontext);
-                       g = POSTGIS2GEOS(gser_in);
-                       MemoryContextSwitchTo(old);
-
-                       if (!g)
+                       if (!lwgeom_is_empty(geom))
                        {
-                               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");
+                               geoms[ngeoms++] = geom;
+                               if (first)
+                               {
+                                       srid = lwgeom_get_srid(geom);
+                                       has_z = lwgeom_has_z(geom);
+                                       first = false;
+                               }
                        }
-
-                       curgeom = state->ngeoms;
-                       state->ngeoms++;
-
-                       if (state->ngeoms > state->alen)
+                       else
                        {
-                               old = MemoryContextSwitchTo(state->mcontext);
-                               state->alen *= 2;
-                               state->geoms = repalloc(state->geoms, sizeof(GEOSGeometry *) * state->alen);
-                               MemoryContextSwitchTo(old);
+                               int type = lwgeom_get_type(geom);
+                               empty_type = type > empty_type ? type : empty_type;
                        }
-
-                       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,17 +570,17 @@ Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS)
                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? */
        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 @@ Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(gser_out);
 }
 
+
+
 /**
  * @example ST_UnaryUnion {@link #geomunion} SELECT ST_UnaryUnion(
  *      'POLYGON((0 0, 10 0, 0 10, 10 10, 0 0))'
index 1dc1b1ea54a0874dbfc4de6903e55295faebf497..26d82ee3087fb91a890872ab06d8a82024675a40 100644 (file)
@@ -3835,12 +3835,6 @@ CREATE OR REPLACE FUNCTION pgis_geometry_accum_transfn(internal, geometry, float
        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 @@ 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
--- 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,
index fc46e1c49819d431c2e8b9f917627a699f4b2c25..2c0fb94fb8232d505b465fc8ca173d772afc6fe1 100644 (file)
@@ -227,6 +227,8 @@ DROP FUNCTION IF EXISTS st_combine_bbox(box3d, geometry);
 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
index 4f5b0c347aaa0c81d3e4539e07c495b44f0e5066..02ff213065821ba9bafb0e1d5e373c8e3eb9e944 100644 (file)
@@ -198,7 +198,6 @@ DROP FUNCTION IF EXISTS st_askml(geography, integer); -- Does not conflict
 -- 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
index 1a6d4e739e7605a5056130e37db67a3a50996eea..9fdc1aa65fb5089f19755503258d32ae381f5574 100755 (executable)
@@ -299,6 +299,25 @@ sub create_upgrade_test_objects
     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 @@ sub drop_upgrade_test_objects
 {
   # 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`;