[postgis-commits] svn - r3085 - in trunk: lwgeom regress

postgis-commits at postgis.refractions.net postgis-commits at postgis.refractions.net
Thu Oct 9 21:39:55 PDT 2008


Author: pramsey
Date: 2008-10-09 21:39:54 -0700 (Thu, 09 Oct 2008)
New Revision: 3085

Added:
   trunk/lwgeom/lwgeom_geos.h
   trunk/lwgeom/lwgeom_geos_prepared.c
   trunk/lwgeom/lwgeom_geos_prepared.h
Modified:
   trunk/lwgeom/Makefile.in
   trunk/lwgeom/lwgeom_geos_c.c
   trunk/lwgeom/lwgeom_rtree.h
   trunk/lwgeom/lwpostgis.sql.in.c
   trunk/regress/README
   trunk/regress/regress_ogc_prep.sql
   trunk/regress/regress_ogc_prep_expected
Log:
Prepared geometries getting closer to readiness. Integrated into standard functions, regression tests added.



Modified: trunk/lwgeom/Makefile.in
===================================================================
--- trunk/lwgeom/Makefile.in	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/Makefile.in	2008-10-10 04:39:54 UTC (rev 3085)
@@ -38,6 +38,7 @@
 	lwgeom_box2dfloat4.o \
 	lwgeom_chip.o \
 	lwgeom_geos_c.o \
+	lwgeom_geos_prepared.o \
 	lwgeom_svg.o \
 	lwgeom_gml.o \
 	lwgeom_kml.o \

Added: trunk/lwgeom/lwgeom_geos.h
===================================================================
--- trunk/lwgeom/lwgeom_geos.h	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwgeom_geos.h	2008-10-10 04:39:54 UTC (rev 3085)
@@ -0,0 +1,45 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include <string.h>
+
+#include "../postgis_config.h"
+
+#include "postgres.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+
+#include "geos_c.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+#include "profile.h"
+
+#ifdef PROFILE
+#warning PROFILE enabled!
+#endif
+
+/*
+** Public prototypes for GEOS utility functions.
+*/
+
+PG_LWGEOM *GEOS2POSTGIS(GEOSGeom geom, char want3d);
+GEOSGeom POSTGIS2GEOS(PG_LWGEOM *g);
+
+LWGEOM *GEOS2LWGEOM(GEOSGeom geom, char want3d);
+GEOSGeom LWGEOM2GEOS(LWGEOM *g);
+
+POINTARRAY *ptarray_from_GEOSCoordSeq(GEOSCoordSeq cs, char want3d);
+void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
+

Modified: trunk/lwgeom/lwgeom_geos_c.c
===================================================================
--- trunk/lwgeom/lwgeom_geos_c.c	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwgeom_geos_c.c	2008-10-10 04:39:54 UTC (rev 3085)
@@ -1,34 +1,23 @@
-#include "../postgis_config.h"
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
 
-#include "postgres.h"
-#include "utils/array.h"
-#include "utils/builtins.h"
-#include "fmgr.h"
-#include "miscadmin.h"
-
-
-#include "lwgeom_pg.h"
-#include "liblwgeom.h"
-#include "profile.h"
-
-#include "geos_c.h"
+#include "lwgeom_geos.h"
 #include "lwgeom_rtree.h"
+#include "lwgeom_geos_prepared.h"
 
-#include <string.h>
 
-
-#ifdef PROFILE
-#warning PROFILE enabled!
-#endif
-
 /*
- * Define this to have have many notices printed
- * during postgis->geos and geos->postgis conversions
  *
- */
-
-/*
- *
  * WARNING: buffer-based GeomUnion has been disabled due to
  *          limitations in the GEOS code (it would only work
  *	    against polygons)
@@ -52,6 +41,7 @@
 Datum crosses(PG_FUNCTION_ARGS);
 Datum within(PG_FUNCTION_ARGS);
 Datum contains(PG_FUNCTION_ARGS);
+Datum containsproperly(PG_FUNCTION_ARGS);
 Datum covers(PG_FUNCTION_ARGS);
 Datum overlaps(PG_FUNCTION_ARGS);
 Datum isvalid(PG_FUNCTION_ARGS);
@@ -77,14 +67,7 @@
 Datum linemerge(PG_FUNCTION_ARGS);
 Datum coveredby(PG_FUNCTION_ARGS);
 
-
-LWGEOM *GEOS2LWGEOM(GEOSGeom geom, char want3d);
-PG_LWGEOM *GEOS2POSTGIS(GEOSGeom geom, char want3d);
-GEOSGeom POSTGIS2GEOS(PG_LWGEOM *g);
-GEOSGeom LWGEOM2GEOS(LWGEOM *g);
-POINTARRAY *ptarray_from_GEOSCoordSeq(GEOSCoordSeq cs, char want3d);
-
-void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
+/* TODO: move these to a lwgeom_functions_analytic.h */
 int point_in_polygon_rtree(RTREE_NODE **root, int ringCount, LWPOINT *point);
 int point_in_multipolygon_rtree(RTREE_NODE **root, int polyCount, int ringCount, LWPOINT *point);
 int point_in_polygon(LWPOLY *polygon, LWPOINT *point);
@@ -92,6 +75,26 @@
 
 /* PROTOTYPES end */
 
+/*********************************************************************************
+**
+**  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"
+
+
+
 PG_FUNCTION_INFO_V1(postgis_geos_version);
 Datum postgis_geos_version(PG_FUNCTION_ARGS)
 {
@@ -126,7 +129,7 @@
 	static int call=1;
 #endif
 
-#if POSTGIS_DEBUG_LEVEL >= 2
+#if POSTGIS_DEBUG_LEVEL > 0
 	call++;
 	POSTGIS_DEBUGF(2, "GEOS incremental union (call %d)", call);
 #endif
@@ -1402,11 +1405,11 @@
 	RTREE_POLY_CACHE *poly_cache;
 	MemoryContext old_context;
 	bool result;
-
-#ifdef PROFILE
-	profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+	PrepGeomCache *prep_cache;
 #endif
 
+
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
 
@@ -1491,50 +1494,103 @@
 
 	initGEOS(lwnotice, lwnotice);
 
-#ifdef PROFILE
-	profstart(PROF_P2G1);
-#endif
-	g1 = POSTGIS2GEOS(geom1);
-#ifdef PROFILE
-	profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-	profstart(PROF_P2G2);
-#endif
-	g2 = POSTGIS2GEOS(geom2);
-#ifdef PROFILE
-	profstop(PROF_P2G2);
-#endif
+#ifdef PREPARED_GEOM
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
-#ifdef PROFILE
-	profstart(PROF_GRUN);
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+	{
+		g1 = POSTGIS2GEOS(geom2);
+		POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
+		result = GEOSPreparedContains( prep_cache->prepared_geom, g1);
+		GEOSGeom_destroy(g1);
+	}
+	else
 #endif
-	result = GEOSContains(g1,g2);
-#ifdef PROFILE
-	profstop(PROF_GRUN);
-#endif
+	{
+		g1 = POSTGIS2GEOS(geom1);
+		g2 = POSTGIS2GEOS(geom2);
+		POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
+		result = GEOSContains( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
 
-	GEOSGeom_destroy(g1);
-	GEOSGeom_destroy(g2);
-
 	if (result == 2)
 	{
 		elog(ERROR,"GEOS contains() threw an error!");
 		PG_RETURN_NULL(); /* never get here */
 	}
 
