[postgis-commits] svn - r3066 - branches/1.3/lwgeom

postgis-commits at postgis.refractions.net postgis-commits at postgis.refractions.net
Sat Oct 4 22:36:31 PDT 2008


Author: pramsey
Date: 2008-10-04 22:36:31 -0700 (Sat, 04 Oct 2008)
New Revision: 3066

Modified:
   branches/1.3/lwgeom/
   branches/1.3/lwgeom/lwgeom_geos_c.c
   branches/1.3/lwgeom/lwpostgis.sql.in
Log:
Backport prepared geometry handling code for GEOS 3.1+



Property changes on: branches/1.3/lwgeom
___________________________________________________________________
Name: svn:ignore
   - liblwgeom.so*
lwgeom.sql
y.output
lwpostgis.sql
postgis_geos_version.h
   + *.so
liblwgeom.so*
lwgeom.sql
y.output
lwpostgis.sql
postgis_geos_version.h


Modified: branches/1.3/lwgeom/lwgeom_geos_c.c
===================================================================
--- branches/1.3/lwgeom/lwgeom_geos_c.c	2008-10-04 21:29:16 UTC (rev 3065)
+++ branches/1.3/lwgeom/lwgeom_geos_c.c	2008-10-05 05:36:31 UTC (rev 3066)
@@ -3615,100 +3615,403 @@
 
 }
 
+/*********************************************************************************
+**
+**  PreparedGeometry implementations that cache intermediate indexed versions
+**  of geometry in a special MemoryContext for re-used by future function
+**  invocations.
+**
+**  By creating a memory context to hold the GEOS PreparedGeometry and Geometry
+**  and making it a child of the fmgr memory context, we can get the memory held
+**  by the GEOS objects released when the memory context delete callback is
+**  invoked by the parent context.
+**
+*********************************************************************************/
 
+#include "utils/memutils.h"
+#include "executor/spi.h"
+#include "access/hash.h"
+#include "utils/hsearch.h"
+
+/*
+** GEOS prepared geometry is only available from GEOS 3.1 onwards
+*/
+#if GEOS_VERNUM >= 31
+#define PREPARED_GEOM 1
+#warning COMPILING PREPARED GEOMETRY
+#endif
+
+/* Prepared geometry function prototypes */
 Datum containsPrepared(PG_FUNCTION_ARGS);
 Datum containsProperlyPrepared(PG_FUNCTION_ARGS);
 Datum coversPrepared(PG_FUNCTION_ARGS);
 Datum intersectsPrepared(PG_FUNCTION_ARGS);
 
+/* 
+** Cache structure. Keys are unique for a unique geometry, usually the row
+** primary key is passed in. This avoid the need to do a memcmp to test
+** for geometry equality at each function invocation. The argnum gives
+** the number of function arguments we are caching. Intersects requires that
+** both arguments be checked for cacheability, while Contains only requires
+** that the containing argument be checked. Both the Geometry and the 
+** PreparedGeometry have to be cached, because the PreparedGeometry
+** contains a reference to the geometry.
+*/
+#ifdef PREPARED_GEOM
+typedef struct
+{
+	int32					key1;
+	int32					key2;
+	int32                   argnum;
+	const GEOSPreparedGeometry*   prepared_geom;
+	const GEOSGeometry*			geom;
+	MemoryContext			context;
+} PrepGeomCache;
+
 /*
- * GEOS prepared geometry is only available from GEOS 3.1 onwards
- */
-#if GEOS_VERNUM >= 31
-#define PREPARED_GEOM 1
+** Backend prepared hash table
+**
+** The memory context call-backs use a MemoryContext as the parameter
+** so we need to map that over to actual references to GEOS objects to 
+** delete.
+**
+** This hash table stores a key/value pair of MemoryContext/Geom* objects.
+*/
+static HTAB* PrepGeomHash = NULL;
+
+#define PREPARED_BACKEND_HASH_SIZE	32		
+
+typedef struct 
+{
+	MemoryContext context;
+	const GEOSPreparedGeometry* prepared_geom;
+	const GEOSGeometry* geom;
+} PrepGeomHashEntry;
+
+/* Utility function to pull or prepare the current cache */
+PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *serialized_geom1, PG_LWGEOM *serialized_geom2, int32 key1, int32 key2);
+
+/* Memory context hash table function prototypes */
+uint32 mcxt_ptr_hasha(const void *key, Size keysize);
+static HTAB* CreatePrepGeomHash(void);
+static void AddPrepGeomHashEntry(PrepGeomHashEntry pghe);
+static PrepGeomHashEntry *GetPrepGeomHashEntry(MemoryContext mcxt);
+static void DeletePrepGeomHashEntry(MemoryContext mcxt);
+
+
+/* Memory context cache function prototypes */
+static void PreparedCacheInit(MemoryContext context);
+static void PreparedCacheReset(MemoryContext context);
+static void PreparedCacheDelete(MemoryContext context);
+static bool PreparedCacheIsEmpty(MemoryContext context);
+static void PreparedCacheStats(MemoryContext context, int level);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void PreparedCacheCheck(MemoryContext context);
 #endif
 
