[postgis-tickets] [SCM] PostGIS branch stable-3.1 updated. 3.1.1-34-g695dc14

git at osgeo.org git at osgeo.org
Wed May 19 10:20:10 PDT 2021


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, stable-3.1 has been updated
       via  695dc142e3fc9a866b630a0a5911cd429ddc022a (commit)
      from  e1856ea8b9029929c02c2b3ca3efbf0b32d546a8 (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 695dc142e3fc9a866b630a0a5911cd429ddc022a
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed May 19 10:20:03 2021 -0700

    Lengthen proj cache lifetime to the life of the backend, and move memory context appropriately to match. Simplify the retrieval of global constants somewhat to not require FunctionCallInfo anymore. Closes #4890

diff --git a/NEWS b/NEWS
index 2c34494..1e25d81 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,7 @@ PostGIS 3.1.2
            (Paul Ramsey, Regina Obe)
   - #4877, mingw64 PostGIS / PostgreSQL 14 compile (Regina Obe, Tom Lane)
   - #4838, Update to support Tiger 2020 (Regina Obe)
+  - #4890, Change Proj cache lifetime to last as long as connection (Paul Ramsey)
 
 
 PostGIS 3.1.1
diff --git a/liblwgeom/lwgeom_transform.c b/liblwgeom/lwgeom_transform.c
index 51d9c2d..d77d3ba 100644
--- a/liblwgeom/lwgeom_transform.c
+++ b/liblwgeom/lwgeom_transform.c
@@ -500,7 +500,7 @@ ptarray_transform(POINTARRAY *pa, LWPROJ *pj)
 		c.xyzt = v;
 		PJ_COORD t = proj_trans(pj->pj, PJ_FWD, c);
 
-		int pj_errno_val = proj_errno(pj->pj);
+		int pj_errno_val = proj_errno_reset(pj->pj);
 		if (pj_errno_val)
 		{
 			lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
@@ -543,7 +543,7 @@ ptarray_transform(POINTARRAY *pa, LWPROJ *pj)
 			return LW_FAILURE;
 		}
 
-		int pj_errno_val = proj_errno(pj->pj);
+		int pj_errno_val = proj_errno_reset(pj->pj);
 		if (pj_errno_val)
 		{
 			lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
diff --git a/libpgcommon/lwgeom_cache.c b/libpgcommon/lwgeom_cache.c
index cea88ca..f6934b3 100644
--- a/libpgcommon/lwgeom_cache.c
+++ b/libpgcommon/lwgeom_cache.c
@@ -28,14 +28,13 @@
 
 #include "lwgeom_cache.h"
 
+
 /*
 * Generic statement caching infrastructure. We cache
 * the following kinds of objects:
 *
 *   geometries-with-trees
 *      PreparedGeometry, RTree, CIRC_TREE, RECT_TREE
-*   srids-with-projections
-*      projPJ
 *
 * Each GenericCache* has a type, and after that
 * some data. Similar to generic LWGEOM*. Test that
@@ -52,17 +51,12 @@ typedef struct {
 * cache entries, the actual trees stored in the
 * geometries-with-trees pattern are quite diverse,
 * and they might be used in combination, so we have
-* one slot for each tree type as well as a slot for
-* projections.
+* one slot for each tree type.
 */
 typedef struct {
 	GenericCache* entry[NUM_CACHE_ENTRIES];
 } GenericCacheCollection;
 
-/**
- * Utility function to read the upper memory context off a function call
- * info data.
- */
 MemoryContext
 PostgisCacheContext(FunctionCallInfo fcinfo)
 {
@@ -94,37 +88,6 @@ GetGenericCacheCollection(FunctionCallInfo fcinfo)
 
 
 /**
-* Get the Proj entry from the generic cache if one exists.
-* If it doesn't exist, make a new empty one and return it.
-*/
-PROJPortalCache *
-GetPROJSRSCache(FunctionCallInfo fcinfo)
-{
-	GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
-	PROJPortalCache* cache = (PROJPortalCache*)(generic_cache->entry[PROJ_CACHE_ENTRY]);
-
-	if ( ! cache )
-	{
-		/* Allocate in the upper context */
-		cache = MemoryContextAlloc(PostgisCacheContext(fcinfo), sizeof(PROJPortalCache));
-
-		if (cache)
-		{
-			POSTGIS_DEBUGF(3,
-				       "Allocating PROJCache for portal with transform() MemoryContext %p",
-				       PostgisCacheContext(fcinfo));
-			memset(cache->PROJSRSCache, 0, sizeof(PROJSRSCacheItem) * PROJ_CACHE_ITEMS);
-			cache->type = PROJ_CACHE_ENTRY;
-			cache->PROJSRSCacheCount = 0;
-
-			/* Store the pointer in GenericCache */
-			generic_cache->entry[PROJ_CACHE_ENTRY] = (GenericCache*)cache;
-		}
-	}
-	return cache;
-}
-
-/**
 * Get an appropriate (based on the entry type number)
 * GeomCache entry from the generic cache if one exists.
 * Returns a cache pointer if there is a cache hit and we have an
@@ -310,7 +273,7 @@ getSRSbySRID(FunctionCallInfo fcinfo, int32_t srid, bool short_crs)
 	char query[512];
 	char *srs, *srscopy;
 	int size, err;
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	if (SPI_OK_CONNECT != SPI_connect())
 	{
@@ -416,7 +379,7 @@ getSRIDbySRS(FunctionCallInfo fcinfo, const char *srs)
 	Datum values[] = {CStringGetDatum(srs)};
 	int32_t srid, err;
 
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 	snprintf(query,
 		 max_query_size,
 		 "SELECT srid "
diff --git a/libpgcommon/lwgeom_cache.h b/libpgcommon/lwgeom_cache.h
index 113abe0..bbf3e7e 100644
--- a/libpgcommon/lwgeom_cache.h
+++ b/libpgcommon/lwgeom_cache.h
@@ -21,16 +21,15 @@
 #include "lwgeom_pg.h"
 
 
-#define PROJ_CACHE_ENTRY 0
+#define TOAST_CACHE_ENTRY 0
 #define PREP_CACHE_ENTRY 1
 #define RTREE_CACHE_ENTRY 2
 #define CIRC_CACHE_ENTRY 3
 #define RECT_CACHE_ENTRY 4
-#define TOAST_CACHE_ENTRY 5
-#define SRSDESC_CACHE_ENTRY 6
-#define SRID_CACHE_ENTRY 7
+#define SRSDESC_CACHE_ENTRY 5
+#define SRID_CACHE_ENTRY 6
 
-#define NUM_CACHE_ENTRIES 8
+#define NUM_CACHE_ENTRIES 7
 
 /* Returns the MemoryContext used to store the caches */
 MemoryContext PostgisCacheContext(FunctionCallInfo fcinfo);
@@ -66,42 +65,6 @@ typedef struct {
 * PrepGeomCache - lwgeom_geos_prepared.h
 */
 
-/*
-* Proj4 caching has it's own mechanism, but is
-* integrated into the generic caching system because
-* some geography functions make cached SRID lookup
-* calls and also CircTree accelerated calls, so
-* there needs to be a management object on
-* fcinfo->flinfo->fn_extra to avoid collisions.
-*/
-
-/* An entry in the PROJ SRS cache */
-typedef struct struct_PROJSRSCacheItem
-{
-	int32_t srid_from;
-	int32_t srid_to;
-	uint64_t hits;
-	LWPROJ *projection;
-}
-PROJSRSCacheItem;
-
-/* PROJ 4 lookup transaction cache methods */
-#define PROJ_CACHE_ITEMS 128
-
-/*
-* The proj4 cache holds a fixed number of reprojection
-* entries. In normal usage we don't expect it to have
-* many entries, so we always linearly scan the list.
-*/
-typedef struct struct_PROJPortalCache
-{
-	int type;
-	PROJSRSCacheItem PROJSRSCache[PROJ_CACHE_ITEMS];
-	uint32_t PROJSRSCacheCount;
-	MemoryContext PROJSRSCacheContext;
-}
-PROJPortalCache;
-
 /**
 * Generic signature for functions to manage a geometry
 * cache structure.
@@ -117,7 +80,6 @@ typedef struct
 /*
 * Cache retrieval functions
 */
-PROJPortalCache *GetPROJSRSCache(FunctionCallInfo fcinfo);
 GeomCache *GetGeomCache(FunctionCallInfo fcinfo,
 			const GeomCacheMethods *cache_methods,
 			SHARED_GSERIALIZED *g1,
diff --git a/libpgcommon/lwgeom_pg.c b/libpgcommon/lwgeom_pg.c
index 4dbaa76..5629e93 100644
--- a/libpgcommon/lwgeom_pg.c
+++ b/libpgcommon/lwgeom_pg.c
@@ -17,13 +17,28 @@
 #include <postgres.h>
 #include <fmgr.h>
 #include <miscadmin.h>
+#include <access/heapam.h>
+#include <access/htup.h>
+#include <access/htup_details.h>
+#include <access/skey.h>
+#include <access/genam.h>
+#include <access/sysattr.h>
+#include <catalog/indexing.h>
 #include <executor/spi.h>
-#include "utils/builtins.h"
+#include <utils/builtins.h>
 #include <utils/guc.h>
 #include <utils/guc_tables.h>
+#include <utils/fmgroids.h>
 #include <catalog/namespace.h>
+#include <catalog/pg_extension.h>
+#include <commands/extension.h>
 
 #include "../postgis_config.h"
+
+#if POSTGIS_PGSQL_VERSION >= 100
+#include <utils/regproc.h>
+#endif
+
 #include "liblwgeom.h"
 #include "lwgeom_pg.h"
 
@@ -49,27 +64,100 @@ static Oid TypenameNspGetTypid(const char *typname, Oid nsp_oid)
 	                       ObjectIdGetDatum(nsp_oid));
 }
 
+/*
+ * get_extension_schema - given an extension OID, fetch its extnamespace
+ *
+ * Returns InvalidOid if no such extension.
+ */
+static Oid
+postgis_get_extension_schema(Oid ext_oid)
+{
+    Oid         result;
+    SysScanDesc scandesc;
+    HeapTuple   tuple;
+    ScanKeyData entry[1];
+
+#if POSTGIS_PGSQL_VERSION < 120
+    Relation rel = heap_open(ExtensionRelationId, AccessShareLock);
+    ScanKeyInit(&entry[0],
+	    ObjectIdAttributeNumber,
+        BTEqualStrategyNumber, F_OIDEQ,
+        ObjectIdGetDatum(ext_oid));
+#else
+    Relation rel = table_open(ExtensionRelationId, AccessShareLock);
+    ScanKeyInit(&entry[0],
+    	Anum_pg_extension_oid,
+        BTEqualStrategyNumber, F_OIDEQ,
+        ObjectIdGetDatum(ext_oid));
+#endif /* POSTGIS_PGSQL_VERSION */
+
+    scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+                                  NULL, 1, entry);
+
+    tuple = systable_getnext(scandesc);
+
+    /* We assume that there can be at most one matching tuple */
+    if (HeapTupleIsValid(tuple))
+        result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
+    else
+        result = InvalidOid;
+
+    systable_endscan(scandesc);
+
+#if POSTGIS_PGSQL_VERSION < 120
+    heap_close(rel, AccessShareLock);
+#else
+    table_close(rel, AccessShareLock);
+#endif
+
+    return result;
+}
+
+static Oid
+postgis_get_full_version_schema()
+{
+	const char* proname = "postgis_full_version";
+	List* names = stringToQualifiedNameList(proname);
+	FuncCandidateList clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
+	if (!clist)
+		return InvalidOid;
+
+	return get_func_namespace(clist->oid);
+}
+
+
 /* Cache type lookups in per-session location */
 static postgisConstants *
-getPostgisConstants(FunctionCallInfo fcinfo)
+getPostgisConstants()
 {
-	char *nsp_name;
-	Oid nsp_oid;
-	postgisConstants *constants;
+	Oid nsp_oid = InvalidOid;
+	Oid ext_oid = get_extension_oid("postgis", true);
+	if (ext_oid != InvalidOid)
+	{
+		nsp_oid = postgis_get_extension_schema(ext_oid);
+	}
+	else
+	{
+		nsp_oid = postgis_get_full_version_schema();
+	}
+
+	/* early exit if we cannot lookup nsp_name, cf #4067 */
+	if (nsp_oid == InvalidOid)
+		elog(ERROR, "Unable to determine 'postgis' install schema");
 
-	/* For some reason we have a hobbled fcinfo/flinfo */
-	if (!fcinfo || !fcinfo->flinfo) return NULL;
+	/* Put constants cache in a child of the CacheContext */
+	MemoryContext context = AllocSetContextCreate(
+	    CacheMemoryContext,
+	    "PostGIS Constants Context",
+	    ALLOCSET_SMALL_SIZES);
 
 	/* Allocate in the CacheContext so we don't lose this at the end of the statement */
-	constants = MemoryContextAlloc(CacheMemoryContext, sizeof(postgisConstants));
+	postgisConstants* constants = MemoryContextAlloc(context, sizeof(postgisConstants));
 
-	/* early exit if we cannot lookup nsp_name, cf #4067 */
-	nsp_oid = get_func_namespace(fcinfo->flinfo->fn_oid);
-	if (!nsp_oid) return NULL;
-	nsp_name = get_namespace_name(nsp_oid);
+	/* Calculate fully qualified name of 'spatial_ref_sys' */
+	char *nsp_name = get_namespace_name(nsp_oid);
 	constants->install_nsp_oid = nsp_oid;
 	constants->install_nsp = MemoryContextStrdup(CacheMemoryContext, nsp_name);
-	elog(DEBUG4, "%s located %s in namespace %s", __func__, get_func_name(fcinfo->flinfo->fn_oid), nsp_name);
 
 	char *spatial_ref_sys_fullpath = quote_qualified_identifier(nsp_name, "spatial_ref_sys");
 	constants->spatial_ref_sys = MemoryContextStrdup(CacheMemoryContext, spatial_ref_sys_fullpath);
@@ -141,11 +229,11 @@ postgis_oid(postgisType typ)
 }
 
 void
-postgis_initialize_cache(FunctionCallInfo fcinfo)
+postgis_initialize_cache()
 {
 	/* Cache the info if we don't already have it */
 	if (!POSTGIS_CONSTANTS)
-		POSTGIS_CONSTANTS = getPostgisConstants(fcinfo);
+		POSTGIS_CONSTANTS = getPostgisConstants();
 }
 
 const char *
diff --git a/libpgcommon/lwgeom_pg.h b/libpgcommon/lwgeom_pg.h
index 96a6647..49d73b8 100644
--- a/libpgcommon/lwgeom_pg.h
+++ b/libpgcommon/lwgeom_pg.h
@@ -61,7 +61,7 @@ extern postgisConstants *POSTGIS_CONSTANTS;
 
 /* Infer the install location of postgis, and thus the namespace to use
  * when looking up the type name, and cache oids */
-void postgis_initialize_cache(FunctionCallInfo fcinfo);
+void postgis_initialize_cache();
 
 /* Useful if postgis_initialize_cache() has been called before.
  * Otherwise it tries to do a bare lookup */
diff --git a/libpgcommon/lwgeom_transform.c b/libpgcommon/lwgeom_transform.c
index 9c1e854..23f20da 100644
--- a/libpgcommon/lwgeom_transform.c
+++ b/libpgcommon/lwgeom_transform.c
@@ -44,6 +44,9 @@
  */
 #define PROJ_BACKEND_HASH_SIZE 256
 
+/* Global to hold the Proj object cache */
+PROJSRSCache *PROJ_CACHE = NULL;
+
 
 /**
  * Utility structure to get many potential string representations
@@ -57,8 +60,8 @@ typedef struct {
 
 /* Internal Cache API */
 static LWPROJ *
-AddToPROJSRSCache(FunctionCallInfo fcinfo, PROJPortalCache *PROJCache, int32_t srid_from, int32_t srid_to);
-static void DeleteFromPROJSRSCache(PROJPortalCache *PROJCache, uint32_t position);
+AddToPROJSRSCache(PROJSRSCache *PROJCache, int32_t srid_from, int32_t srid_to);
+static void DeleteFromPROJSRSCache(PROJSRSCache *PROJCache, uint32_t position);
 
 static void
 PROJSRSDestroyPJ(void *projection)
@@ -85,12 +88,65 @@ PROJSRSDestroyPJ(void *projection)
 #endif
 }
 
+/**
+* Destroy all the malloc'ed proj objects stored
+* in the PROJSRSCache, in case the containing
+* context is removed (shouldn't actually happen)
+*/
+static void
+PROJSRSDestroyPortalCache(void *portalCache)
+{
+	PROJSRSCache* cache = (PROJSRSCache*)portalCache;
+	for (uint32_t i = 0; i < cache->PROJSRSCacheCount; i++)
+	{
+		if (cache->PROJSRSCache[i].projection)
+			PROJSRSDestroyPJ(cache->PROJSRSCache[i].projection);
+	}
+}
+
+/**
+* Get the Proj cache entry from the global variable if one exists.
+* If it doesn't exist, make a new blank one and return it.
+*/
+PROJSRSCache *
+GetPROJSRSCache()
+{
+	PROJSRSCache* cache = PROJ_CACHE;
+	if (!cache)
+	{
+		/* Put proj cache in a child of the CacheContext */
+		MemoryContext context = AllocSetContextCreate(
+		    CacheMemoryContext,
+		    "Proj Context",
+		    ALLOCSET_SMALL_SIZES);
+
+		/* Allocate in the upper context */
+		cache = MemoryContextAllocZero(context, sizeof(PROJSRSCache));
+
+		if (!cache)
+			elog(ERROR, "Unable to allocate space for PROJSRSCache in context %p", context);
+
+		cache->PROJSRSCacheCount = 0;
+		cache->PROJSRSCacheContext = context;
+
+		/* Use this to clean up PROJSRSCache in event of MemoryContext reset */
+		MemoryContextCallback* callback = MemoryContextAlloc(context, sizeof(MemoryContextCallback));
+		callback->func = PROJSRSDestroyPortalCache;
+		callback->arg = (void *)cache;
+		MemoryContextRegisterResetCallback(context, callback);
+
+		PROJ_CACHE = cache;
+	}
+	return cache;
+}
+
+
 /*****************************************************************************
  * Per-cache management functions
  */
 
 static LWPROJ *
-GetProjectionFromPROJCache(PROJPortalCache *cache, int32_t srid_from, int32_t srid_to)
+GetProjectionFromPROJCache(PROJSRSCache *cache, int32_t srid_from, int32_t srid_to)
 {
 	uint32_t i;
 	for (i = 0; i < cache->PROJSRSCacheCount; i++)
@@ -345,7 +401,7 @@ GetProj4String(int32_t srid)
  * which is the definition for the other half of the transformation.
  */
 static LWPROJ *
-AddToPROJSRSCache(FunctionCallInfo fcinfo, PROJPortalCache *PROJCache, int32_t srid_from, int32_t srid_to)
+AddToPROJSRSCache(PROJSRSCache *PROJCache, int32_t srid_from, int32_t srid_to)
 {
 	MemoryContext oldContext;
 
@@ -363,7 +419,7 @@ AddToPROJSRSCache(FunctionCallInfo fcinfo, PROJPortalCache *PROJCache, int32_t s
 	if (!pjstrs_has_entry(&to_strs))
 		elog(ERROR, "got NULL for SRID (%d)", srid_to);
 
-	oldContext = MemoryContextSwitchTo(PostgisCacheContext(fcinfo));
+	oldContext = MemoryContextSwitchTo(PROJCache->PROJSRSCacheContext);
 
 #if POSTGIS_PROJ_VERSION < 60
 	PJ *projection = palloc(sizeof(PJ));
@@ -450,13 +506,7 @@ AddToPROJSRSCache(FunctionCallInfo fcinfo, PROJPortalCache *PROJCache, int32_t s
 	pjstrs_pfree(&from_strs);
 	pjstrs_pfree(&to_strs);
 
-	/* We register a new callback to delete the projection on exit */
-	MemoryContextCallback *callback =
-	    MemoryContextAlloc(PostgisCacheContext(fcinfo), sizeof(MemoryContextCallback));
-	callback->func = PROJSRSDestroyPJ;
-	callback->arg = (void *)projection;
-	MemoryContextRegisterResetCallback(PostgisCacheContext(fcinfo), callback);
-
+	/* Store everything in new cache entry */
 	PROJCache->PROJSRSCache[cache_position].srid_from = srid_from;
 	PROJCache->PROJSRSCache[cache_position].srid_to = srid_to;
 	PROJCache->PROJSRSCache[cache_position].projection = projection;
@@ -467,7 +517,7 @@ AddToPROJSRSCache(FunctionCallInfo fcinfo, PROJPortalCache *PROJCache, int32_t s
 }
 
 static void
-DeleteFromPROJSRSCache(PROJPortalCache *PROJCache, uint32_t position)
+DeleteFromPROJSRSCache(PROJSRSCache *PROJCache, uint32_t position)
 {
 	POSTGIS_DEBUGF(3,
 		       "removing query cache entry with SRIDs %d => %d at index %u",
@@ -486,23 +536,19 @@ DeleteFromPROJSRSCache(PROJPortalCache *PROJCache, uint32_t position)
 
 
 int
-GetPJUsingFCInfo(FunctionCallInfo fcinfo, int32_t srid_from, int32_t srid_to, LWPROJ **pj)
+GetLWPROJ(int32_t srid_from, int32_t srid_to, LWPROJ **pj)
 {
-	PROJPortalCache *proj_cache = NULL;
-
-	/* Look up the spatial_ref_sys schema if we haven't already */
-	postgis_initialize_cache(fcinfo);
-
 	/* get or initialize the cache for this round */
-	proj_cache = GetPROJSRSCache(fcinfo);
+	PROJSRSCache* proj_cache = GetPROJSRSCache();
 	if (!proj_cache)
 		return LW_FAILURE;
 
+	postgis_initialize_cache();
 	/* Add the output srid to the cache if it's not already there */
 	*pj = GetProjectionFromPROJCache(proj_cache, srid_from, srid_to);
 	if (*pj == NULL)
 	{
-		*pj = AddToPROJSRSCache(fcinfo, proj_cache, srid_from, srid_to);
+		*pj = AddToPROJSRSCache(proj_cache, srid_from, srid_to);
 	}
 
 	return pj != NULL;
@@ -519,21 +565,21 @@ proj_pj_is_latlong(const LWPROJ *pj)
 }
 
 static int
-srid_is_latlong(FunctionCallInfo fcinfo, int32_t srid)
+srid_is_latlong(int32_t srid)
 {
 	LWPROJ *pj;
-	if ( GetPJUsingFCInfo(fcinfo, srid, srid, &pj) == LW_FAILURE)
+	if ( GetLWPROJ(srid, srid, &pj) == LW_FAILURE)
 		return LW_FALSE;
 	return proj_pj_is_latlong(pj);
 }
 
 void
-srid_check_latlong(FunctionCallInfo fcinfo, int32_t srid)
+srid_check_latlong(int32_t srid)
 {
 	if (srid == SRID_DEFAULT || srid == SRID_UNKNOWN)
 		return;
 
-	if (srid_is_latlong(fcinfo, srid))
+	if (srid_is_latlong(srid))
 		return;
 
 	ereport(ERROR, (
@@ -542,7 +588,7 @@ srid_check_latlong(FunctionCallInfo fcinfo, int32_t srid)
 }
 
 srs_precision
-srid_axis_precision(FunctionCallInfo fcinfo, int32_t srid, int precision)
+srid_axis_precision(int32_t srid, int precision)
 {
 	srs_precision sp;
 	sp.precision_xy = precision;
@@ -552,7 +598,7 @@ srid_axis_precision(FunctionCallInfo fcinfo, int32_t srid, int precision)
 	if ( srid == SRID_UNKNOWN )
 		return sp;
 
-	if ( srid_is_latlong(fcinfo, srid) )
+	if ( srid_is_latlong(srid) )
 	{
 		sp.precision_xy += 5;
 		return sp;
@@ -562,14 +608,14 @@ srid_axis_precision(FunctionCallInfo fcinfo, int32_t srid, int precision)
 }
 
 int
-spheroid_init_from_srid(FunctionCallInfo fcinfo, int32_t srid, SPHEROID *s)
+spheroid_init_from_srid(int32_t srid, SPHEROID *s)
 {
 	LWPROJ *pj;
 #if POSTGIS_PROJ_VERSION >= 48 && POSTGIS_PROJ_VERSION < 60
 	double major_axis, minor_axis, eccentricity_squared;
 #endif
 
-	if ( GetPJUsingFCInfo(fcinfo, srid, srid, &pj) == LW_FAILURE)
+	if ( GetLWPROJ(srid, srid, &pj) == LW_FAILURE)
 		return LW_FAILURE;
 
 #if POSTGIS_PROJ_VERSION >= 60
diff --git a/libpgcommon/lwgeom_transform.h b/libpgcommon/lwgeom_transform.h
index a8ecdb0..f2a65e5 100644
--- a/libpgcommon/lwgeom_transform.h
+++ b/libpgcommon/lwgeom_transform.h
@@ -15,6 +15,40 @@
 #include "lwgeom_pg.h"
 
 
+/*
+* Proj4 caching has it's own mechanism, and is
+* stored globally as the cost of proj_create_crs_to_crs()
+* is so high (20-40ms) that the lifetime of fcinfo->flinfo->fn_extra
+* is too short to assist some work loads.
+*/
+
+/* An entry in the PROJ SRS cache */
+typedef struct struct_PROJSRSCacheItem
+{
+	int32_t srid_from;
+	int32_t srid_to;
+	uint64_t hits;
+	LWPROJ *projection;
+}
+PROJSRSCacheItem;
+
+/* PROJ 4 lookup transaction cache methods */
+#define PROJ_CACHE_ITEMS 128
+
+/*
+* The proj4 cache holds a fixed number of reprojection
+* entries. In normal usage we don't expect it to have
+* many entries, so we always linearly scan the list.
+*/
+typedef struct struct_PROJSRSCache
+{
+	PROJSRSCacheItem PROJSRSCache[PROJ_CACHE_ITEMS];
+	uint32_t PROJSRSCacheCount;
+	MemoryContext PROJSRSCacheContext;
+}
+PROJSRSCache;
+
+
 typedef struct srs_precision
 {
 	int precision_xy;
@@ -27,15 +61,13 @@ typedef struct srs_precision
 char *GetProj4String(int32_t srid);
 #endif
 
-/**
- * Opaque type to use in the projection cache API.
- */
-typedef void *ProjCache ;
 
-int GetPJUsingFCInfo(FunctionCallInfo fcinfo, int32_t srid_from, int32_t srid_to, LWPROJ **pj);
-int spheroid_init_from_srid(FunctionCallInfo fcinfo, int32_t srid, SPHEROID *s);
-void srid_check_latlong(FunctionCallInfo fcinfo, int32_t srid);
-srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int32_t srid, int precision);
+/* Prototypes */
+PROJSRSCache* GetPROJSRSCache();
+int GetLWPROJ(int32_t srid_from, int32_t srid_to, LWPROJ **pj);
+int spheroid_init_from_srid(int32_t srid, SPHEROID *s);
+void srid_check_latlong(int32_t srid);
+srs_precision srid_axis_precision(int32_t srid, int precision);
 
 /**
  * Builtin SRID values
diff --git a/postgis/geography_centroid.c b/postgis/geography_centroid.c
index 87ad72f..386e029 100644
--- a/postgis/geography_centroid.c
+++ b/postgis/geography_centroid.c
@@ -82,7 +82,7 @@ Datum geography_centroid(PG_FUNCTION_ARGS)
 	}
 
     /* Initialize spheroid */
-    spheroid_init_from_srid(fcinfo, srid, &s);
+    spheroid_init_from_srid(srid, &s);
 
     /* Set to sphere if requested */
     use_spheroid = PG_GETARG_BOOL(1);
diff --git a/postgis/geography_inout.c b/postgis/geography_inout.c
index c9cad9b..7dae289 100644
--- a/postgis/geography_inout.c
+++ b/postgis/geography_inout.c
@@ -172,7 +172,7 @@ Datum geography_in(PG_FUNCTION_ARGS)
 	}
 
 	/* Error on any SRID != default */
-	srid_check_latlong(fcinfo, lwgeom->srid);
+	srid_check_latlong(lwgeom->srid);
 
 	/* Convert to gserialized */
 	g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
@@ -449,7 +449,7 @@ Datum geography_from_text(PG_FUNCTION_ARGS)
 		PG_PARSER_ERROR(lwg_parser_result);
 
 	/* Error on any SRID != default */
-	srid_check_latlong(fcinfo, lwg_parser_result.geom->srid);
+	srid_check_latlong(lwg_parser_result.geom->srid);
 
 	/* Clean up string */
 	pfree(wkt);
@@ -477,7 +477,7 @@ Datum geography_from_binary(PG_FUNCTION_ARGS)
 		lwpgerror("Unable to parse WKB");
 
 	/* Error on any SRID != default */
-	srid_check_latlong(fcinfo, lwgeom->srid);
+	srid_check_latlong(lwgeom->srid);
 
 	gser = gserialized_geography_from_lwgeom(lwgeom, -1);
 	lwgeom_free(lwgeom);
@@ -501,7 +501,7 @@ Datum geography_from_geometry(PG_FUNCTION_ARGS)
 	}
 
 	/* Error on any SRID != default */
-	srid_check_latlong(fcinfo, lwgeom->srid);
+	srid_check_latlong(lwgeom->srid);
 
 	/* Force the geometry to have valid geodetic coordinate range. */
 	lwgeom_nudge_geodetic(lwgeom);
@@ -565,7 +565,7 @@ Datum geography_recv(PG_FUNCTION_ARGS)
 	lwgeom = lwgeom_from_wkb((uint8_t*)buf->data, buf->len, LW_PARSER_CHECK_ALL);
 
 	/* Error on any SRID != default */
-	srid_check_latlong(fcinfo, lwgeom->srid);
+	srid_check_latlong(lwgeom->srid);
 
 	g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
 
diff --git a/postgis/geography_measurement.c b/postgis/geography_measurement.c
index a975f2f..0d1d388 100644
--- a/postgis/geography_measurement.c
+++ b/postgis/geography_measurement.c
@@ -86,7 +86,7 @@ Datum geography_distance_knn(PG_FUNCTION_ARGS)
 	gserialized_error_if_srid_mismatch(g1, g2, __func__);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -157,7 +157,7 @@ Datum geography_distance_uncached(PG_FUNCTION_ARGS)
 	gserialized_error_if_srid_mismatch(g1, g2, __func__);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -220,7 +220,7 @@ Datum geography_distance(PG_FUNCTION_ARGS)
 	gserialized_error_if_srid_mismatch(g1, g2, __func__);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -288,7 +288,7 @@ Datum geography_dwithin(PG_FUNCTION_ARGS)
 		use_spheroid = PG_GETARG_BOOL(3);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -360,7 +360,7 @@ Datum geography_distance_tree(PG_FUNCTION_ARGS)
 		use_spheroid = PG_GETARG_BOOL(3);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -409,7 +409,7 @@ Datum geography_dwithin_uncached(PG_FUNCTION_ARGS)
 		use_spheroid = PG_GETARG_BOOL(3);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Set to sphere if requested */
 	if ( ! use_spheroid )
@@ -506,7 +506,7 @@ Datum geography_area(PG_FUNCTION_ARGS)
 	use_spheroid = PG_GETARG_BOOL(1);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g), &s);
 
 	lwgeom = lwgeom_from_gserialized(g);
 
@@ -596,7 +596,7 @@ Datum geography_perimeter(PG_FUNCTION_ARGS)
 	use_spheroid = PG_GETARG_BOOL(1);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g), &s);
 
 	/* User requests spherical calculation, turn our spheroid into a sphere */
 	if ( ! use_spheroid )
@@ -647,7 +647,7 @@ Datum geography_length(PG_FUNCTION_ARGS)
 	use_spheroid = PG_GETARG_BOOL(1);
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g), &s);
 
 	/* User requests spherical calculation, turn our spheroid into a sphere */
 	if ( ! use_spheroid )
@@ -984,7 +984,7 @@ Datum geography_project(PG_FUNCTION_ARGS)
 		azimuth = PG_GETARG_FLOAT8(2); /* Azimuth in Radians */
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g), &s);
 
 	/* Handle the zero distance case */
 	if( FP_EQUALS(distance, 0.0) )
@@ -1054,7 +1054,7 @@ Datum geography_azimuth(PG_FUNCTION_ARGS)
 	}
 
 	/* Initialize spheroid */