-#ifdef PROFILE
-	profstop(PROF_QRUN);
-	profreport("cont",geom1, geom2, NULL);
+	PG_FREE_IF_COPY(geom1, 0);
+	PG_FREE_IF_COPY(geom2, 1);
+
+	PG_RETURN_BOOL(result);
+
+}
+
+PG_FUNCTION_INFO_V1(containsproperly);
+Datum containsproperly(PG_FUNCTION_ARGS)
+{
+	PG_LWGEOM *				geom1;
+	PG_LWGEOM *				geom2;
+	bool 					result;
+	BOX2DFLOAT4 			box1, box2;
+#ifdef PREPARED_GEOM
+	PrepGeomCache *	prep_cache;
 #endif
 
+	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+	geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+	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.
+	*/
+	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+	     getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	{
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
+	}
+
+	initGEOS(lwnotice, lwnotice);
+
+#ifdef PREPARED_GEOM
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+	{
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+		result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
+	}
+	else
+#endif
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSRelatePattern( g1, g2, "T**FF*FF*" );
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
+
+	if (result == 2)
+	{
+		elog(ERROR,"GEOS contains() threw an error!");
+		PG_RETURN_NULL(); /* never get here */
+	}
+
 	PG_FREE_IF_COPY(geom1, 0);
 	PG_FREE_IF_COPY(geom2, 1);
 
 	PG_RETURN_BOOL(result);
 }
 
-
 /*
  * Described at
  * http://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
@@ -1544,19 +1600,15 @@
 {
 	PG_LWGEOM *geom1;
 	PG_LWGEOM *geom2;
-	GEOSGeom g1,g2;
 	bool result;
 	BOX2DFLOAT4 box1, box2;
 	int type1, type2;
 	LWGEOM *lwgeom;
-	/* LWMPOLY *mpoly; */
 	LWPOINT *point;
 	RTREE_POLY_CACHE *poly_cache;
 	MemoryContext old_context;
-	char *patt = "******FF*";
-
-#ifdef PROFILE
-	profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+	PrepGeomCache *prep_cache;
 #endif
 
 	geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -1571,7 +1623,7 @@
 	 * 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);
@@ -1641,49 +1693,40 @@
 
 	initGEOS(lwnotice, lwnotice);
 
-#ifdef PROFILE
-	profstart(PROF_P2G1);
-#endif
-	g1 = POSTGIS2GEOS(geom1);
-#ifdef PROFILE
-	profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-	profstart(PROF_P2G2);
-#endif
-	g2 = POSTGIS2GEOS(geom2);
-#ifdef PROFILE
-	profstop(PROF_P2G2);
-#endif
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
-#ifdef PROFILE
-	profstart(PROF_GRUN);
+#ifdef PREPARED_GEOM
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom2);
+		result = GEOSPreparedCovers( prep_cache->prepared_geom, g1);
+		GEOSGeom_destroy(g1);
+	}
+	else
 #endif
-	result = GEOSRelatePattern(g1,g2,patt);
-#ifdef PROFILE
-	profstop(PROF_GRUN);
-#endif
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSRelatePattern( g1, g2, "******FF*" );
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
 
-	GEOSGeom_destroy(g1);
-	GEOSGeom_destroy(g2);
-
 	if (result == 2)
 	{
 		elog(ERROR,"GEOS covers() threw an error!");
 		PG_RETURN_NULL(); /* never get here */
 	}
 
-#ifdef PROFILE
-	profstop(PROF_QRUN);
-	profreport("geos",geom1, geom2, NULL);
-#endif
-
 	PG_FREE_IF_COPY(geom1, 0);
 	PG_FREE_IF_COPY(geom2, 1);
 
 	PG_RETURN_BOOL(result);
+
 }
 
+
+
 PG_FUNCTION_INFO_V1(within);
 Datum within(PG_FUNCTION_ARGS)
 {
@@ -2053,7 +2096,6 @@
 	PG_LWGEOM *geom1;
 	PG_LWGEOM *geom2;
 	uchar *serialized_poly;
-	GEOSGeom g1,g2;
 	bool result;
 	BOX2DFLOAT4 box1, box2;
 	int type1, type2, polytype;
@@ -2061,9 +2103,8 @@
 	LWGEOM *lwgeom;
 	MemoryContext old_context;
 	RTREE_POLY_CACHE *poly_cache;
-
-#ifdef PROFILE
-	profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+	PrepGeomCache *prep_cache;
 #endif
 
 	geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -2154,42 +2195,40 @@
 	}
 
 	initGEOS(lwnotice, lwnotice);
+#ifdef PREPARED_GEOM
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
 