+/* Memory context definition must match the current version of PostgreSQL */
+static MemoryContextMethods PreparedCacheContextMethods = {
+	NULL,
+	NULL,
+	NULL,
+	PreparedCacheInit,
+	PreparedCacheReset,
+	PreparedCacheDelete,
+	NULL,
+	PreparedCacheIsEmpty,
+	PreparedCacheStats
+#ifdef MEMORY_CONTEXT_CHECKING
+	, PreparedCacheCheck
+#endif
+};
 
-#ifdef PREPARED_GEOM
+static void
+PreparedCacheInit(MemoryContext context)
+{
+	/*
+	 * Do nothing as the cache is initialised when the transform()
+	 * function is first called
+	 */
+}
 
-typedef struct
+static void
+PreparedCacheDelete(MemoryContext context)
 {
-	int32					key;
-	GEOSPreparedGeometry * 	prepared_geom;
-} PREPARED_GEOM_CACHE;
+	PrepGeomHashEntry* pghe;
 
+	/* Lookup the hash entry pointer in the global hash table so we can free it */
+	pghe = GetPrepGeomHashEntry(context);
 
-/* 
- * get cached prepared geometry for geom1
- * only geom1 is potentially prepared as only
- * the first arg of the prepared predicates CAN be prepared
- * briefly, the following does:
- *
- * get cache
- * if cache not exist
- *   create cache 
- *   key into cache
- *
- * else if key matches cached key
- *    if cached prepared not exist
- *        geom1 prepared into cache
- *    
- * else
- *   key into cache
- *   clear prepared cache
- */
-PREPARED_GEOM_CACHE *
-get_prepared_geometry_cache( 
-	PREPARED_GEOM_CACHE * 	cache, 
-	uchar * 				serialized_geom,
-	int32					key )
+	if (!pghe)
+		elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
+#ifdef PGIS_DEBUG
+	lwnotice("deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context);
+#endif
+	/* Free them */
+	if( pghe->prepared_geom )
+		GEOSPreparedGeom_destroy( pghe->prepared_geom );
+	if( pghe->geom )
+		GEOSGeom_destroy( pghe->geom );
+
+	/* Remove the hash entry as it is no longer needed */
+	DeletePrepGeomHashEntry(context);
+}
+
+static void
+PreparedCacheReset(MemoryContext context)
 {
-	GEOSGeom g;
+	/*
+	 * Do nothing, but we must supply a function since this call is mandatory according to tgl
+	 * (see postgis-devel archives July 2007)
+	 */
+}
 