-	spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s);
+	spheroid_init_from_srid(gserialized_get_srid(g1), &s);
 
 	/* Calculate the direction */
 	azimuth = lwgeom_azumith_spheroid(lwgeom_as_lwpoint(lwgeom1), lwgeom_as_lwpoint(lwgeom2), &s);
diff --git a/postgis/gserialized_estimate.c b/postgis/gserialized_estimate.c
index 71a29a9..b867ed7 100644
--- a/postgis/gserialized_estimate.c
+++ b/postgis/gserialized_estimate.c
@@ -2310,7 +2310,7 @@ Datum gserialized_estimated_extent(PG_FUNCTION_ARGS)
 	int key_type;
 
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	if ( PG_NARGS() == 4 )
 	{
@@ -2577,7 +2577,7 @@ Datum _postgis_gserialized_index_extent(PG_FUNCTION_ARGS)
 	Oid idx_oid;
 
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	idx_oid = table_get_spatial_index(tbl_oid, col, &key_type);
 	if (!idx_oid)
diff --git a/postgis/gserialized_spgist_2d.c b/postgis/gserialized_spgist_2d.c
index 3957075..8ab682b 100644
--- a/postgis/gserialized_spgist_2d.c
+++ b/postgis/gserialized_spgist_2d.c
@@ -292,7 +292,7 @@ PGDLLEXPORT Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS)
 	spgConfigOut *cfg = (spgConfigOut *)PG_GETARG_POINTER(1);
 	Oid boxoid = InvalidOid;
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 	boxoid = postgis_oid(BOX2DFOID);
 
 	cfg->prefixType = boxoid;