-#ifdef PROFILE
-	profstart(PROF_P2G1);
+	if ( prep_cache && prep_cache->prepared_geom )
+	{
+		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
 #endif
-	g1 = POSTGIS2GEOS(geom1 );
-#ifdef PROFILE
-	profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-	profstart(PROF_P2G2);
-#endif
-	g2 = POSTGIS2GEOS(geom2 );
-#ifdef PROFILE
-	profstop(PROF_P2G2);
-#endif
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSIntersects( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
 
-#ifdef PROFILE
-	profstart(PROF_GRUN);
-#endif
-	result = GEOSIntersects(g1,g2);
-#ifdef PROFILE
-	profstop(PROF_GRUN);
-#endif
-	GEOSGeom_destroy(g1);
-	GEOSGeom_destroy(g2);
 	if (result == 2)
 	{
 		elog(ERROR,"GEOS intersects() threw an error!");
 		PG_RETURN_NULL(); /* never get here */
 	}
 
-#ifdef PROFILE
-	profstop(PROF_QRUN);
-	profreport("intr",geom1, geom2, NULL);
-#endif
-
 	PG_FREE_IF_COPY(geom1, 0);
 	PG_FREE_IF_COPY(geom2, 1);
 
@@ -3517,734 +3556,3 @@
 
 }
 
-/*********************************************************************************
-**
-**  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 POSTGIS_GEOS_VERSION >= 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
-{
-	PG_LWGEOM*                    pg_geom1;
-	PG_LWGEOM*                    pg_geom2;
-	size_t                        pg_geom1_size;
-	size_t                        pg_geom2_size;
-	int32                         argnum;
-	const GEOSPreparedGeometry*   prepared_geom;
-	const GEOSGeometry*           geom;
-	MemoryContext                 context;
-} PrepGeomCache;
-
-/*
-** 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 *pg_geom1, PG_LWGEOM *pg_geom2);
-
-/* Memory context hash table function prototypes */
-uint32 mcxt_ptr_hasha(const void *key, Size keysize);
-static void 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
-};
-
-static void
-PreparedCacheInit(MemoryContext context)
-{
-	/*
-	 * Do nothing as the cache is initialised when the transform()
-	 * function is first called
-	 */
-}
-
-static void
-PreparedCacheDelete(MemoryContext context)
-{
-	PrepGeomHashEntry* pghe;
-
-	/* Lookup the hash entry pointer in the global hash table so we can free it */
-	pghe = GetPrepGeomHashEntry(context);
-
-	if (!pghe)
-		elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
-
-	POSTGIS_DEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context);
-
-	/* 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)
-{
-	/*
-	 * Do nothing, but we must supply a function since this call is mandatory according to tgl
-	 * (see postgis-devel archives July 2007)
-	 */
-}
-
-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 void
-CreatePrepGeomHash(void)
-{
-	HASHCTL ctl;
-
-	ctl.keysize = sizeof(MemoryContext);
-	ctl.entrysize = sizeof(PrepGeomHashEntry);
-	ctl.hash = mcxt_ptr_hasha;
-
-	PrepGeomHash = 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)
-	{
-		/* 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 *pg_geom1, PG_LWGEOM *pg_geom2)
-{
-	MemoryContext old_context;
-	PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
-	int copy_keys = 1;
-	size_t pg_geom1_size = 0;
-	size_t pg_geom2_size = 0;
-
-	if (!PrepGeomHash)
-		CreatePrepGeomHash();
-
-	if( pg_geom1 ) 
-		pg_geom1_size = VARSIZE(pg_geom1) + VARHDRSZ;
-
-	if( pg_geom2 ) 
-		pg_geom2_size = VARSIZE(pg_geom2) + VARHDRSZ;
-
-	if ( cache == NULL)
-	{
-		/*
-		** Cache hit, but the cache isn't set up yet.
-		** Set it up, but don't prepare the geometry yet.
-		*/
-		PrepGeomHashEntry pghe;
-	
-		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-		cache = palloc(sizeof(PrepGeomCache));		
-		MemoryContextSwitchTo(old_context);
-	
-		cache->prepared_geom = 0;
-		cache->geom = 0;
-		cache->argnum = 0;
-		cache->pg_geom1 = 0;
-		cache->pg_geom2 = 0;
-		cache->pg_geom1_size = 0;
-		cache->pg_geom2_size = 0;
-		cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
-		                 &PreparedCacheContextMethods,
-		                 fcinfo->flinfo->fn_mcxt,
-		                 "PostGIS Prepared Geometry Context");
-
-		POSTGIS_DEBUGF(1, "GetPrepGeomCache: creating cache: %p", cache);
-
-		pghe.context = cache->context;
-		pghe.geom = 0;
-		pghe.prepared_geom = 0;
-		AddPrepGeomHashEntry( pghe );
-
-		fcinfo->flinfo->fn_extra = cache;
-
-		POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache);
-	}
-	else if ( pg_geom1 &&
-	          cache->argnum != 2 &&
-	          cache->pg_geom1_size == pg_geom1_size && 
-	          memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0)
-	{
-		if ( !cache->prepared_geom )
-		{
-			/*
-			** Cache hit, but we haven't prepared our geometry yet.
-			** Prepare it.
-			*/
-			PrepGeomHashEntry* pghe;
-		
-			GEOSGeom g = POSTGIS2GEOS( pg_geom1 );
-			cache->geom = g;
-			cache->prepared_geom = GEOSPrepare( g );
-			cache->argnum = 1;
-			POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 1");
-
-			pghe = GetPrepGeomHashEntry(cache->context);
-			pghe->geom = cache->geom;
-			pghe->prepared_geom = cache->prepared_geom;
-			POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1");
-		}
-		else
-		{	
-			/*
-			** Cache hit, and we're good to go. Do nothing.
-			*/
-			POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 1");
-		}
-		/* we don't need new keys until we have a cache miss */
-		copy_keys = 0;
-	}
-	else if ( pg_geom2 && 
-	          cache->argnum != 1 &&
-	          cache->pg_geom2_size == pg_geom2_size && 
-	          memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0)
-	{
-		 	if ( !cache->prepared_geom )
-			{
-				/*
-				** Cache hit on arg2, but we haven't prepared our geometry yet.
-				** Prepare it.
-				*/
-				PrepGeomHashEntry* pghe;
-				GEOSGeom g = POSTGIS2GEOS( pg_geom2 );
-				cache->geom = g;
-				cache->prepared_geom = GEOSPrepare( g );
-				cache->argnum = 2;
-				POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 2");
-			
-				pghe = GetPrepGeomHashEntry(cache->context);
-				pghe->geom = cache->geom;
-				pghe->prepared_geom = cache->prepared_geom;
-				POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2");
-			}
-			else 
-			{
-				/*
-				** Cache hit, and we're good to go. Do nothing.
-				*/
-					POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 2");
-			}
-			/* we don't need new keys until we have a cache miss */
-			copy_keys = 0;
-	}
-	else if ( cache->prepared_geom )
-	{
-		/*
-		** No cache hits, so this must be a miss.
-		** Destroy the GEOS objects, empty the cache.
-		*/
-		PrepGeomHashEntry* pghe;
-
-		pghe = GetPrepGeomHashEntry(cache->context);
-		pghe->geom = 0;
-		pghe->prepared_geom = 0;
-
-		POSTGIS_DEBUGF(1, "GetPrepGeomCache: cache miss, argument %d", cache->argnum);
-		GEOSPreparedGeom_destroy( cache->prepared_geom );
-		GEOSGeom_destroy( cache->geom );
-		
-		cache->prepared_geom = 0;
-		cache->geom = 0;
-		cache->argnum = 0;
-
-	}
-	
-	if( copy_keys && pg_geom1 ) 
-	{
-		POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom1 into cache");
-		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-		if( cache->pg_geom1 ) 
-			pfree(cache->pg_geom1);
-		cache->pg_geom1 = palloc(pg_geom1_size);
-		MemoryContextSwitchTo(old_context);
-		memcpy(cache->pg_geom1, pg_geom1, pg_geom1_size);
-		cache->pg_geom1_size = pg_geom1_size;
-	}
-	if( copy_keys && pg_geom2 )
-	{ 
-		POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom2 into cache");
-		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-		if( cache->pg_geom2 ) 
-			pfree(cache->pg_geom2);
-		cache->pg_geom2 = palloc(pg_geom2_size);
-		MemoryContextSwitchTo(old_context);
-		memcpy(cache->pg_geom2, pg_geom2, pg_geom2_size);
-		cache->pg_geom2_size = pg_geom2_size;
-	}
-
-	return cache;
-	
-	/* select sum(v.gid) from vada2005 v, ed2000 e where st_contains(e.the_geom, v.the_geom, e.gid) and e.id like 'PR%' */
-
-}
-#endif /* PREPARED_GEOM */
-
-
-PG_FUNCTION_INFO_V1(containsPrepared);
-Datum containsPrepared(PG_FUNCTION_ARGS)
-{
-#ifndef PREPARED_GEOM
-	elog(ERROR,"Not implemented in this version!");
-	PG_RETURN_NULL(); 
-#else
-	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));
-	key1 = PG_GETARG_INT32(2);
-
-	errorIfGeometryCollection(geom1,geom2);
-	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
-	POSTGIS_DEBUG(3, "containsPrepared: entered function");
-
-	/*
-	* 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) )
-	{
-		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-			PG_RETURN_BOOL(FALSE);
-	}
-
-	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-	initGEOS(lwnotice, lwnotice);
-
-	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-	{
-		GEOSGeom g = POSTGIS2GEOS(geom2);
-		POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
-		result = GEOSPreparedContains( prep_cache->prepared_geom, g);
-		GEOSGeom_destroy(g);
-	}
-	else
-	{
-		GEOSGeom g1 = POSTGIS2GEOS(geom1);
-		GEOSGeom g2 = POSTGIS2GEOS(geom2);
-		POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
-		result = GEOSContains( g1, g2);
-		GEOSGeom_destroy(g1);
-		GEOSGeom_destroy(g2);
-	}
-
-	if (result == 2)
-	{
-		elog(ERROR,"GEOS contains() threw an error!");
-		PG_RETURN_NULL(); /* never get here */
-	}
-
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
-	PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
-PG_FUNCTION_INFO_V1(containsProperlyPrepared);
-Datum containsProperlyPrepared(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;
-	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));
-	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.
-	*/
-	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-	{
-		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-			PG_RETURN_BOOL(FALSE);
-	}
-
-	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-	initGEOS(lwnotice, lwnotice);
-
-	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-	{
-		GEOSGeom g = POSTGIS2GEOS(geom2);
-		result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
-		GEOSGeom_destroy(g);
-	}
-	else
-	{
-		GEOSGeom g1 = POSTGIS2GEOS(geom1);
-		GEOSGeom g2 = POSTGIS2GEOS(geom2);
-		result = GEOSRelatePattern( g1, g2, "T**FF*FF*" );
-		GEOSGeom_destroy(g1);
-		GEOSGeom_destroy(g2);
-	}
-
-	if (result == 2)
-	{
-		elog(ERROR,"GEOS contains() threw an error!");
-		PG_RETURN_NULL(); /* never get here */
-	}
-
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
-	PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
-PG_FUNCTION_INFO_V1(coversPrepared);
-Datum coversPrepared(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;
-	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));
-	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.
-	*/
-	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-	{
-		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-			PG_RETURN_BOOL(FALSE);
-	}
-
-	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-	initGEOS(lwnotice, lwnotice);
-
-	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-	{
-		GEOSGeom g = POSTGIS2GEOS(geom2);
-		result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
-		GEOSGeom_destroy(g);
-	}
-	else
-	{
-		GEOSGeom g1 = POSTGIS2GEOS(geom1);
-		GEOSGeom g2 = POSTGIS2GEOS(geom2);
-		result = GEOSRelatePattern( g1, g2, "******FF*" );
-		GEOSGeom_destroy(g1);
-		GEOSGeom_destroy(g2);
-	}
-
-	if (result == 2)
-	{
-		elog(ERROR,"GEOS contains() threw an error!");
-		PG_RETURN_NULL(); /* never get here */
-	}
-
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
-	PG_RETURN_BOOL(result);
-#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;
-	bool 					result;
-	BOX2DFLOAT4 			box1, box2;
-	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));
-	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.
-	*/
-	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-	{
-		if (( box2.xmax < box1.xmin ) || ( box2.xmin > box1.xmax ) ||
-		    ( box2.ymax < box1.ymin ) || ( box2.ymin > box1.ymax ))
-			PG_RETURN_BOOL(FALSE);
-	}
-
-	prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
-
-	initGEOS(lwnotice, lwnotice);
-
-	if ( prep_cache && prep_cache->prepared_geom )
-	{
-		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
-	{
-		GEOSGeom g1 = POSTGIS2GEOS(geom1);
-		GEOSGeom g2 = POSTGIS2GEOS(geom2);
-		result = GEOSIntersects( g1, g2);
-		GEOSGeom_destroy(g1);
-		GEOSGeom_destroy(g2);
-	}
-
-	if (result == 2)
-	{
-		elog(ERROR,"GEOS contains() threw an error!");
-		PG_RETURN_NULL(); /* never get here */
-	}
-
-	PG_FREE_IF_COPY(geom1, 0);
-	PG_FREE_IF_COPY(geom2, 1);
-
-	PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-

Added: trunk/lwgeom/lwgeom_geos_prepared.c
===================================================================
--- trunk/lwgeom/lwgeom_geos_prepared.c	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwgeom_geos_prepared.c	2008-10-10 04:39:54 UTC (rev 3085)
@@ -0,0 +1,679 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2007 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include "lwgeom_geos_prepared.h"
+
+/* 
+** Prepared geometry function prototypes 
+*/
+Datum containsPrepared(PG_FUNCTION_ARGS);
+Datum coversPrepared(PG_FUNCTION_ARGS);
+Datum intersectsPrepared(PG_FUNCTION_ARGS);
+
+/**********************************************************************
+** Internal prototype and structure definitions for the 
+** prepared geometry code.
+**
+** Working parts:
+**
+** PrepGeomCache, the actual struct that holds the keys we compare
+** to determine if our cache is stale, and references to the GEOS
+** objects used in computations.
+**
+** PrepGeomHash, a global hash table that uses a MemoryContext as
+** key and returns a structure holding references to the GEOS
+** objects used in computations.
+**
+** PreparedCacheContextMethods, a set of callback functions that
+** get hooked into a MemoryContext that is in turn used as a 
+** key in the PrepGeomHash.
+**
+** All this is to allow us to clean up external malloc'ed objects
+** (the GEOS Geometry and PreparedGeometry) before the structure
+** that references them (PrepGeomCache) is pfree'd by PgSQL. The
+** methods in the PreparedCacheContext are called just before the
+** function context is freed, allowing us to look up the references
+** in the PrepGeomHash and free them before the function context
+** is freed.
+**/
+
+#ifdef PREPARED_GEOM
+
+#warning COMPILING PREPARED GEOMETRY
+
+/*
+** 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;
+
+/* Memory context hash table function prototypes */
+uint32 mcxt_ptr_hasha(const void *key, Size keysize);
+static void 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
+};
+
+static void
+PreparedCacheInit(MemoryContext context)
+{
+	/*
+	 * Do nothing as the cache is initialised when the transform()
+	 * function is first called
+	 */
+}
+
+static void
+PreparedCacheDelete(MemoryContext context)
+{
+	PrepGeomHashEntry* pghe;
+
+	/* Lookup the hash entry pointer in the global hash table so we can free it */
+	pghe = GetPrepGeomHashEntry(context);
+
+	if (!pghe)
+		elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
+
+	POSTGIS_DEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context);
+
+	/* 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)
+{
+	/*
+	 * Do nothing, but we must supply a function since this call is mandatory according to tgl
+	 * (see postgis-devel archives July 2007)
+	 */
+}
+
+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 void
+CreatePrepGeomHash(void)
+{
+	HASHCTL ctl;
+
+	ctl.keysize = sizeof(MemoryContext);
+	ctl.entrysize = sizeof(PrepGeomHashEntry);
+	ctl.hash = mcxt_ptr_hasha;
+
+	PrepGeomHash = 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)
+	{
+		/* 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 *pg_geom1, PG_LWGEOM *pg_geom2)
+{
+	MemoryContext old_context;
+	PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
+	int copy_keys = 1;
+	size_t pg_geom1_size = 0;
+	size_t pg_geom2_size = 0;
+
+	if (!PrepGeomHash)
+		CreatePrepGeomHash();
+
+	if( pg_geom1 ) 
+		pg_geom1_size = VARSIZE(pg_geom1) + VARHDRSZ;
+
+	if( pg_geom2 ) 
+		pg_geom2_size = VARSIZE(pg_geom2) + VARHDRSZ;
+
+	if ( cache == NULL)
+	{
+		/*
+		** Cache requested, but the cache isn't set up yet.
+		** Set it up, but don't prepare the geometry yet.
+		** That way if the next call is a cache miss we haven't
+		** wasted time preparing a geometry we don't need.
+		*/
+		PrepGeomHashEntry pghe;
+	
+		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+		cache = palloc(sizeof(PrepGeomCache));		
+		MemoryContextSwitchTo(old_context);
+	
+		cache->prepared_geom = 0;
+		cache->geom = 0;
+		cache->argnum = 0;
+		cache->pg_geom1 = 0;
+		cache->pg_geom2 = 0;
+		cache->pg_geom1_size = 0;
+		cache->pg_geom2_size = 0;
+		cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
+		                 &PreparedCacheContextMethods,
+		                 fcinfo->flinfo->fn_mcxt,
+		                 "PostGIS Prepared Geometry Context");
+
+		POSTGIS_DEBUGF(1, "GetPrepGeomCache: creating cache: %p", cache);
+
+		pghe.context = cache->context;
+		pghe.geom = 0;
+		pghe.prepared_geom = 0;
+		AddPrepGeomHashEntry( pghe );
+
+		fcinfo->flinfo->fn_extra = cache;
+
+		POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache);
+	}
+	else if ( pg_geom1 &&
+	          cache->argnum != 2 &&
+	          cache->pg_geom1_size == pg_geom1_size && 
+	          memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0)
+	{
+		if ( !cache->prepared_geom )
+		{
+			/*
+			** Cache hit, but we haven't prepared our geometry yet.
+			** Prepare it.
+			*/
+			PrepGeomHashEntry* pghe;
+		
+			cache->geom = POSTGIS2GEOS( pg_geom1 );
+			cache->prepared_geom = GEOSPrepare( cache->geom );
+			cache->argnum = 1;
+			POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 1");
+
+			pghe = GetPrepGeomHashEntry(cache->context);
+			pghe->geom = cache->geom;
+			pghe->prepared_geom = cache->prepared_geom;
+			POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1");
+		}
+		else
+		{	
+			/*
+			** Cache hit, and we're good to go. Do nothing.
+			*/
+			POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 1");
+		}
+		/* We don't need new keys until we have a cache miss */
+		copy_keys = 0;
+	}
+	else if ( pg_geom2 && 
+	          cache->argnum != 1 &&
+	          cache->pg_geom2_size == pg_geom2_size && 
+	          memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0)
+	{
+		 	if ( !cache->prepared_geom )
+			{
+				/*
+				** Cache hit on arg2, but we haven't prepared our geometry yet.
+				** Prepare it.
+				*/
+				PrepGeomHashEntry* pghe;
+				
+				cache->geom = POSTGIS2GEOS( pg_geom2 );
+				cache->prepared_geom = GEOSPrepare( cache->geom );
+				cache->argnum = 2;
+				POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 2");
+			
+				pghe = GetPrepGeomHashEntry(cache->context);
+				pghe->geom = cache->geom;
+				pghe->prepared_geom = cache->prepared_geom;
+				POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2");
+			}
+			else 
+			{
+				/*
+				** Cache hit, and we're good to go. Do nothing.
+				*/
+				POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 2");
+			}
+			/* We don't need new keys until we have a cache miss */
+			copy_keys = 0;
+	}
+	else if ( cache->prepared_geom )
+	{
+		/*
+		** No cache hits, so this must be a miss.
+		** Destroy the GEOS objects, empty the cache.
+		*/
+		PrepGeomHashEntry* pghe;
+
+		pghe = GetPrepGeomHashEntry(cache->context);
+		pghe->geom = 0;
+		pghe->prepared_geom = 0;
+
+		POSTGIS_DEBUGF(1, "GetPrepGeomCache: cache miss, argument %d", cache->argnum);
+		GEOSPreparedGeom_destroy( cache->prepared_geom );
+		GEOSGeom_destroy( cache->geom );
+		
+		cache->prepared_geom = 0;
+		cache->geom = 0;
+		cache->argnum = 0;
+
+	}
+	
+	if( copy_keys && pg_geom1 ) 
+	{
+		/*
+		** If this is a new key (cache miss) we flip into the function
+		** manager memory context and make a copy. We can't just store a pointer
+		** because this copy will be pfree'd at the end of this function
+		** call.
+		*/
+		POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom1 into cache");
+		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+		if( cache->pg_geom1 ) 
+			pfree(cache->pg_geom1);
+		cache->pg_geom1 = palloc(pg_geom1_size);
+		MemoryContextSwitchTo(old_context);
+		memcpy(cache->pg_geom1, pg_geom1, pg_geom1_size);
+		cache->pg_geom1_size = pg_geom1_size;
+	}
+	if( copy_keys && pg_geom2 )
+	{ 
+		POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom2 into cache");
+		old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+		if( cache->pg_geom2 ) 
+			pfree(cache->pg_geom2);
+		cache->pg_geom2 = palloc(pg_geom2_size);
+		MemoryContextSwitchTo(old_context);
+		memcpy(cache->pg_geom2, pg_geom2, pg_geom2_size);
+		cache->pg_geom2_size = pg_geom2_size;
+	}
+
+	return cache;
+	
+}
+#endif /* PREPARED_GEOM */
+
+
+
+#if 0
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in contains()
+*/
+PG_FUNCTION_INFO_V1(containsPrepared);
+Datum containsPrepared(PG_FUNCTION_ARGS)
+{
+#ifndef PREPARED_GEOM
+	elog(ERROR,"Not implemented in this version!");
+	PG_RETURN_NULL(); 
+#else
+	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));
+	key1 = PG_GETARG_INT32(2);
+
+	errorIfGeometryCollection(geom1,geom2);
+	errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
+
+	POSTGIS_DEBUG(3, "containsPrepared: entered function");
+
+	/*
+	* 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) )
+	{
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
+	}
+
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+	initGEOS(lwnotice, lwnotice);
+
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+	{
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+		POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
+		result = GEOSPreparedContains( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
+	}
+	else
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
+		result = GEOSContains( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
+
+	if (result == 2)
+	{
+		elog(ERROR,"GEOS contains() threw an error!");
+		PG_RETURN_NULL(); /* never get here */
+	}
+
+	PG_FREE_IF_COPY(geom1, 0);
+	PG_FREE_IF_COPY(geom2, 1);
+
+	PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+
+
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in covers()
+*/
+PG_FUNCTION_INFO_V1(coversPrepared);
+Datum coversPrepared(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;
+	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));
+	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.
+	*/
+	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	{
+		if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+		    ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
+	}
+
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+	initGEOS(lwnotice, lwnotice);
+
+	if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+	{
+		GEOSGeom g = POSTGIS2GEOS(geom2);
+		result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
+		GEOSGeom_destroy(g);
+	}
+	else
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSRelatePattern( g1, g2, "******FF*" );
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
+
+	if (result == 2)
+	{
+		elog(ERROR,"GEOS contains() threw an error!");
+		PG_RETURN_NULL(); /* never get here */
+	}
+
+	PG_FREE_IF_COPY(geom1, 0);
+	PG_FREE_IF_COPY(geom2, 1);
+
+	PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+
+
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in intersects()
+*/
+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;
+	bool 					result;
+	BOX2DFLOAT4 			box1, box2;
+	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));
+	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.
+	*/
+	if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+	                getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+	{
+		if (( box2.xmax < box1.xmin ) || ( box2.xmin > box1.xmax ) ||
+		    ( box2.ymax < box1.ymin ) || ( box2.ymin > box1.ymax ))
+			PG_RETURN_BOOL(FALSE);
+	}
+
+	prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
+
+	initGEOS(lwnotice, lwnotice);
+
+	if ( prep_cache && prep_cache->prepared_geom )
+	{
+		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
+	{
+		GEOSGeom g1 = POSTGIS2GEOS(geom1);
+		GEOSGeom g2 = POSTGIS2GEOS(geom2);
+		result = GEOSIntersects( g1, g2);
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+	}
+
+	if (result == 2)
+	{
+		elog(ERROR,"GEOS contains() threw an error!");
+		PG_RETURN_NULL(); /* never get here */
+	}
+
+	PG_FREE_IF_COPY(geom1, 0);
+	PG_FREE_IF_COPY(geom2, 1);
+
+	PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+#endif /* 0 */
+

Added: trunk/lwgeom/lwgeom_geos_prepared.h
===================================================================
--- trunk/lwgeom/lwgeom_geos_prepared.h	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwgeom_geos_prepared.h	2008-10-10 04:39:54 UTC (rev 3085)
@@ -0,0 +1,70 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include "../postgis_config.h"
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "access/hash.h"
+
+#include "geos_c.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+#include "lwgeom_geos.h"
+
+/*
+** GEOS prepared geometry is only available from GEOS 3.1 onwards
+*/
+#if POSTGIS_GEOS_VERSION >= 31
+#define PREPARED_GEOM 
+#endif
+
+/* 
+** Cache structure. We use PG_LWGEOM as keys so no transformations
+** are needed before we memcmp them with other keys. We store the 
+** size to avoid having to calculate the size every time.
+** 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
+{
+	PG_LWGEOM*                    pg_geom1;
+	PG_LWGEOM*                    pg_geom2;
+	size_t                        pg_geom1_size;
+	size_t                        pg_geom2_size;
+	int32                         argnum;
+	const GEOSPreparedGeometry*   prepared_geom;
+	const GEOSGeometry*           geom;
+	MemoryContext                 context;
+} PrepGeomCache;
+
+/* 
+** Get the current cache, given the input geometries.
+** Function will create cache if none exists, and prepare geometries in
+** cache if necessary, or pull an existing cache if possible.
+**
+** If you are only caching one argument (e.g., in contains) supply 0 as the
+** value for pg_geom2.
+*/
+PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *pg_geom1, PG_LWGEOM *pg_geom2);
+
+
+#endif /* PREPARED_GEOM */
+

