[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