diff --git a/postgis/gserialized_spgist_3d.c b/postgis/gserialized_spgist_3d.c
index 61953fa..dfdc8fc 100644
--- a/postgis/gserialized_spgist_3d.c
+++ b/postgis/gserialized_spgist_3d.c
@@ -375,7 +375,7 @@ PGDLLEXPORT Datum gserialized_spgist_config_3d(PG_FUNCTION_ARGS)
 
 	Oid boxoid = InvalidOid;
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 	boxoid = postgis_oid(BOX3DOID);
 
 	cfg->prefixType = boxoid;
diff --git a/postgis/gserialized_spgist_nd.c b/postgis/gserialized_spgist_nd.c
index 1576048..7424783 100644
--- a/postgis/gserialized_spgist_nd.c
+++ b/postgis/gserialized_spgist_nd.c
@@ -268,7 +268,7 @@ PGDLLEXPORT Datum gserialized_spgist_config_nd(PG_FUNCTION_ARGS)
 {
 	spgConfigOut *cfg = (spgConfigOut *)PG_GETARG_POINTER(1);
 	Oid boxoid = InvalidOid;
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 	boxoid = postgis_oid(GIDXOID);
 
 	cfg->prefixType = boxoid;
diff --git a/postgis/gserialized_supportfn.c b/postgis/gserialized_supportfn.c
index ce53816..986e347 100644
--- a/postgis/gserialized_supportfn.c
+++ b/postgis/gserialized_supportfn.c
@@ -283,7 +283,7 @@ Datum postgis_index_supportfn(PG_FUNCTION_ARGS)
 	 * Otherwise it will need look them up dynamically, which only works in the schema where Postgis
 	 * is installed is part of the search path (Trac #4739)
 	 */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	if (IsA(rawreq, SupportRequestSelectivity))
 	{
diff --git a/postgis/gserialized_typmod.c b/postgis/gserialized_typmod.c
index 50bedac..9d81190 100644
--- a/postgis/gserialized_typmod.c
+++ b/postgis/gserialized_typmod.c
@@ -299,7 +299,7 @@ Datum geography_typmod_in(PG_FUNCTION_ARGS)
 	int32 typmod = gserialized_typmod_in(arr, LW_TRUE);
 	int32_t srid = TYPMOD_GET_SRID(typmod);
 	/* Check the SRID is legal (geographic coordinates) */
-	srid_check_latlong(fcinfo, srid);
+	srid_check_latlong(srid);
 
 	PG_RETURN_INT32(typmod);
 }
diff --git a/postgis/lwgeom_in_gml.c b/postgis/lwgeom_in_gml.c
index b9ba261..664ed93 100644
--- a/postgis/lwgeom_in_gml.c
+++ b/postgis/lwgeom_in_gml.c
@@ -117,7 +117,7 @@ Datum geom_from_gml(PG_FUNCTION_ARGS)
 	 * That function requires access to spatial_ref_sys, so in order to have it ready we need to ensure
 	 * the internal cache is initialized
 	 */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 #endif
 	lwgeom = lwgeom_from_gml(xml, xml_size);
 	if ( root_srid != SRID_UNKNOWN )
diff --git a/postgis/lwgeom_inout.c b/postgis/lwgeom_inout.c
index e5e75af..d5b0768 100644
--- a/postgis/lwgeom_inout.c
+++ b/postgis/lwgeom_inout.c
@@ -437,7 +437,7 @@ Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS)
 	geom = PG_GETARG_GSERIALIZED_P(0);
 
 	/* Read sensible precision defaults (about one meter) given the srs */
-	sp = srid_axis_precision(fcinfo, gserialized_get_srid(geom), TWKB_DEFAULT_PRECISION);
+	sp = srid_axis_precision(gserialized_get_srid(geom), TWKB_DEFAULT_PRECISION);
 
 	/* If user specified XY precision, use it */
 	if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
@@ -577,7 +577,7 @@ Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS)
 	}
 
 	/* Read sensible precision defaults (about one meter) given the srs */
