lwgeom_box2dfloat4.o \
lwgeom_chip.o \
lwgeom_geos_c.o \
+ lwgeom_geos_prepared.o \
lwgeom_svg.o \
lwgeom_gml.o \
lwgeom_kml.o \
--- /dev/null
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey@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);
+
-#include "../postgis_config.h"
-
-#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"
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey@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 "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
- *
- */
/*
*
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);
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);
/* 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)
{
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
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));
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);
-#endif
- result = GEOSContains(g1,g2);
-#ifdef PROFILE
- profstop(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
-
- GEOSGeom_destroy(g1);
- GEOSGeom_destroy(g2);
+ {
+ 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);
+ }
if (result == 2)
{
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
{
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));
* 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);
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);
-#endif
- result = GEOSRelatePattern(g1,g2,patt);
-#ifdef PROFILE
- profstop(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
-
- GEOSGeom_destroy(g1);
- GEOSGeom_destroy(g2);
+ {
+ GEOSGeom g1 = POSTGIS2GEOS(geom1);
+ GEOSGeom g2 = POSTGIS2GEOS(geom2);
+ result = GEOSRelatePattern( g1, g2, "******FF*" );
+ GEOSGeom_destroy(g1);
+ GEOSGeom_destroy(g2);
+ }
if (result == 2)
{
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)
{
PG_LWGEOM *geom1;
PG_LWGEOM *geom2;
uchar *serialized_poly;
- GEOSGeom g1,g2;
bool result;
BOX2DFLOAT4 box1, box2;
int type1, type2, polytype;
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));
}
initGEOS(lwnotice, lwnotice);
+#ifdef PREPARED_GEOM
+ prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
-#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);
+ 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
+ {
+ 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);
}
-/*********************************************************************************
-**
-** 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 */
-}
-
--- /dev/null
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2007 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey@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 */
+
--- /dev/null
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey@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 */
+
void populateCache(RTREE_POLY_CACHE *cache, LWGEOM *lwgeom, uchar *serializedPoly);\r
void clearCache(RTREE_POLY_CACHE *cache);\r
\r
+\r
+\r
#endif /* !defined _LIBLWGEOM_H */\r
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
AS 'MODULE_PATHNAME','geomequals'
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
-----------------------------------------------------------------------
-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.
---
---
-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);
-
-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);
+('contains104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+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);
+
-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
-contains100|t
-contains100|t
+contains099|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
-containsproperly100|t
-containsproperly100|t
+covers099|t
+covers100|t
+covers101|t
+covers102|f
+covers103|t
+covers104|f
+containsproperly099|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