Modified: trunk/lwgeom/lwgeom_rtree.h
===================================================================
--- trunk/lwgeom/lwgeom_rtree.h	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwgeom_rtree.h	2008-10-10 04:39:54 UTC (rev 3085)
@@ -61,4 +61,6 @@
 void populateCache(RTREE_POLY_CACHE *cache, LWGEOM *lwgeom, uchar *serializedPoly);
 void clearCache(RTREE_POLY_CACHE *cache);
 
+
+
 #endif /* !defined _LIBLWGEOM_H */

Modified: trunk/lwgeom/lwpostgis.sql.in.c
===================================================================
--- trunk/lwgeom/lwpostgis.sql.in.c	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/lwgeom/lwpostgis.sql.in.c	2008-10-10 04:39:54 UTC (rev 3085)
@@ -3875,6 +3875,21 @@
    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
 #endif
 
+#if POSTGIS_GEOS_VERSION >= 31
+-- Availability: 1.4.0
+CREATEFUNCTION _ST_ContainsProperly(geometry,geometry)
+    RETURNS boolean
+    AS 'MODULE_PATHNAME','containsproperly'
+    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- Availability: 1.4.0
+-- Inlines index magic
+CREATEFUNCTION ST_ContainsProperly(geometry,geometry)
+    RETURNS boolean
+    AS 'SELECT $1 && $2 AND _ST_ContainsProperly($1,$2)'
+    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
+#endif
+
 -- Deprecation in 1.2.3
 CREATEFUNCTION overlaps(geometry,geometry)
    RETURNS boolean
