[postgis-tickets] r16410 - Add cache support for rect_node trees and expose for testing
Paul Ramsey
pramsey at cleverelephant.ca
Thu Feb 22 11:44:07 PST 2018
Author: pramsey
Date: 2018-02-22 11:44:06 -0800 (Thu, 22 Feb 2018)
New Revision: 16410
Added:
trunk/postgis/lwgeom_rectree.c
Modified:
trunk/libpgcommon/lwgeom_cache.c
trunk/libpgcommon/lwgeom_cache.h
trunk/postgis/Makefile.in
trunk/postgis/geography_measurement_trees.c
trunk/postgis/lwgeom_functions_analytic.c
trunk/postgis/lwgeom_geos.c
trunk/postgis/lwgeom_geos_prepared.c
trunk/postgis/lwgeom_geos_prepared.h
trunk/postgis/lwgeom_rtree.c
trunk/postgis/lwgeom_rtree.h
trunk/postgis/postgis.sql.in
Log:
Add cache support for rect_node trees and expose for testing
in _ST_DistanceRectTreeCached(geom, geom).
References #2614
Modified: trunk/libpgcommon/lwgeom_cache.c
===================================================================
--- trunk/libpgcommon/lwgeom_cache.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/libpgcommon/lwgeom_cache.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -174,6 +174,16 @@
cache_methods->GeomIndexFreer(cache);
cache->argnum = 0;
}
+ if ( cache->lwgeom1 )
+ {
+ lwgeom_free(cache->lwgeom1);
+ cache->lwgeom1 = 0;
+ }
+ if ( cache->lwgeom2 )
+ {
+ lwgeom_free(cache->lwgeom2);
+ cache->lwgeom2 = 0;
+ }
}
/* Cache hit, but no tree built yet, build it! */
@@ -180,24 +190,29 @@
if ( cache_hit && ! cache->argnum )
{
int rv;
- LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
+ LWGEOM *lwgeom;
+ /* Save the tree and supporting geometry in the cache */
+ /* memory context */
+ old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+ lwgeom = lwgeom_from_gserialized(geom);
+ cache->argnum = 0;
+
/* Can't build a tree on a NULL or empty */
- if ( (!lwgeom) || lwgeom_is_empty(lwgeom) )
+ if ((!lwgeom) || lwgeom_is_empty(lwgeom))
+ {
+ MemoryContextSwitchTo(old_context);
return NULL;
-
- old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+ }
rv = cache_methods->GeomIndexBuilder(lwgeom, cache);
MemoryContextSwitchTo(old_context);
- cache->argnum = cache_hit;
/* Something went awry in the tree build phase */
if ( ! rv )
- {
- cache->argnum = 0;
return NULL;
- }
+ /* Only set an argnum if everything completely successfully */
+ cache->argnum = cache_hit;
}
/* We have a hit and a calculated tree, we're done */
Modified: trunk/libpgcommon/lwgeom_cache.h
===================================================================
--- trunk/libpgcommon/lwgeom_cache.h 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/libpgcommon/lwgeom_cache.h 2018-02-22 19:44:06 UTC (rev 16410)
@@ -42,6 +42,8 @@
GSERIALIZED* geom2;
size_t geom1_size;
size_t geom2_size;
+ LWGEOM* lwgeom1;
+ LWGEOM* lwgeom2;
int32 argnum;
} GeomCache;
Modified: trunk/postgis/Makefile.in
===================================================================
--- trunk/postgis/Makefile.in 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/Makefile.in 2018-02-22 19:44:06 UTC (rev 16410)
@@ -85,6 +85,7 @@
lwgeom_dumppoints.o \
lwgeom_functions_lrs.o \
lwgeom_functions_temporal.o \
+ lwgeom_rectree.o \
long_xact.o \
lwgeom_sqlmm.o \
lwgeom_rtree.o \
Modified: trunk/postgis/geography_measurement_trees.c
===================================================================
--- trunk/postgis/geography_measurement_trees.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/geography_measurement_trees.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -33,13 +33,8 @@
* liblwgeom, where most of the circtree logic lives.
*/
typedef struct {
- int type; // <GeomCache>
- GSERIALIZED* geom1; //
- GSERIALIZED* geom2; //
- size_t geom1_size; //
- size_t geom2_size; //
- int32 argnum; // </GeomCache>
- CIRC_NODE* index;
+ GeomCache gcache;
+ CIRC_NODE* index;
} CircTreeGeomCache;
@@ -73,7 +68,7 @@
{
circ_tree_free(circ_cache->index);
circ_cache->index = 0;
- circ_cache->argnum = 0;
+ circ_cache->gcache.argnum = 0;
}
return LW_SUCCESS;
}
@@ -176,7 +171,7 @@
/* OK, we have an index at the ready! Use it for the one tree argument and */
/* fill in the other tree argument */
- if ( tree_cache && tree_cache->argnum && tree_cache->index )
+ if ( tree_cache && tree_cache->gcache.argnum && tree_cache->index )
{
CIRC_NODE* circtree_cached = tree_cache->index;
CIRC_NODE* circtree = NULL;
@@ -188,7 +183,7 @@
POINT4D p4d;
/* We need to dynamically build a tree for the uncached side of the function call */
- if ( tree_cache->argnum == 1 )
+ if ( tree_cache->gcache.argnum == 1 )
{
g_cached = g1;
g = g2;
@@ -195,7 +190,7 @@
geomtype_cached = type1;
geomtype = type2;
}
- else if ( tree_cache->argnum == 2 )
+ else if ( tree_cache->gcache.argnum == 2 )
{
g_cached = g2;
g = g1;
Modified: trunk/postgis/lwgeom_functions_analytic.c
===================================================================
--- trunk/postgis/lwgeom_functions_analytic.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_functions_analytic.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -46,7 +46,6 @@
Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
Datum ST_IsPolygonCW(PG_FUNCTION_ARGS);
-Datum ST_DistanceRectTree(PG_FUNCTION_ARGS);
static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
@@ -1275,22 +1274,3 @@
PG_RETURN_BOOL(is_ccw);
}
-/**********************************************************************
- * ST_DistanceRectTree
- **********************************************************************/
-
-#include "liblwgeom_internal.h"
-#include "lwtree.h"
-
-PG_FUNCTION_INFO_V1(ST_DistanceRectTree);
-Datum ST_DistanceRectTree(PG_FUNCTION_ARGS)
-{
- GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
- GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
- LWGEOM *lwg1 = lwgeom_from_gserialized(g1);
- LWGEOM *lwg2 = lwgeom_from_gserialized(g2);
- RECT_NODE *n1 = rect_tree_from_lwgeom(lwg1);
- RECT_NODE *n2 = rect_tree_from_lwgeom(lwg2);
- PG_RETURN_FLOAT8(rect_tree_distance_tree(n1, n2, 0.0));
-}
-
Modified: trunk/postgis/lwgeom_geos.c
===================================================================
--- trunk/postgis/lwgeom_geos.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_geos.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -1876,7 +1876,7 @@
prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
- if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+ if ( prep_cache && prep_cache->prepared_geom && prep_cache->gcache.argnum == 1 )
{
g1 = POSTGIS2GEOS(geom2);
if (!g1)
@@ -1950,7 +1950,7 @@
prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
- if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+ if ( prep_cache && prep_cache->prepared_geom && prep_cache->gcache.argnum == 1 )
{
GEOSGeometry *g = POSTGIS2GEOS(geom2);
if (!g)
@@ -2084,7 +2084,7 @@
prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
- if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+ if ( prep_cache && prep_cache->prepared_geom && prep_cache->gcache.argnum == 1 )
{
GEOSGeometry *g1 = POSTGIS2GEOS(geom2);
if (!g1)
@@ -2401,7 +2401,7 @@
if ( prep_cache && prep_cache->prepared_geom )
{
- if ( prep_cache->argnum == 1 )
+ if ( prep_cache->gcache.argnum == 1 )
{
GEOSGeometry *g = POSTGIS2GEOS(geom2);
if (!g)
Modified: trunk/postgis/lwgeom_geos_prepared.c
===================================================================
--- trunk/postgis/lwgeom_geos_prepared.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_geos_prepared.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -343,7 +343,7 @@
* Hum, we shouldn't be asked to build a new cache on top of
* an existing one. Error.
*/
- if ( prepcache->argnum || prepcache->geom || prepcache->prepared_geom )
+ if ( prepcache->gcache.argnum || prepcache->geom || prepcache->prepared_geom )
{
lwpgerror("PrepGeomCacheBuilder asked to build new prepcache where one already exists.");
return LW_FAILURE;
@@ -363,7 +363,7 @@
if ( ! prepcache->geom ) return LW_FAILURE;
prepcache->prepared_geom = GEOSPrepare( prepcache->geom );
if ( ! prepcache->prepared_geom ) return LW_FAILURE;
- prepcache->argnum = cache->argnum;
+ prepcache->gcache.argnum = cache->argnum;
/*
* In order to find the objects we need to destroy, we keep
@@ -417,10 +417,10 @@
/*
* Free the GEOS objects and free the index tree
*/
- POSTGIS_DEBUGF(3, "PrepGeomCacheFreeer: freeing %p argnum %d", prepcache, prepcache->argnum);
+ POSTGIS_DEBUGF(3, "PrepGeomCacheFreeer: freeing %p argnum %d", prepcache, prepcache->gcache.argnum);
GEOSPreparedGeom_destroy( prepcache->prepared_geom );
GEOSGeom_destroy( (GEOSGeometry *)prepcache->geom );
- prepcache->argnum = 0;
+ prepcache->gcache.argnum = 0;
prepcache->prepared_geom = 0;
prepcache->geom = 0;
@@ -433,7 +433,7 @@
PrepGeomCache* prepcache = palloc(sizeof(PrepGeomCache));
memset(prepcache, 0, sizeof(PrepGeomCache));
prepcache->context_statement = CurrentMemoryContext;
- prepcache->type = PREP_CACHE_ENTRY;
+ prepcache->gcache.type = PREP_CACHE_ENTRY;
return (GeomCache*)prepcache;
}
Modified: trunk/postgis/lwgeom_geos_prepared.h
===================================================================
--- trunk/postgis/lwgeom_geos_prepared.h 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_geos_prepared.h 2018-02-22 19:44:06 UTC (rev 16410)
@@ -36,6 +36,7 @@
#include "lwgeom_pg.h"
#include "liblwgeom.h"
#include "lwgeom_geos.h"
+#include "lwgeom_cache.h"
/*
* Cache structure. We use GSERIALIZED as keys so no transformations
@@ -53,12 +54,7 @@
* prepared geometry, circtrees, recttrees, and rtrees).
*/
typedef struct {
- int type; // <GeomCache>
- GSERIALIZED* geom1; //
- GSERIALIZED* geom2; //
- size_t geom1_size; //
- size_t geom2_size; //
- int32 argnum; // </GeomCache>
+ GeomCache gcache;
MemoryContext context_statement;
MemoryContext context_callback;
const GEOSPreparedGeometry* prepared_geom;
Added: trunk/postgis/lwgeom_rectree.c
===================================================================
--- trunk/postgis/lwgeom_rectree.c (rev 0)
+++ trunk/postgis/lwgeom_rectree.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -0,0 +1,195 @@
+/**********************************************************************
+ *
+ * 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) 2018 Paul Ramsey
+ *
+ **********************************************************************/
+
+
+#include "postgres.h"
+#include "funcapi.h"
+#include "fmgr.h"
+#include "liblwgeom.h"
+#include "liblwgeom_internal.h" /* For FP comparators. */
+#include "lwgeom_pg.h"
+#include "lwtree.h"
+#include "lwgeom_cache.h"
+
+
+/* Prototypes */
+Datum ST_DistanceRectTree(PG_FUNCTION_ARGS);
+Datum ST_DistanceRectTreeCached(PG_FUNCTION_ARGS);
+
+
+/**********************************************************************
+* RectTree Caching support
+**********************************************************************/
+
+/*
+* Specific tree types include all the generic slots and
+* their own slots for their trees. We put the implementation
+* for the RectTreeGeomCache here because we can't shove
+* the PgSQL specific bits of the code (fcinfo) back into
+* liblwgeom/lwtree.c, where most of the rect_tree logic lives.
+*/
+typedef struct {
+ GeomCache gcache;
+ RECT_NODE *index;
+} RectTreeGeomCache;
+
+
+/**
+* Builder, freeer and public accessor for cached RECT_NODE trees
+*/
+static int
+RectTreeBuilder(const LWGEOM *lwgeom, GeomCache *cache)
+{
+ RectTreeGeomCache *rect_cache = (RectTreeGeomCache*)cache;
+ RECT_NODE *tree = rect_tree_from_lwgeom(lwgeom);
+
+ if ( rect_cache->index )
+ {
+ rect_tree_free(rect_cache->index);
+ rect_cache->index = 0;
+ }
+ if ( ! tree )
+ return LW_FAILURE;
+
+ rect_cache->index = tree;
+ return LW_SUCCESS;
+}
+
+static int
+RectTreeFreer(GeomCache *cache)
+{
+ RectTreeGeomCache *rect_cache = (RectTreeGeomCache*)cache;
+ if ( rect_cache->index )
+ {
+ rect_tree_free(rect_cache->index);
+ rect_cache->index = 0;
+ rect_cache->gcache.argnum = 0;
+ }
+ return LW_SUCCESS;
+}
+
+static GeomCache *
+RectTreeAllocator(void)
+{
+ RectTreeGeomCache *cache = palloc(sizeof(RectTreeGeomCache));
+ memset(cache, 0, sizeof(RectTreeGeomCache));
+ return (GeomCache*)cache;
+}
+
+static GeomCacheMethods RectTreeCacheMethods =
+{
+ RECT_CACHE_ENTRY,
+ RectTreeBuilder,
+ RectTreeFreer,
+ RectTreeAllocator
+};
+
+static RectTreeGeomCache *
+GetRectTreeGeomCache(FunctionCallInfoData *fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2)
+{
+ return (RectTreeGeomCache*)GetGeomCache(fcinfo, &RectTreeCacheMethods, g1, g2);
+}
+
+
+/**********************************************************************
+* ST_DistanceRectTree
+**********************************************************************/
+
+PG_FUNCTION_INFO_V1(ST_DistanceRectTree);
+Datum ST_DistanceRectTree(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+
+ /* Return NULL on empty arguments. */
+ if (gserialized_is_empty(g1) || gserialized_is_empty(g2))
+ {
+ PG_FREE_IF_COPY(g1, 0);
+ PG_FREE_IF_COPY(g2, 1);
+ PG_RETURN_NULL();
+ }
+
+ LWGEOM *lwg1 = lwgeom_from_gserialized(g1);
+ LWGEOM *lwg2 = lwgeom_from_gserialized(g2);
+
+ /* Two points? Get outa here... */
+ if (lwg1->type == POINTTYPE && lwg2->type == POINTTYPE)
+ PG_RETURN_FLOAT8(lwgeom_mindistance2d(lwg1, lwg2));
+
+
+ RECT_NODE *n1 = rect_tree_from_lwgeom(lwg1);
+ RECT_NODE *n2 = rect_tree_from_lwgeom(lwg2);
+ PG_RETURN_FLOAT8(rect_tree_distance_tree(n1, n2, 0.0));
+}
+
+PG_FUNCTION_INFO_V1(ST_DistanceRectTreeCached);
+Datum ST_DistanceRectTreeCached(PG_FUNCTION_ARGS)
+{
+ RectTreeGeomCache *tree_cache = NULL;
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+
+ /* Return NULL on empty arguments. */
+ if (gserialized_is_empty(g1) || gserialized_is_empty(g2))
+ {
+ PG_FREE_IF_COPY(g1, 0);
+ PG_FREE_IF_COPY(g2, 1);
+ PG_RETURN_NULL();
+ }
+
+ LWGEOM *lwg1 = lwgeom_from_gserialized(g1);
+ LWGEOM *lwg2 = lwgeom_from_gserialized(g2);
+
+ /* Two points? Get outa here... */
+ if (lwg1->type == POINTTYPE && lwg2->type == POINTTYPE)
+ PG_RETURN_FLOAT8(lwgeom_mindistance2d(lwg1, lwg2));
+
+ /* Fetch/build our cache, if appropriate, etc... */
+ tree_cache = GetRectTreeGeomCache(fcinfo, g1, g2);
+
+ if (tree_cache && tree_cache->gcache.argnum)
+ {
+ RECT_NODE *n;
+ RECT_NODE *n_cached = tree_cache->index;;
+ if (tree_cache->gcache.argnum == 1)
+ {
+ n = rect_tree_from_lwgeom(lwg2);
+ }
+ else if (tree_cache->gcache.argnum == 2)
+ {
+ n = rect_tree_from_lwgeom(lwg1);
+ }
+ else
+ {
+ elog(ERROR, "reached unreachable block in %s", __func__);
+ }
+ PG_RETURN_FLOAT8(rect_tree_distance_tree(n, n_cached, 0.0));
+ }
+ else
+ {
+ PG_RETURN_FLOAT8(lwgeom_mindistance2d(lwg1, lwg2));
+ }
+
+ PG_RETURN_NULL();
+}
Modified: trunk/postgis/lwgeom_rtree.c
===================================================================
--- trunk/postgis/lwgeom_rtree.c 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_rtree.c 2018-02-22 19:44:06 UTC (rev 16410)
@@ -407,7 +407,7 @@
RTreeCacheClear(rtree_cache->index);
lwfree(rtree_cache->index);
rtree_cache->index = 0;
- rtree_cache->argnum = 0;
+ rtree_cache->gcache.argnum = 0;
}
return LW_SUCCESS;
}
Modified: trunk/postgis/lwgeom_rtree.h
===================================================================
--- trunk/postgis/lwgeom_rtree.h 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/lwgeom_rtree.h 2018-02-22 19:44:06 UTC (rev 16410)
@@ -26,6 +26,7 @@
#define _LWGEOM_RTREE_H 1
#include "liblwgeom.h"
+#include "lwgeom_cache.h"
/**
* Representation for the y-axis interval spanned by an edge.
@@ -59,22 +60,15 @@
RTREE_NODE **ringIndices;
int* ringCounts;
int polyCount;
-}
-RTREE_POLY_CACHE;
+} RTREE_POLY_CACHE;
-
-typedef struct {
- int type; // <GeomCache>
- GSERIALIZED* geom1; //
- GSERIALIZED* geom2; //
- size_t geom1_size; //
- size_t geom2_size; //
- int32 argnum; // </GeomCache>
- RTREE_POLY_CACHE* index;
+typedef struct
+{
+ GeomCache gcache;
+ RTREE_POLY_CACHE *index;
} RTreeGeomCache;
-
/**
* Retrieves a collection of line segments given the root and crossing value.
*/
Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in 2018-02-22 15:52:47 UTC (rev 16409)
+++ trunk/postgis/postgis.sql.in 2018-02-22 19:44:06 UTC (rev 16410)
@@ -1315,6 +1315,11 @@
AS 'MODULE_PATHNAME', 'ST_DistanceRectTree'
LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+CREATE OR REPLACE FUNCTION _ST_DistanceRectTreeCached(g1 geometry, g2 geometry)
+ RETURNS float8
+ AS 'MODULE_PATHNAME', 'ST_DistanceRectTreeCached'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+
------------------------------------------------------------------------
-- MISC
------------------------------------------------------------------------
More information about the postgis-tickets
mailing list