[postgis-tickets] [SCM] PostGIS branch master updated. 3.1.0alpha1-50-g8b548a4

git at osgeo.org git at osgeo.org
Thu Apr 9 08:47:03 PDT 2020


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  8b548a4697490b7e45508b91cb340eb79b424a92 (commit)
      from  d92c6b7cf4b3c8078b6ffcbc59a91462db57f92a (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 8b548a4697490b7e45508b91cb340eb79b424a92
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Thu Apr 9 08:46:56 2020 -0700

    Add a toast cache to index cache infrastructure, so that repeated loads of toasted-to-disk datums can avoid actually having to decompress/detoast the same object, closes #4657

diff --git a/libpgcommon/lwgeom_cache.c b/libpgcommon/lwgeom_cache.c
index 1dcb3f8..4664060 100644
--- a/libpgcommon/lwgeom_cache.c
+++ b/libpgcommon/lwgeom_cache.c
@@ -12,6 +12,7 @@
 
 #include "postgres.h"
 #include "fmgr.h"
+#include "access/tuptoaster.h"
 
 #include "../postgis_config.h"
 #include "lwgeom_cache.h"
@@ -145,9 +146,10 @@ GetGeomCache(FunctionCallInfo fcinfo,
 
 	/* Cache hit on the first argument */
 	if ( g1 &&
-	     cache->argnum != 2 &&
-	     cache->geom1_size == VARSIZE(g1) &&
-	     memcmp(cache->geom1, g1, cache->geom1_size) == 0 )
+	     cache->argnum != 2 && (
+	     (g1 == cache->geom1) ||
+	     (cache->geom1_size == VARSIZE(g1) && memcmp(cache->geom1, g1, cache->geom1_size) == 0)
+	     ))
 	{
 		cache_hit = 1;
 		geom = cache->geom1;
@@ -155,9 +157,10 @@ GetGeomCache(FunctionCallInfo fcinfo,
 	}
 	/* Cache hit on second argument */
 	else if ( g2 &&
-	          cache->argnum != 1 &&
-	          cache->geom2_size == VARSIZE(g2) &&
-	          memcmp(cache->geom2, g2, cache->geom2_size) == 0 )
+	          cache->argnum != 1 && (
+	          (g2 == cache->geom2) ||
+	          (cache->geom2_size == VARSIZE(g2) && memcmp(cache->geom2, g2, cache->geom2_size) == 0)
+	          ))
 	{
 		cache_hit = 2;
 		geom = cache->geom2;
@@ -236,4 +239,73 @@ GetGeomCache(FunctionCallInfo fcinfo,
 	return NULL;
 }
 
+/******************************************************************************/
 
+inline static ToastCache*
+ToastCacheGet(FunctionCallInfo fcinfo)
+{
+	const uint32_t entry_number = TOAST_CACHE_ENTRY;
+	GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
+	ToastCache* cache = (ToastCache*)(generic_cache->entry[entry_number]);
+	if (!cache)
+	{
+		cache = MemoryContextAllocZero(FIContext(fcinfo), sizeof(ToastCache));
+		cache->type = entry_number;
+		generic_cache->entry[entry_number] = (GenericCache*)cache;
+	}
+	return cache;
+}
+
+GSERIALIZED*
+ToastCacheGetGeometry(FunctionCallInfo fcinfo, uint32_t argnum)
+{
+	Assert(argnum < ToastCacheSize);
+	ToastCache* cache = ToastCacheGet(fcinfo);
+	ToastCacheArgument* arg = &(cache->arg[argnum]);
+
+	Datum datum = PG_GETARG_DATUM(argnum);
+	struct varlena *attr = (struct varlena *) DatumGetPointer(datum);
+
+	/*
+	* In general, you have to detoast the whole object to
+	* determine if it's different from the cached object, but
+	* for toasted objects, the va_valueid and va_toastrelid are
+	* a unique key. Only objects toasted to disk have this
+	* property, but fortunately those are also the objects
+	* that are costliest to de-TOAST, which is why this
+	* cache is a performance win.
+	* https://www.postgresql.org/message-id/8196.1585870220@sss.pgh.pa.us
+	*/
+	if (!VARATT_IS_EXTERNAL_ONDISK(attr))
+		return (GSERIALIZED*)PG_DETOAST_DATUM(datum);
+
+	/* Retrieve the unique keys for this object */
+	struct varatt_external ve;
+	VARATT_EXTERNAL_GET_POINTER(ve, attr);
+	Oid valueid = ve.va_valueid;
+	Oid toastrelid = ve.va_toastrelid;
+
+	/* We've seen this object before? */
+	if (arg->valueid == valueid && arg->toastrelid == toastrelid)
+	{
+		if (arg->geom)
+			return arg->geom;
+
+		/* Take a copy into the upper context */
+		MemoryContext old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+		arg->geom = (GSERIALIZED*)PG_DETOAST_DATUM_COPY(datum);
+		MemoryContextSwitchTo(old_context);
+		return arg->geom;
+	}
+	/* New object, clear our old copies and see if it */
+	/* shows up a second time before taking a copy */
+	else
+	{
+		if (arg->geom)
+			pfree(arg->geom);
+		arg->valueid = valueid;
+		arg->toastrelid = toastrelid;
+		arg->geom = NULL;
+		return (GSERIALIZED*)PG_DETOAST_DATUM(datum);
+	}
+}
diff --git a/libpgcommon/lwgeom_cache.h b/libpgcommon/lwgeom_cache.h
index 16edb09..745e178 100644
--- a/libpgcommon/lwgeom_cache.h
+++ b/libpgcommon/lwgeom_cache.h
@@ -26,8 +26,9 @@
 #define RTREE_CACHE_ENTRY 2
 #define CIRC_CACHE_ENTRY 3
 #define RECT_CACHE_ENTRY 4
+#define TOAST_CACHE_ENTRY 5
 
-#define NUM_CACHE_ENTRIES 16
+#define NUM_CACHE_ENTRIES 8
 
 
 /*
@@ -110,4 +111,24 @@ GeomCache *GetGeomCache(FunctionCallInfo fcinfo,
 			const GSERIALIZED *g1,
 			const GSERIALIZED *g2);
 
+/******************************************************************************/
+
+#define ToastCacheSize 2
+
+typedef struct
+{
+	Oid valueid;
+	Oid toastrelid;
+	GSERIALIZED *geom;
+} ToastCacheArgument;
+
+typedef struct
+{
+	int type;
+	ToastCacheArgument arg[ToastCacheSize];
+} ToastCache;
+
+GSERIALIZED* ToastCacheGetGeometry(FunctionCallInfo fcinfo, uint32_t argnum);
+
+
 #endif /* LWGEOM_CACHE_H_ */
diff --git a/postgis/geography_measurement.c b/postgis/geography_measurement.c
index aa66929..851eda0 100644
--- a/postgis/geography_measurement.c
+++ b/postgis/geography_measurement.c
@@ -212,8 +212,8 @@ Datum geography_distance(PG_FUNCTION_ARGS)
 	SPHEROID s;
 
 	/* Get our geometry objects loaded into memory. */
-	g1 = PG_GETARG_GSERIALIZED_P(0);
-	g2 = PG_GETARG_GSERIALIZED_P(1);
+	g1 = ToastCacheGetGeometry(fcinfo, 0);
+	g2 = ToastCacheGetGeometry(fcinfo, 1);
 
 	if (PG_NARGS() > 2)
 		use_spheroid = PG_GETARG_BOOL(2);
@@ -230,8 +230,6 @@ Datum geography_distance(PG_FUNCTION_ARGS)
 	/* 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();
 	}
 
@@ -250,10 +248,6 @@ Datum geography_distance(PG_FUNCTION_ARGS)
 		*/
 	}
 
-	/* Clean up */
-	PG_FREE_IF_COPY(g1, 0);
-	PG_FREE_IF_COPY(g2, 1);
-
 	/* Knock off any funny business at the nanometer level, ticket #2168 */
 	distance = round(distance * INVMINDIST) / INVMINDIST;
 
@@ -274,8 +268,8 @@ Datum geography_distance(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(geography_dwithin);
 Datum geography_dwithin(PG_FUNCTION_ARGS)
 {
-	GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
-	GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+	GSERIALIZED *g1 = ToastCacheGetGeometry(fcinfo, 0);
+	GSERIALIZED *g2 = ToastCacheGetGeometry(fcinfo, 1);
 	SPHEROID s;
 	double tolerance = 0.0;
 	bool use_spheroid = true;
@@ -317,8 +311,6 @@ Datum geography_dwithin(PG_FUNCTION_ARGS)
 		lwgeom_free(lwgeom2);
 	}
 
-	PG_FREE_IF_COPY(g1, 0);
-	PG_FREE_IF_COPY(g2, 1);
 	PG_RETURN_BOOL(dwithin);
 }
 
diff --git a/postgis/lwgeom_geos.c b/postgis/lwgeom_geos.c
index d8580ab..8030ece 100644
--- a/postgis/lwgeom_geos.c
+++ b/postgis/lwgeom_geos.c
@@ -1658,8 +1658,8 @@ Datum overlaps(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(contains);
 Datum contains(PG_FUNCTION_ARGS)
 {
-	GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
-	GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
+	GSERIALIZED *geom1 = ToastCacheGetGeometry(fcinfo, 0);
+	GSERIALIZED *geom2 = ToastCacheGetGeometry(fcinfo, 1);
 	int result;
 	GEOSGeometry *g1, *g2;
 	GBOX box1, box2;
@@ -1777,8 +1777,6 @@ Datum contains(PG_FUNCTION_ARGS)
 
 	if (result == 2) HANDLE_GEOS_ERROR("GEOSContains");
 
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
 	PG_RETURN_BOOL(result > 0);
 }
 
@@ -1801,8 +1799,8 @@ Datum containsproperly(PG_FUNCTION_ARGS)
 	GBOX 			box1, box2;
 	PrepGeomCache *	prep_cache;
 
-	geom1 = PG_GETARG_GSERIALIZED_P(0);
-	geom2 = PG_GETARG_GSERIALIZED_P(1);
+	geom1 = ToastCacheGetGeometry(fcinfo, 0);
+	geom2 = ToastCacheGetGeometry(fcinfo, 1);
 	gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
 
 	/* A.ContainsProperly(Empty) == FALSE */
@@ -1851,9 +1849,6 @@ Datum containsproperly(PG_FUNCTION_ARGS)
 
 	if (result == 2) HANDLE_GEOS_ERROR("GEOSContains");
 
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
 	PG_RETURN_BOOL(result);
 }
 
@@ -1870,8 +1865,8 @@ Datum covers(PG_FUNCTION_ARGS)
 	GBOX box1, box2;
 	PrepGeomCache *prep_cache;
 
-	geom1 = PG_GETARG_GSERIALIZED_P(0);
-	geom2 = PG_GETARG_GSERIALIZED_P(1);
+	geom1 = ToastCacheGetGeometry(fcinfo, 0);
+	geom2 = ToastCacheGetGeometry(fcinfo, 1);
 
 	/* A.Covers(Empty) == FALSE */
 	if ( gserialized_is_empty(geom1) || gserialized_is_empty(geom2) )
@@ -1936,8 +1931,6 @@ Datum covers(PG_FUNCTION_ARGS)
 			PG_RETURN_NULL();
 		}
 