-	if ( !cache )
+static bool
+PreparedCacheIsEmpty(MemoryContext context)
+{
+	/*
+	 * Always return false since this call is mandatory according to tgl
+	 * (see postgis-devel archives July 2007)
+	 */
+	return FALSE;
+}
+
+static void
+PreparedCacheStats(MemoryContext context, int level)
+{
+	/*
+	 * Simple stats display function - we must supply a function since this call is mandatory according to tgl
+	 * (see postgis-devel archives July 2007)
+	 */
+
+	fprintf(stderr, "%s: Prepared context\n", context->name);
+}
+
+#ifdef MEMORY_CONTEXT_CHECKING
+static void
+PreparedCacheCheck(MemoryContext context)
+{
+	/*
+	 * Do nothing - stub required for when PostgreSQL is compiled
+	 * with MEMORY_CONTEXT_CHECKING defined
+	 */
+}
+#endif
+
+/* TODO: put this in common are for both transform and prepared
+** mcxt_ptr_hash
+** Build a key from a pointer and a size value.
+*/
+uint32 
+mcxt_ptr_hasha(const void *key, Size keysize)
+{
+	uint32 hashval;
+
+	hashval = DatumGetUInt32(hash_any(key, keysize));
+
+	return hashval;
+}
+
+static HTAB*
+CreatePrepGeomHash(void)
+{
+	HASHCTL ctl;
+
+	ctl.keysize = sizeof(MemoryContext);
+	ctl.entrysize = sizeof(PrepGeomHashEntry);
+	ctl.hash = mcxt_ptr_hasha;
+
+	return hash_create("PostGIS Prepared Geometry Backend MemoryContext Hash", PREPARED_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
+}
+
+static void 
+AddPrepGeomHashEntry(PrepGeomHashEntry pghe)
+{
+	bool found;
+	void **key;
+	PrepGeomHashEntry *he;
+
+	/* The hash key is the MemoryContext pointer */
+	key = (void *)&(pghe.context);
+	
+	he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_ENTER, &found);
+	if (!found)
 	{
-		#ifdef PGIS_DEBUG
-		lwnotice( "get_prepared_geometry_cache: creating cache: %x", cache);
-		#endif
-		
-		cache = lwalloc( sizeof( PREPARED_GEOM_CACHE ));
-		
+		/* Insert the entry into the new hash element */
+		he->context = pghe.context;
+		he->geom = pghe.geom;
+		he->prepared_geom = pghe.prepared_geom;
+	}
+	else
+	{
+		elog(ERROR, "AddPrepGeomHashEntry: This memory context is already in use! (%p)", (void *)pghe.context);
+	}
+}
+
+static PrepGeomHashEntry*
+GetPrepGeomHashEntry(MemoryContext mcxt)
+{
+	void **key;
+	PrepGeomHashEntry *he;
+
+	/* The hash key is the MemoryContext pointer */
+	key = (void *)&mcxt;
+
+	/* Return the projection object from the hash */
+	he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_FIND, NULL);
+
+	return he;
+}
+
+
+static void 
+DeletePrepGeomHashEntry(MemoryContext mcxt)
+{
+	void **key;
+	PrepGeomHashEntry *he;	
+
+	/* The hash key is the MemoryContext pointer */
+	key = (void *)&mcxt;
+
+	/* Delete the projection object from the hash */
+	he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_REMOVE, NULL);
+
+	he->prepared_geom = NULL;
+	he->geom = NULL;
+
+	if (!he)
+		elog(ERROR, "DeletePrepGeomHashEntry: There was an error removing the geometry object from this MemoryContext (%p)", (void *)mcxt);
+}
+
+/*
+** GetPrepGeomCache
+**
+** Pull the current prepared geometry from the cache or make
+** one if there is not one available. Only prepare geometry
+** if we are seeing a key for the second time. That way rapidly 
+** cycling keys don't cause too much preparing.
+**
+*/
+PrepGeomCache* 
+GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *serialized_geom1, PG_LWGEOM *serialized_geom2, int32 key1, int32 key2)
+{
+	PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
+
+	if (!PrepGeomHash)
+		PrepGeomHash = CreatePrepGeomHash();
+
+	if ( cache == NULL)
+	{
+		PrepGeomHashEntry pghe;
+		MemoryContext old_context;
+	
+		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+		cache = palloc(sizeof(PrepGeomCache));		
+		MemoryContextSwitchTo(old_context);
+	
 		cache->prepared_geom = 0;
-		cache->key = key;
+		cache->geom = 0;
+		cache->argnum = 0;
+		cache->key1 = 0;
+		cache->key2 = 0;
+		cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
+		                 &PreparedCacheContextMethods,
+		                 fcinfo->flinfo->fn_mcxt,
+		                 "PostGIS Prepared Geometry Context");
+#ifdef PGIS_DEBUG
+		lwnotice("GetPrepGeomCache: creating cache: %x", cache);
+#endif
+		pghe.context = cache->context;
+		pghe.geom = 0;
+		pghe.prepared_geom = 0;
+		AddPrepGeomHashEntry( pghe );
+
+		fcinfo->flinfo->fn_extra = cache;
+#ifdef PGIS_DEBUG
+		lwnotice("GetPrepGeomCache: adding context to hash: %x", cache);
+#endif
 	}