@@ -3980,74 +3995,6 @@
     LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -----------------------------------------------------------------------
--- Prepared Geometry Predicates
--- requires GEOS 3.1.0-CAPI-1.5.0 or better
------------------------------------------------------------------------
-
-#if POSTGIS_GEOS_VERSION >= 31
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_ContainsPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','containsPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Contains(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_ContainsPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_ContainsProperlyPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','containsProperlyPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_ContainsProperly(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_ContainsProperlyPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
--- Added for completeness, and to make testing ST_ContainsProperlyPrepared easier
-CREATE OR REPLACE FUNCTION ST_ContainsProperly(geometry,geometry)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND ST_relate($1,$2,''T**FF*FF*'')'
-    LANGUAGE 'SQL' IMMUTABLE; 
-	
--- Availability: 1.4.0
-CREATEFUNCTION _ST_CoversPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','coversPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
- 	
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Covers(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_CoversPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_IntersectsPrepared(geometry,geometry,integer,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','intersectsPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
- 	
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Intersects(geometry,geometry,integer,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_IntersectsPrepared($1,$2,$3,$4)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
-#endif
-
------------------------------------------------------------------------
 -- SVG OUTPUT
 -----------------------------------------------------------------------
 -- Deprecation in 1.2.3

Modified: trunk/regress/README
===================================================================
--- trunk/regress/README	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/regress/README	2008-10-10 04:39:54 UTC (rev 3085)
@@ -9,6 +9,9 @@
       -a -- unaligned columns
       -f | -- use | (pipe) as the field separator between columns
       -t -- output rows only, ie. no table header
+
+   cat file.sql | psql -F\| -t -A > file_expected
+
 3. Edit regress/Makefile adding <testname> to the TESTS variable.
    Any _expected.in files need to be added to the PREPROC variable.
 

Modified: trunk/regress/regress_ogc_prep.sql
===================================================================
--- trunk/regress/regress_ogc_prep.sql	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/regress/regress_ogc_prep.sql	2008-10-10 04:39:54 UTC (rev 3085)
@@ -3,199 +3,270 @@
 ---
 ---
 
-SELECT 'intersects', ST_intersects('LINESTRING(0 10, 0 -10)', p, 0) from ( values 
-('LINESTRING(0 0, 1 1)'),('LINESTRING(0 0, 1 1)'),('LINESTRING(0 0, 1 1)')
-) as v(p);
+SELECT c, ST_Intersects(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('intersects099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'intersects100', ST_intersects('POINT(5 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point on polygon vertex
-SELECT 'intersects101', ST_intersects('POINT(0 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects102', ST_intersects('POINT(-1 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
 -- PIP - point on polygon edge
-SELECT 'intersects103', ST_intersects('POINT(0 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects104', ST_intersects('POINT(0 12)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects105', ST_intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
--- PIP - repeated vertex
-SELECT 'intersects106', ST_intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
+('intersects104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+SELECT c, ST_Contains(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('contains099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'intersects150', ST_intersects('POINT(5 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point on polygon vertex
-SELECT 'intersects151', ST_intersects('POINT(0 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects152', ST_intersects('POINT(-1 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
 -- PIP - point on polygon edge
-SELECT 'intersects153', ST_intersects('POINT(0 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects154', ST_intersects('POINT(0 12)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects155', ST_intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
--- PIP - repeated vertex
-SELECT 'intersects156', ST_intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
+('contains104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
 
-SELECT 'intersects200', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)') 
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'intersects201', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
+SELECT c, ST_Covers(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('covers099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point within polygon
+('covers100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point on polygon vertex
+('covers101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects202', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'intersects203', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
+('covers102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
+-- PIP - point on polygon edge
+('covers103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects204', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects205', ST_intersects(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'intersects206', ST_intersects(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'intersects210', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'intersects211', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
+('covers104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
 
-
+SELECT c, ST_ContainsProperly(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('containsproperly099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'contains100', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'contains101', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
+('containsproperly100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point on polygon vertex
+('containsproperly101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'contains102', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of rect
-SELECT 'contains103', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of rect
-SELECT 'contains103a', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'contains103b', ST_Contains('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of polygon
-SELECT 'contains103c', ST_Contains('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
+('containsproperly102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
+-- PIP - point on polygon edge
+('containsproperly103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'contains104', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'contains105', ST_Contains(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'contains106', ST_Contains(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'contains110', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+('containsproperly104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'intersects105', ST_Intersects(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'intersects106', ST_Intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'intersects107', ST_Intersects(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'intersects108', ST_Intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'contains105', ST_Contains(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'contains106', ST_Contains(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'contains107', ST_Contains(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'contains108', ST_Contains(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'containsproperly105', ST_ContainsProperly(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'containsproperly106', ST_ContainsProperly(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'containsproperly107', ST_ContainsProperly(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'containsproperly108', ST_ContainsProperly(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'covers105', ST_Covers(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'covers106', ST_Covers(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'covers107', ST_Covers(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'covers108', ST_Covers(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+	(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+
+SELECT c, ST_Intersects(p1, p2) AS intersects_p1p2, ST_Intersects(p2, p1) AS intersects_p2p1 FROM
+( VALUES
+('intersects200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('intersects203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('intersects204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('intersects205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('intersects208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('intersects209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_Contains(p1, p2) AS contains_p1p2, ST_Contains(p2, p1) AS contains_p2p1 FROM
+( VALUES
+('contains200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('contains203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('contains204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('contains205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('contains208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('contains209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_ContainsProperly(p1, p2) AS containsproperly_p1p2, ST_ContainsProperly(p2, p1) AS containsproperly_p2p1 FROM
+( VALUES
+('containsproperly200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('containsproperly203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('containsproperly204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('containsproperly205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('containsproperly208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('containsproperly209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_Covers(p1, p2) AS covers_p1p2, ST_Covers(p2, p1) AS covers_p2p1 FROM
+( VALUES
+('covers200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('covers203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('covers204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('covers205', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers206', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers207', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('covers208', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('covers209', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT 'intersects310', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'contains111', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'intersects311', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
+) AS v(p);
 
--- PIP - point within polygon
-SELECT 'containsproperly100', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'containsproperly101', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
--- PIP - point outside polygon
-SELECT 'containsproperly102', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of rect
-SELECT 'containsproperly103', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of rect
-SELECT 'containsproperly103a', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'containsproperly103b', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of polygon
-SELECT 'containsproperly103c', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point in line with polygon edge
-SELECT 'containsproperly104', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'containsproperly105', ST_ContainsProperly(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'containsproperly106', ST_ContainsProperly(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'containsproperly110', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+SELECT 'contains310', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'containsproperly111', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'contains311', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
+) AS v(p);
 
--- Covers cases
-SELECT 'covers100', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+SELECT 'containsproperly310', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'covers101', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'containsproperly311', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
--- PIP - point within polygon
-SELECT 'covers102', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'covers103', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
--- PIP - point outside polygon
-SELECT 'covers104', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'covers105', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point in line with polygon edge
-SELECT 'covers106', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'covers107', ST_Covers(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'covers108', ST_Covers(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
+) AS v(p);
+
+SELECT 'covers310', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
+('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
+) AS v(p);
+SELECT 'covers311', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
+('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
+) AS v(p);
+

Modified: trunk/regress/regress_ogc_prep_expected
===================================================================
--- trunk/regress/regress_ogc_prep_expected	2008-10-10 01:35:58 UTC (rev 3084)
+++ trunk/regress/regress_ogc_prep_expected	2008-10-10 04:39:54 UTC (rev 3085)
@@ -1,143 +1,136 @@
-intersects|t
-intersects|t
-intersects|t
+intersects099|t
 intersects100|t
 intersects101|t
 intersects102|f
 intersects103|t
 intersects104|f
-intersects105|t
-intersects106|f
-intersects150|t
-intersects151|t
-intersects152|f
-intersects153|t
-intersects154|f
-intersects155|t
-intersects156|f
-intersects200|t
-intersects200|t
-intersects200|t
-intersects201|t
-intersects201|t
-intersects201|t
-intersects202|f
-intersects202|f
-intersects202|f
-intersects203|t
-intersects203|t
-intersects203|t
-intersects204|f
-intersects204|f
-intersects204|f
-intersects205|t
-intersects205|t
-intersects205|t
-intersects206|t
-intersects206|t
-intersects206|t
-intersects210|t
-intersects210|t
-intersects210|t
-intersects211|t
-intersects211|t
-intersects211|t
+contains099|t
 contains100|t
-contains100|t
-contains100|t
 contains101|f
-contains101|f
-contains101|f
 contains102|f
-contains102|f
-contains102|f
 contains103|f
-contains103|f
-contains103|f
-contains103a|f
-contains103a|f
-contains103a|f
-contains103b|f
-contains103b|f
-contains103b|f
-contains103c|f
-contains103c|f
-contains103c|f
 contains104|f
-contains104|f
-contains104|f
-contains105|t
-contains105|t
-contains105|t
-contains106|t
-contains106|t
-contains106|t
-contains110|t
-contains110|t
-contains110|t
-contains111|f
-contains111|f
-contains111|f
+covers099|t
+covers100|t
+covers101|t
+covers102|f
+covers103|t
+covers104|f
+containsproperly099|t
 containsproperly100|t
-containsproperly100|t
-containsproperly100|t
 containsproperly101|f
-containsproperly101|f
-containsproperly101|f
 containsproperly102|f
-containsproperly102|f
-containsproperly102|f
 containsproperly103|f
-containsproperly103|f
-containsproperly103|f
-containsproperly103a|f
-containsproperly103a|f
-containsproperly103a|f
-containsproperly103b|f
-containsproperly103b|f
-containsproperly103b|f
-containsproperly103c|f
-containsproperly103c|f
-containsproperly103c|f
 containsproperly104|f
-containsproperly104|f
-containsproperly104|f
+intersects105|t
+intersects105|t
+intersects105|t
+intersects106|t
+intersects106|t
+intersects106|t
+intersects107|f
+intersects107|f
+intersects107|f
+intersects108|f
+intersects108|f
+intersects108|f
+contains105|t
+contains105|t
+contains105|t
+contains106|f
+contains106|f
+contains106|f
+contains107|f
+contains107|f
+contains107|f
+contains108|f
+contains108|f
+contains108|f
 containsproperly105|t
 containsproperly105|t
 containsproperly105|t
-containsproperly106|t
-containsproperly106|t
-containsproperly106|t
-containsproperly110|f
-containsproperly110|f
-containsproperly110|f
-containsproperly111|f
-containsproperly111|f
-containsproperly111|f
-covers100|t
-covers100|t
-covers100|t
-covers101|t
-covers101|t
-covers101|t
-covers102|t
-covers102|t
-covers102|t
-covers103|t
-covers103|t
-covers103|t
-covers104|f
-covers104|f
-covers104|f
+containsproperly106|f
+containsproperly106|f
+containsproperly106|f
+containsproperly107|f
+containsproperly107|f
+containsproperly107|f
+containsproperly108|f
+containsproperly108|f
+containsproperly108|f
 covers105|t
 covers105|t
 covers105|t
 covers106|f
 covers106|f
 covers106|f
-covers107|t
-covers107|t
-covers107|t
-covers108|t
-covers108|t
-covers108|t
+covers107|f
+covers107|f
+covers107|f
+covers108|f
+covers108|f
+covers108|f
+intersects200|t|t
+intersects201|t|t
+intersects202|t|t
+intersects203|t|t
+intersects204|f|f
+intersects205|t|t
+intersects206|t|t
+intersects207|t|t
+intersects208|t|t
+intersects209|f|f
+contains200|t|f
+contains201|t|f
+contains202|t|f
+contains203|f|f
+contains204|f|f
+contains205|t|f
+contains206|t|f
+contains207|t|f
+contains208|f|f
+contains209|f|f
+containsproperly200|t|f
+containsproperly201|t|f
+containsproperly202|f|f
+containsproperly203|f|f
+containsproperly204|f|f
+containsproperly205|t|f
+containsproperly206|t|f
+containsproperly207|f|f
+containsproperly208|f|f
+containsproperly209|f|f
+covers200|t|f
+covers201|t|f
+covers202|t|f
+covers203|f|f
+covers204|f|f
+covers205|t|f
+covers206|t|f
+covers207|t|f
+covers208|f|f
+covers209|f|f
+intersects310|t
+intersects310|t
+intersects310|t
+intersects311|t
+intersects311|t
+intersects311|t
+contains310|t
+contains310|t
+contains310|t
+contains311|f
+contains311|f
+contains311|f
+containsproperly310|f
+containsproperly310|f
+containsproperly310|f
+containsproperly311|f
+containsproperly311|f
+containsproperly311|f
+covers310|t
+covers310|t
+covers310|t
+covers311|t
+covers311|t
+covers311|t



More information about the postgis-commits mailing list