-	sp = srid_axis_precision(fcinfo, lwgeom_get_srid(lwcollection_as_lwgeom(col)), TWKB_DEFAULT_PRECISION);
+	sp = srid_axis_precision(lwgeom_get_srid(lwcollection_as_lwgeom(col)), TWKB_DEFAULT_PRECISION);
 
 	/* If user specified XY precision, use it */
 	if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
diff --git a/postgis/lwgeom_out_geobuf.c b/postgis/lwgeom_out_geobuf.c
index b8bc61e..3948f4a 100644
--- a/postgis/lwgeom_out_geobuf.c
+++ b/postgis/lwgeom_out_geobuf.c
@@ -53,7 +53,7 @@ Datum pgis_asgeobuf_transfn(PG_FUNCTION_ARGS)
 	struct geobuf_agg_context *ctx;
 
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
 		elog(ERROR, "pgis_asgeobuf_transfn: called in non-aggregate context");
diff --git a/postgis/lwgeom_out_geojson.c b/postgis/lwgeom_out_geojson.c
index b1e6948..fcedef7 100644
--- a/postgis/lwgeom_out_geojson.c
+++ b/postgis/lwgeom_out_geojson.c
@@ -94,7 +94,7 @@ ST_AsGeoJsonRow(PG_FUNCTION_ARGS)
 	Oid geog_oid = InvalidOid;
 
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 	geom_oid = postgis_oid(GEOMETRYOID);
 	geog_oid = postgis_oid(GEOGRAPHYOID);
 