-	else if ( cache->key == key )
+	else if ( cache->key1 == key1 )
 	{
 		if ( !cache->prepared_geom )
 		{
-			#ifdef PGIS_DEBUG
-			lwnotice("get_prepared_geometry_cache: preparing obj");
-			#endif
+			PrepGeomHashEntry* pghe;
 			
-			g = POSTGIS2GEOS( serialized_geom);
-			cache->prepared_geom = GEOSPrepare( g);
+			GEOSGeom g = POSTGIS2GEOS( serialized_geom1 );
+			cache->geom = g;
+			cache->prepared_geom = GEOSPrepare( g );
+			cache->argnum = 1;
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: preparing obj in argument 1");
+#endif
+			pghe = GetPrepGeomHashEntry(cache->context);
+			pghe->geom = cache->geom;
+			pghe->prepared_geom = cache->prepared_geom;
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: storing references to prepared obj in argument 1");
+#endif
+
 		}
 		else
 		{
-			#ifdef PGIS_DEBUG
-			lwnotice("get_prepared_geometry_cache: prepared obj in cache");
-			#endif
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: prepared obj 1 in cache");
+#endif
 		}
 	}
-	else
+	else if ( key2 && cache->key2 == key2 )
 	{
-		#ifdef PGIS_DEBUG
-		lwnotice("get_prepared_geometry_cache: obj NOT in cache");
-		#endif
+		if ( !cache->prepared_geom )
+		{
+			PrepGeomHashEntry* pghe;
 
-		GEOSPreparedGeom_destroy( cache->prepared_geom);
+			GEOSGeom g = POSTGIS2GEOS( serialized_geom2 );
+			cache->geom = g;
+			cache->prepared_geom = GEOSPrepare( g );
+			cache->argnum = 2;
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: preparing obj in argument 2");
+#endif			
+			pghe = GetPrepGeomHashEntry(cache->context);
+			pghe->geom = cache->geom;
+			pghe->prepared_geom = cache->prepared_geom;
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: storing references to prepared obj in argument 2");
+#endif
 
+		}
+		else
+		{
+#ifdef PGIS_DEBUG
+			lwnotice("GetPrepGeomCache: prepared obj 2 in cache");
+#endif
+		}
+	}
+	else if ( cache->prepared_geom )
+	{
+		PrepGeomHashEntry* pghe;
+
+		pghe = GetPrepGeomHashEntry(cache->context);
+		pghe->geom = 0;
+		pghe->prepared_geom = 0;
+
+#ifdef PGIS_DEBUG
+		lwnotice("GetPrepGeomCache: obj NOT in cache, deleting prepared geometries");
+#endif
+		GEOSPreparedGeom_destroy( cache->prepared_geom );
+		GEOSGeom_destroy( cache->geom );
+		
 		cache->prepared_geom = 0;
-		cache->key = key;
+		cache->geom = 0;
+		cache->argnum = 0;
+
 	}
-	
+
+	cache->key1 = key1;
+	cache->key2 = key2;
+
 	return cache;
 }
-
 #endif /* PREPARED_GEOM */
 
 