-		PG_FREE_IF_COPY(geom1, 0);
-		PG_FREE_IF_COPY(geom2, 1);
 		PG_RETURN_BOOL(retval);
 	}
 	else
@@ -1976,11 +1969,7 @@ Datum covers(PG_FUNCTION_ARGS)
 
 	if (result == 2) HANDLE_GEOS_ERROR("GEOSCovers");
 
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
 	PG_RETURN_BOOL(result);
-
 }
 
 
@@ -2005,8 +1994,8 @@ Datum coveredby(PG_FUNCTION_ARGS)
 	GBOX box1, box2;
 	char *patt = "**F**F***";
 
-	geom1 = PG_GETARG_GSERIALIZED_P(0);
-	geom2 = PG_GETARG_GSERIALIZED_P(1);
+	geom1 = ToastCacheGetGeometry(fcinfo, 0);
+	geom2 = ToastCacheGetGeometry(fcinfo, 1);
 	gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
 
 	/* A.CoveredBy(Empty) == FALSE */
@@ -2072,8 +2061,6 @@ Datum coveredby(PG_FUNCTION_ARGS)
 			PG_RETURN_NULL();
 		}
 
-		PG_FREE_IF_COPY(geom1, 0);
-		PG_FREE_IF_COPY(geom2, 1);
 		PG_RETURN_BOOL(retval);
 	}
 	else