diff --git a/postgis/lwgeom_out_mvt.c b/postgis/lwgeom_out_mvt.c
index e4e71e5..49ac5f2 100644
--- a/postgis/lwgeom_out_mvt.c
+++ b/postgis/lwgeom_out_mvt.c
@@ -133,7 +133,7 @@ Datum pgis_asmvt_transfn(PG_FUNCTION_ARGS)
 	mvt_agg_context *ctx;
 
 	/* We need to initialize the internal cache to access it later via postgis_oid() */
-	postgis_initialize_cache(fcinfo);
+	postgis_initialize_cache();
 
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
 		elog(ERROR, "%s called in non-aggregate context", __func__);
diff --git a/postgis/lwgeom_transform.c b/postgis/lwgeom_transform.c
index 110543d..fbd3d89 100644
--- a/postgis/lwgeom_transform.c
+++ b/postgis/lwgeom_transform.c
@@ -73,7 +73,8 @@ Datum transform(PG_FUNCTION_ARGS)
 	if ( srid_from == srid_to )
 		PG_RETURN_POINTER(geom);
 
-	if ( GetPJUsingFCInfo(fcinfo, srid_from, srid_to, &pj) == LW_FAILURE )
+	postgis_initialize_cache();
+	if ( GetLWPROJ(srid_from, srid_to, &pj) == LW_FAILURE )
 	{
 		PG_FREE_IF_COPY(geom, 0);
 		elog(ERROR, "ST_Transform: Failure reading projections from spatial_ref_sys.");
@@ -210,7 +211,7 @@ Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
 	if (srid_from != srid_to)
 	{
 		LWPROJ *pj;
-		if (GetPJUsingFCInfo(fcinfo, srid_from, srid_to, &pj) == LW_FAILURE)
+		if (GetLWPROJ(srid_from, srid_to, &pj) == LW_FAILURE)
 		{
 			PG_FREE_IF_COPY(geom, 0);
 			elog(ERROR, "ST_AsKML: Failure reading projections from spatial_ref_sys.");
diff --git a/postgis/postgis_module.c b/postgis/postgis_module.c
index 1cccc88..6a872f8 100644
--- a/postgis/postgis_module.c
+++ b/postgis/postgis_module.c
@@ -61,7 +61,6 @@ void _PG_init(void);
 void
 _PG_init(void)
 {
-
   coreIntHandler = pqsignal(SIGINT, handleInterrupt);
 
 #ifdef WIN32
@@ -69,8 +68,8 @@ _PG_init(void)
   lwgeom_register_interrupt_callback(interruptCallback);
 #endif
 
-    /* install PostgreSQL handlers */
-    pg_install_lwgeom_handlers();
+  /* install PostgreSQL handlers */
+  pg_install_lwgeom_handlers();
 }
 
 /*

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

Summary of changes:
 NEWS                            |   1 +
 liblwgeom/lwgeom_transform.c    |   4 +-
 libpgcommon/lwgeom_cache.c      |  45 ++-------------
 libpgcommon/lwgeom_cache.h      |  46 ++--------------
 libpgcommon/lwgeom_pg.c         | 118 +++++++++++++++++++++++++++++++++++-----
 libpgcommon/lwgeom_pg.h         |   2 +-
 libpgcommon/lwgeom_transform.c  | 104 +++++++++++++++++++++++++----------
 libpgcommon/lwgeom_transform.h  |  48 +++++++++++++---
 postgis/geography_centroid.c    |   2 +-
 postgis/geography_inout.c       |  10 ++--
 postgis/geography_measurement.c |  22 ++++----
 postgis/gserialized_estimate.c  |   4 +-
 postgis/gserialized_spgist_2d.c |   2 +-
 postgis/gserialized_spgist_3d.c |   2 +-
 postgis/gserialized_spgist_nd.c |   2 +-
 postgis/gserialized_supportfn.c |   2 +-
 postgis/gserialized_typmod.c    |   2 +-
 postgis/lwgeom_in_gml.c         |   2 +-
 postgis/lwgeom_inout.c          |   4 +-
 postgis/lwgeom_out_geobuf.c     |   2 +-
 postgis/lwgeom_out_geojson.c    |   2 +-
 postgis/lwgeom_out_mvt.c        |   2 +-
 postgis/lwgeom_transform.c      |   5 +-
 postgis/postgis_module.c        |   5 +-
 24 files changed, 265 insertions(+), 173 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list