@@ -3717,68 +4020,63 @@
 {
 #ifndef PREPARED_GEOM
 	elog(ERROR,"Not implemented in this version!");
-	PG_RETURN_NULL(); /* never get here */
-
+	PG_RETURN_NULL(); 
 #else
-	PG_LWGEOM *				geom1;
-	PG_LWGEOM *				geom2;
-	GEOSGeom 				g1, g2;
-	GEOSPreparedGeometry * 	pg;
-	bool 					result;
-	BOX2DFLOAT4 			box1, box2;
-	PREPARED_GEOM_CACHE *	prep_cache;
-	MemoryContext 			old_context;
-	int32					surrogate_key;
+	PG_LWGEOM *              geom1;
+	PG_LWGEOM *              geom2;
+	bool                     result;
+	BOX2DFLOAT4              box1, box2;
+	PrepGeomCache *          prep_cache;
+	int32                    key1;
 
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-	
-	surrogate_key = PG_GETARG_INT32(2);
-	
+	key1 = PG_GETARG_INT32(2);
+
 	errorIfGeometryCollection(geom1,geom2);
 	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
+#ifdef PGIS_DEBUG
+	lwnotice("containsPrepared: entered function");
+#endif
 	/*
-	 * short-circuit: if geom2 bounding box is not completely inside
-	 * geom1 bounding box we can prematurely return FALSE.
-	 * Do the test IFF BOUNDING BOX AVAILABLE.
-	 */
+	* short-circuit: if geom2 bounding box is not completely inside
+	* geom1 bounding box we can prematurely return FALSE.
+	* Do the test IFF BOUNDING BOX AVAILABLE.
+	*/
 	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-		getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
 	{
-		if ( box2.xmin < box1.xmin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.xmax > box1.xmax ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymin < box1.ymin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymax > box1.ymax ) PG_RETURN_BOOL(FALSE);
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		                ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
 	}
-	
-	old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-	prep_cache =  fcinfo->flinfo->fn_extra;
+#ifdef PGIS_DEBUG
+	lwnotice("containsPrepared: calling for prep_cache with key1 = %d", key1);
+#endif
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
 
-	prep_cache = get_prepared_geometry_cache( prep_cache, geom1, surrogate_key);
-
-	fcinfo->flinfo->fn_extra = prep_cache;
-	MemoryContextSwitchTo(old_context);
-
 	initGEOS(lwnotice, lwnotice);
 
-	g2 = POSTGIS2GEOS(geom2);
-
-	if ( !prep_cache || !prep_cache->prepared_geom )
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
 	{
-		g1 = POSTGIS2GEOS(geom1);
-
-		result = GEOSContains( g1, g2);
-		
-		GEOSGeom_destroy(g1);
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+#ifdef PGIS_DEBUG
+		lwnotice("containsPrepared: cache is live, running preparedcontains");
+#endif
+		result = GEOSPreparedContains( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
 	}
 	else
 	{
-		pg = prep_cache->prepared_geom;
-
-		result = GEOSPreparedContains( pg, g2);	
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+#ifdef PGIS_DEBUG
+		lwnotice("containsPrepared: cache is not ready, running standard contains");
+#endif
+		result = GEOSContains( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
 	}
-	GEOSGeom_destroy(g2);
 
 	if (result == 2)
 	{
@@ -3799,67 +4097,52 @@
 #ifndef PREPARED_GEOM
 	elog(ERROR,"Not implemented in this version!");
 	PG_RETURN_NULL(); /* never get here */
-
 #else
 	PG_LWGEOM *				geom1;
 	PG_LWGEOM *				geom2;
-	GEOSGeom 				g1, g2;
-	GEOSPreparedGeometry * 	pg;
 	bool 					result;
 	BOX2DFLOAT4 			box1, box2;
-	PREPARED_GEOM_CACHE *	prep_cache;
-	MemoryContext 			old_context;
-	int32					surrogate_key;
+	PrepGeomCache *	prep_cache;
+	int32					key1;
 
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-	
-	surrogate_key = PG_GETARG_INT32(2);
-	
+	key1 = PG_GETARG_INT32(2);
+
 	errorIfGeometryCollection(geom1,geom2);
 	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
 
 	/*
-	 * short-circuit: if geom2 bounding box is not completely inside
-	 * geom1 bounding box we can prematurely return FALSE.
-	 * Do the test IFF BOUNDING BOX AVAILABLE.
-	 */
+	* short-circuit: if geom2 bounding box is not completely inside
+	* geom1 bounding box we can prematurely return FALSE.
+	* Do the test IFF BOUNDING BOX AVAILABLE.
+	*/
 	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-		getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
 	{
-		if ( box2.xmin < box1.xmin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.xmax > box1.xmax ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymin < box1.ymin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymax > box1.ymax ) PG_RETURN_BOOL(FALSE);
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		                ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
 	}
-	
-	old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-	prep_cache =  fcinfo->flinfo->fn_extra;
-	
-	prep_cache = get_prepared_geometry_cache( prep_cache, geom1, surrogate_key);
-	
-	fcinfo->flinfo->fn_extra = prep_cache;
-	MemoryContextSwitchTo( old_context);
 
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
+
 	initGEOS(lwnotice, lwnotice);
-		
-	g2 = POSTGIS2GEOS(geom2);
 
-	if ( !prep_cache || !prep_cache->prepared_geom )
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
 	{
-		g1 = POSTGIS2GEOS(geom1);
-		
-		result = GEOSRelatePattern( g1, g2, "T**FF*FF*");
-		
-		GEOSGeom_destroy(g1);
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+		result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
 	}
 	else
 	{
-		pg = prep_cache->prepared_geom;
-
-		result = GEOSPreparedContainsProperly( pg, g2);	
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSRelatePattern( g1, g2, "T**FF*FF*" );
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
 	}
-	GEOSGeom_destroy(g2);
 
 	if (result == 2)
 	{
@@ -3880,67 +4163,52 @@
 #ifndef PREPARED_GEOM
 	elog(ERROR,"Not implemented in this version!");
 	PG_RETURN_NULL(); /* never get here */
-
 #else
 	PG_LWGEOM *				geom1;
 	PG_LWGEOM *				geom2;
-	GEOSGeom 				g1, g2;
-	GEOSPreparedGeometry * 	pg;
 	bool 					result;
 	BOX2DFLOAT4 			box1, box2;
-	PREPARED_GEOM_CACHE *	prep_cache;
-	MemoryContext 			old_context;
-	int32					surrogate_key;
+	PrepGeomCache *	prep_cache;
+	int32					key1;
 
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-	
-	surrogate_key = PG_GETARG_INT32(2);
-	
+	key1 = PG_GETARG_INT32(2);
+
 	errorIfGeometryCollection(geom1,geom2);
 	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
 
 	/*
-	 * short-circuit: if geom2 bounding box is not completely inside
-	 * geom1 bounding box we can prematurely return FALSE.
-	 * Do the test IFF BOUNDING BOX AVAILABLE.
-	 */
+	* short-circuit: if geom2 bounding box is not completely inside
+	* geom1 bounding box we can prematurely return FALSE.
+	* Do the test IFF BOUNDING BOX AVAILABLE.
+	*/
 	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-		getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
 	{
-		if ( box2.xmin < box1.xmin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.xmax > box1.xmax ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymin < box1.ymin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymax > box1.ymax ) PG_RETURN_BOOL(FALSE);
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		                ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
 	}
-	
-	old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-	prep_cache =  fcinfo->flinfo->fn_extra;
 
-	prep_cache = get_prepared_geometry_cache( prep_cache, geom1, surrogate_key);
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0, key1, 0 );
 
-	fcinfo->flinfo->fn_extra = prep_cache;
-	MemoryContextSwitchTo(old_context);
-
 	initGEOS(lwnotice, lwnotice);
 
-	g2 = POSTGIS2GEOS(geom2);
-
-	if ( !prep_cache || !prep_cache->prepared_geom )
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
 	{
-		g1 = POSTGIS2GEOS(geom1);
-
-		result = GEOSRelatePattern( g1, g2, "******FF*");
-		
-		GEOSGeom_destroy(g1);
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+		result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
 	}
 	else
 	{
-		pg = prep_cache->prepared_geom;
-
-		result = GEOSPreparedCovers( pg, g2);	
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSRelatePattern( g1, g2, "******FF*" );
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
 	}
-	GEOSGeom_destroy(g2);
 
 	if (result == 2)
 	{
@@ -3955,73 +4223,69 @@
 #endif /* PREPARED_GEOM */
 }
 
+
 PG_FUNCTION_INFO_V1(intersectsPrepared);
 Datum intersectsPrepared(PG_FUNCTION_ARGS)
 {
 #ifndef PREPARED_GEOM
 	elog(ERROR,"Not implemented in this version!");
 	PG_RETURN_NULL(); /* never get here */
-
 #else
 	PG_LWGEOM *				geom1;
 	PG_LWGEOM *				geom2;
-	GEOSGeom 				g1, g2;
-	GEOSPreparedGeometry * 	pg;
 	bool 					result;
 	BOX2DFLOAT4 			box1, box2;
-	PREPARED_GEOM_CACHE *	prep_cache;
-	MemoryContext 			old_context;
-	int32					surrogate_key;
+	PrepGeomCache *	prep_cache;
+	int32					key1, key2;
 
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-	
-	surrogate_key = PG_GETARG_INT32(2);
-	
+	key1 = PG_GETARG_INT32(2);
+	key2 = PG_GETARG_INT32(3);
+
 	errorIfGeometryCollection(geom1,geom2);
 	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
 
 	/*
-	 * short-circuit 1: if geom2 bounding box does not overlap
-	 * geom1 bounding box we can prematurely return FALSE.
-	 * Do the test IFF BOUNDING BOX AVAILABLE.
-	 */
+	* short-circuit 1: if geom2 bounding box does not overlap
+	* geom1 bounding box we can prematurely return FALSE.
+	* Do the test IFF BOUNDING BOX AVAILABLE.
+	*/
 	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-		getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
 	{
-		if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(FALSE);
-		if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(FALSE);
+		if (( box2.xmax < box1.xmin ) || ( box2.xmin > box1.xmax ) ||
+		                ( box2.ymax < box1.ymin ) || ( box2.ymin > box2.ymax ))
+			PG_RETURN_BOOL(FALSE);
 	}
-	
-	old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-	prep_cache =  fcinfo->flinfo->fn_extra;
 
-	prep_cache = get_prepared_geometry_cache( prep_cache, geom1, surrogate_key);
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2, key1, key2 );
 
-	fcinfo->flinfo->fn_extra = prep_cache;
-	MemoryContextSwitchTo(old_context);
-
 	initGEOS(lwnotice, lwnotice);
 
-	g2 = POSTGIS2GEOS(geom2);
-
-	if ( !prep_cache || !prep_cache->prepared_geom )
+	if ( prep_cache && prep_cache->prepared_geom )
 	{
-		g1 = POSTGIS2GEOS(geom1);
-
-		result = GEOSIntersects( g1, g2);
-		
-		GEOSGeom_destroy(g1);
+		if ( prep_cache->argnum == 1 )
+		{
+			GEOSGeom g = POSTGIS2GEOS(geom2);
+			result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+			GEOSGeom_destroy(g);
+		}
+		else
+		{
+			GEOSGeom g = POSTGIS2GEOS(geom1);
+			result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+			GEOSGeom_destroy(g);
+		}
 	}
 	else
 	{
-		pg = prep_cache->prepared_geom;
-
-		result = GEOSPreparedIntersects( pg, g2);	
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSIntersects( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
 	}
-	GEOSGeom_destroy(g2);
 
 	if (result == 2)
 	{
@@ -4035,4 +4299,3 @@
 	PG_RETURN_BOOL(result);
 #endif /* PREPARED_GEOM */
 }
-

Modified: branches/1.3/lwgeom/lwpostgis.sql.in
===================================================================
--- branches/1.3/lwgeom/lwpostgis.sql.in	2008-10-04 21:29:16 UTC (rev 3065)
+++ branches/1.3/lwgeom/lwpostgis.sql.in	2008-10-05 05:36:31 UTC (rev 3066)
@@ -4711,16 +4711,16 @@
     LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
 
 -- Availability: 1.4.0
-CREATEFUNCTION _ST_IntersectsPrepared(geometry,geometry,integer)
+CREATEFUNCTION _ST_IntersectsPrepared(geometry,geometry,integer,integer)
     RETURNS boolean
     AS '@MODULE_FILENAME@','intersectsPrepared'
     LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
  	
 -- Availability: 1.4.0
 -- Inlines index magic
-CREATEFUNCTION ST_Intersects(geometry,geometry,integer)
+CREATEFUNCTION ST_Intersects(geometry,geometry,integer,integer)
     RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_IntersectsPrepared($1,$2,$3)'
+    AS 'SELECT $1 && $2 AND _ST_IntersectsPrepared($1,$2,$3,$4)'
     LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
 
 #endif



More information about the postgis-commits mailing list