@@ -2103,9 +2090,6 @@ Datum coveredby(PG_FUNCTION_ARGS)
 
 	if (result == 2) HANDLE_GEOS_ERROR("GEOSCoveredBy");
 
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
 	PG_RETURN_BOOL(result);
 }
 
@@ -2174,8 +2158,8 @@ Datum ST_Intersects(PG_FUNCTION_ARGS)
 	GBOX box1, box2;
 	PrepGeomCache *prep_cache;
 
-	geom1 = PG_GETARG_GSERIALIZED_P(0);
-	geom2 = PG_GETARG_GSERIALIZED_P(1);
+	geom1 = ToastCacheGetGeometry(fcinfo, 0);
+	geom2 = ToastCacheGetGeometry(fcinfo, 1);
 	gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
 
 	/* A.Intersects(Empty) == FALSE */
@@ -2238,8 +2222,6 @@ Datum ST_Intersects(PG_FUNCTION_ARGS)
 			PG_RETURN_NULL();
 		}
 
-		PG_FREE_IF_COPY(geom1, 0);
-		PG_FREE_IF_COPY(geom2, 1);
 		PG_RETURN_BOOL(retval);
 	}
 
@@ -2283,9 +2265,6 @@ Datum ST_Intersects(PG_FUNCTION_ARGS)
 
 	if (result == 2) HANDLE_GEOS_ERROR("GEOSIntersects");
 
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
 	PG_RETURN_BOOL(result);
 }
 

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

Summary of changes:
 libpgcommon/lwgeom_cache.c      | 84 ++++++++++++++++++++++++++++++++++++++---
 libpgcommon/lwgeom_cache.h      | 23 ++++++++++-
 postgis/geography_measurement.c | 16 ++------
 postgis/lwgeom_geos.c           | 41 +++++---------------
 4 files changed, 114 insertions(+), 50 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list