]> granicus.if.org Git - postgis/commitdiff
Update the caching infrastructure to allow for arbitrary new caching systems to be...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 15 Jun 2012 23:11:03 +0000 (23:11 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 15 Jun 2012 23:11:03 +0000 (23:11 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@9918 b70326c6-7e19-0410-871a-916f4a2858ee

16 files changed:
libpgcommon/Makefile.in
libpgcommon/lwgeom_cache.c [new file with mode: 0644]
libpgcommon/lwgeom_cache.h [new file with mode: 0644]
libpgcommon/lwgeom_pg.h
libpgcommon/lwgeom_transform.c
postgis/Makefile.in
postgis/lwgeom_cache.c [deleted file]
postgis/lwgeom_cache.h [deleted file]
postgis/lwgeom_functions_analytic.c
postgis/lwgeom_geos.c
postgis/lwgeom_geos.h
postgis/lwgeom_geos_prepared.c
postgis/lwgeom_geos_prepared.h
postgis/lwgeom_ogc.c
postgis/lwgeom_rtree.c
postgis/lwgeom_rtree.h

index 7071a0056bcedd8e0b49cb8d4a630243333062cf..3b8e823e77362dd207df62d3df1c7b873e880e0e 100644 (file)
@@ -23,12 +23,14 @@ LEX=@LEX@
 SA_OBJS = \
        gserialized_gist.o \
        lwgeom_transform.o \
+       lwgeom_cache.o \
        lwgeom_pg.o
 
 
 SA_HEADERS = \
        lwgeom_pg.h \
        lwgeom_transform.h \
+       lwgeom_cache.h \
        gserialized_gist.h \
        pgsql_compat.h
 
diff --git a/libpgcommon/lwgeom_cache.c b/libpgcommon/lwgeom_cache.c
new file mode 100644 (file)
index 0000000..5906371
--- /dev/null
@@ -0,0 +1,308 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ *
+ * Copyright (C) 2012 Sandro Santilli <strk@keybit.net>
+ *
+ * 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 "fmgr.h"
+
+#include "../postgis_config.h"
+#include "lwgeom_cache.h"
+
+/* 
+* Generic statement caching infrastructure. We cache 
+* the following kinds of objects:
+* 
+*   geometries-with-trees
+*      PreparedGeometry, RTree, CIRC_TREE, RECT_TREE
+*   srids-with-projections
+*      projPJ
+* 
+* Each GenericCache* has a type, and after that
+* some data. Similar to generic LWGEOM*. Test that
+* the type number is what you expect before casting
+* and de-referencing struct members.
+*/
+typedef struct {
+       int type;
+       char data[1];
+} GenericCache;
+
+/* 
+* Although there are only two basic kinds of 
+* cache entries, the actual trees stored in the
+* geometries-with-trees pattern are quite diverse,
+* and they might be used in combination, so we have 
+* one slot for each tree type as well as a slot for
+* projections.
+*/
+typedef struct {
+       GenericCache* entry[NUM_CACHE_ENTRIES];
+} GenericCacheCollection;
+
+/**
+* Utility function to read the upper memory context off a function call 
+* info data.
+*/
+static MemoryContext 
+FIContext(FunctionCallInfoData* fcinfo)
+{
+       return fcinfo->flinfo->fn_mcxt;
+}
+
+/**
+* Get the generic collection off the statement, allocate a 
+* new one if we don't have one already.
+*/ 
+static GenericCacheCollection* 
+GetGenericCacheCollection(FunctionCallInfoData* fcinfo)
+{
+       GenericCacheCollection* cache = fcinfo->flinfo->fn_extra;
+
+       if ( ! cache ) 
+       {
+               cache = MemoryContextAlloc(FIContext(fcinfo), sizeof(GenericCacheCollection));
+               memset(cache, 0, sizeof(GenericCacheCollection));
+               fcinfo->flinfo->fn_extra = cache;
+       }
+       return cache;           
+}
+       
+
+/**
+* Get the Proj4 entry from the generic cache if one exists.
+* If it doesn't exist, make a new empty one and return it.
+*/
+PROJ4PortalCache*
+GetPROJ4SRSCache(FunctionCallInfoData* fcinfo)
+{
+       GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
+       PROJ4PortalCache* cache = (PROJ4PortalCache*)(generic_cache->entry[PROJ_CACHE_ENTRY]);
+       
+       if ( ! cache )
+       {
+               /* Allocate in the upper context */
+               cache = MemoryContextAlloc(FIContext(fcinfo), sizeof(PROJ4PortalCache));
+
+               if (cache)
+               {
+                       int i;
+
+                       POSTGIS_DEBUGF(3, "Allocating PROJ4Cache for portal with transform() MemoryContext %p", FIContext(fcinfo));
+                       /* Put in any required defaults */
+                       for (i = 0; i < PROJ4_CACHE_ITEMS; i++)
+                       {
+                               cache->PROJ4SRSCache[i].srid = SRID_UNKNOWN;
+                               cache->PROJ4SRSCache[i].projection = NULL;
+                               cache->PROJ4SRSCache[i].projection_mcxt = NULL;
+                       }
+                       cache->type = PROJ_CACHE_ENTRY;
+                       cache->PROJ4SRSCacheCount = 0;
+                       cache->PROJ4SRSCacheContext = FIContext(fcinfo);
+
+                       /* Store the pointer in GenericCache */
+                       generic_cache->entry[PROJ_CACHE_ENTRY] = (GenericCache*)cache;
+               }
+       }
+       return cache;
+}
+
+/**
+* Get an appropriate (based on the entry type number) 
+* GeomCache entry from the generic cache if one exists.
+* If it doesn't exist, make a new empty one and return it.
+*/
+GeomCache*            
+GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry)
+{
+       GeomCache* cache;
+       GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
+       
+       if ( (cache_entry) < 0 || (cache_entry >= NUM_CACHE_ENTRIES) )
+               return NULL;
+       
+       cache = (GeomCache*)(generic_cache->entry[cache_entry]);
+       
+       if ( ! cache )
+       {
+               /* Allocate in the upper context */
+               cache = MemoryContextAlloc(FIContext(fcinfo), sizeof(GeomCache));
+               /* Zero everything out */
+               memset(cache, 0, sizeof(GeomCache));
+               /* Set the cache type */
+               cache->type = cache_entry;
+               cache->context_statement = FIContext(fcinfo);
+
+               /* Store the pointer in GenericCache */
+               generic_cache->entry[cache_entry] = (GenericCache*)cache;
+               
+       }
+
+       /* The cache object type should always equal the entry type */
+       if ( cache->type != cache_entry )
+       {
+               lwerror("cache type does not equal expected entry type");
+               return NULL;
+       }
+               
+       return cache;
+}
+
+
+
+
+
+/**
+* Get an appropriate tree from the cache, based on the entry number
+* and the geometry values. Checks for a cache, checks for cache hits, 
+* returns a built tree if one exists. 
+*/
+void* 
+GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer index_free, const GSERIALIZED* geom1, const GSERIALIZED* geom2, int* argnum)
+{
+       int cache_hit = 0;
+       MemoryContext old_context;
+       GeomCache* cache = GetGeomCache(fcinfo, cache_entry);
+       const GSERIALIZED *geom;
+
+       /* Initialize output of argnum */
+       if ( argnum )
+               *argnum = cache_hit;
+
+       /* Cache hit on the first argument */
+       if ( geom1 &&
+            cache->argnum != 2 &&
+            cache->geom1_size == VARSIZE(geom1) &&
+            memcmp(cache->geom1, geom1, cache->geom1_size) == 0 )
+       {
+               cache_hit = 1;
+               geom = geom1;
+
+       }
+       /* Cache hit on second argument */
+       else if ( geom2 &&
+                 cache->argnum != 1 &&
+                 cache->geom2_size == VARSIZE(geom2) &&
+                 memcmp(cache->geom2, geom2, cache->geom2_size) == 0 )
+       {
+               cache_hit = 2;
+               geom = geom2;
+       }
+       /* No cache hit. If we have a tree, free it. */
+       else
+       {
+               cache_hit = 0;
+               if ( cache->index )
+               {
+                       index_free(cache);
+                       cache->index = NULL;
+               }
+       }
+
+       /* Cache hit, but no tree built yet, build it! */
+       if ( cache_hit && ! cache->index )
+       {
+               LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
+               int rv;
+
+               /* Can't build a tree on a NULL or empty */
+               if ( (!lwgeom) || lwgeom_is_empty(lwgeom) )
+                       return NULL;
+
+               cache->argnum = cache_hit;
+               old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+               rv = index_build(lwgeom, cache);
+               MemoryContextSwitchTo(old_context);
+
+               /* Something went awry in the tree build phase */
+               if ( ! rv )
+               {
+                       cache->argnum = 0;
+                       return NULL;
+               }
+
+       }
+
+       /* We have a hit and a calculated tree, we're done */
+       if ( cache_hit && (cache_hit == cache->argnum) && cache->index )
+       {
+               if ( argnum )
+                       *argnum = cache_hit;
+               return cache->index;
+       }
+
+       /* Argument one didn't match, so copy the new value in. */
+       if ( geom1 && cache_hit != 1 )
+       {
+               if ( cache->geom1 ) pfree(cache->geom1);
+               cache->geom1_size = VARSIZE(geom1);
+               cache->geom1 = MemoryContextAlloc(FIContext(fcinfo), cache->geom1_size);
+               memcpy(cache->geom1, geom1, cache->geom1_size);
+       }
+       /* Argument two didn't match, so copy the new value in. */
+       if ( geom2 && cache_hit != 2 )
+       {
+               if ( cache->geom2 ) pfree(cache->geom2);
+               cache->geom2_size = VARSIZE(geom2);
+               cache->geom2 = MemoryContextAlloc(FIContext(fcinfo), cache->geom2_size);
+               memcpy(cache->geom2, geom2, cache->geom2_size);
+       }
+
+       return NULL;
+}
+
+
+
+/**
+* Builder, freeer and public accessor for cached CIRC_NODE trees
+*/
+static int
+CircTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
+{
+       CIRC_NODE* tree = lwgeom_calculate_circ_tree(lwgeom);
+       if ( cache->index )
+       {
+               circ_tree_free((CIRC_NODE*)(cache->index));
+               cache->index = 0;
+       }
+       if ( ! tree )
+               return LW_FAILURE;
+       
+       cache->index = (void*)tree;
+       return LW_SUCCESS;
+}
+
+static int
+CircTreeFreer(GeomCache* cache)
+{
+       CIRC_NODE* tree = (CIRC_NODE*)(cache->index);
+       if ( tree ) 
+       {
+               circ_tree_free(tree);
+               cache->index = 0;
+       }
+       return LW_SUCCESS;
+}
+
+CIRC_NODE* 
+GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache)
+{
+       int argnum = 0;
+       CIRC_NODE* tree = NULL;
+
+       tree = (CIRC_NODE*)GetGeomIndex(fcinfo, CIRC_CACHE_ENTRY, CircTreeBuilder, CircTreeFreer, g1, g2, &argnum);
+
+       if ( argnum_of_cache )
+               *argnum_of_cache = argnum;
+               
+       return tree;
+}
+
+
diff --git a/libpgcommon/lwgeom_cache.h b/libpgcommon/lwgeom_cache.h
new file mode 100644 (file)
index 0000000..4bb2571
--- /dev/null
@@ -0,0 +1,101 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ *
+ * Copyright (C) 2012 Sandro Santilli <strk@keybit.net>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************/
+
+#ifndef LWGEOM_CACHE_H_
+#define LWGEOM_CACHE_H_ 1
+
+#include "postgres.h"
+#include "fmgr.h"
+
+#include "liblwgeom_internal.h"
+#include "lwgeodetic.h";
+#include "lwgeodetic_tree.h"
+
+#include "lwgeom_pg.h"
+
+
+#define PROJ_CACHE_ENTRY 0
+#define PREP_CACHE_ENTRY 1
+#define RTREE_CACHE_ENTRY 2
+#define CIRC_CACHE_ENTRY 3
+#define RECT_CACHE_ENTRY 4
+#define NUM_CACHE_ENTRIES 5
+
+
+/* 
+* For the geometry-with-tree case, we need space for
+* the serialized geometries and their sizes, so we can
+* test for cache hits/misses. The argnum tells us which
+* argument the tree is built for.
+*/
+typedef struct {
+       int            type;
+       GSERIALIZED*   geom1;
+       GSERIALIZED*   geom2;
+       size_t         geom1_size;
+       size_t         geom2_size;
+       int32          argnum;
+       MemoryContext  context_statement;
+       MemoryContext  context_callback;
+       void*          index;
+} GeomCache;
+
+
+/* An entry in the PROJ4 SRS cache */
+typedef struct struct_PROJ4SRSCacheItem
+{
+       int srid;
+       projPJ projection;
+       MemoryContext projection_mcxt;
+}
+PROJ4SRSCacheItem;
+
+/* PROJ 4 lookup transaction cache methods */
+#define PROJ4_CACHE_ITEMS      8
+
+/*
+* The proj4 cache holds a fixed number of reprojection
+* entries. In normal usage we don't expect it to have
+* many entries, so we always linearly scan the list.
+*/
+typedef struct struct_PROJ4PortalCache
+{
+       int type;
+       PROJ4SRSCacheItem PROJ4SRSCache[PROJ4_CACHE_ITEMS];
+       int PROJ4SRSCacheCount;
+       MemoryContext PROJ4SRSCacheContext;
+}
+PROJ4PortalCache;
+
+/*
+* Generic signature for function to take a serialized 
+* geometry and return a tree structure for fast edge
+* access.
+*/
+typedef int (*GeomIndexBuilder)(const LWGEOM* lwgeom, GeomCache* cache);
+typedef int (*GeomIndexFreer)(GeomCache* cache);
+
+/* 
+* Cache retrieval functions
+*/
+PROJ4PortalCache* GetPROJ4SRSCache(FunctionCallInfoData *fcinfo);
+GeomCache*        GetGeomCache(FunctionCallInfoData *fcinfo, int cache_entry);
+CIRC_NODE*        GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache);
+
+/* 
+* Given candidate geometries, a builer function and an entry type, cache and/or return an
+* appropriate tree.
+*/
+void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer tree_free, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum);
+
+
+#endif /* LWGEOM_CACHE_H_ */
index 9333a0f008d2c72d8579ca7ae7051e3779bd78b5..4dcef332d89af92eecf73b33327c331c6d1ed0f7 100644 (file)
@@ -154,4 +154,4 @@ Datum LWGEOM_getBBOX(PG_FUNCTION_ARGS);
 Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS);
 Datum LWGEOM_dropBBOX(PG_FUNCTION_ARGS);
 
-#endif /* !defined _LWGEOM_PG_H */
+#endif /* !defined _LWGEOM_PG_H */
index 30951b1ac5e81a1683a0e3b2d87f48ab8accefbf..07b0e5e5d6fa28f040083d4630eecaaebcb3ca45 100644 (file)
@@ -24,6 +24,7 @@
 #include "../postgis_config.h"
 #include "liblwgeom.h"
 #include "lwgeom_pg.h"
+#include "lwgeom_cache.h"
 #include "lwgeom_transform.h"
 
 /* C headers */
@@ -37,9 +38,6 @@
 int pj_transform_nodatum(projPJ srcdefn, projPJ dstdefn, long point_count, int point_offset, double *x, double *y, double *z );
 
 
-/* PROJ 4 lookup transaction cache methods */
-#define PROJ4_CACHE_ITEMS      8
-
 /*
  * PROJ 4 backend hash table initial hash size
  * (since 16 is the default portal hash table size, and we would
@@ -49,25 +47,6 @@ int pj_transform_nodatum(projPJ srcdefn, projPJ dstdefn, long point_count, int p
 #define PROJ4_BACKEND_HASH_SIZE        32
 
 
-/* An entry in the PROJ4 SRS cache */
-typedef struct struct_PROJ4SRSCacheItem
-{
-       int srid;
-       projPJ projection;
-       MemoryContext projection_mcxt;
-}
-PROJ4SRSCacheItem;
-
-/** The portal cache: it's contents and cache context
- */
-typedef struct struct_PROJ4PortalCache
-{
-       PROJ4SRSCacheItem PROJ4SRSCache[PROJ4_CACHE_ITEMS];
-       int PROJ4SRSCacheCount;
-       MemoryContext PROJ4SRSCacheContext;
-}
-PROJ4PortalCache;
-
 /**
  * Backend projPJ hash table
  *
@@ -98,7 +77,7 @@ static projPJ GetPJHashEntry(MemoryContext mcxt);
 static void DeletePJHashEntry(MemoryContext mcxt);
 
 /* Internal Cache API */
-static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo) ;
+/* static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo) ; */
 static bool IsInPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid);
 static projPJ GetProjectionFromPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid);
 static void AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid);
@@ -635,12 +614,13 @@ void SetPROJ4LibPath(void)
 }
 
 Proj4Cache GetPROJ4Cache(FunctionCallInfo fcinfo) {
-       return (Proj4Cache)GetPROJ4SRSCache(fcinfo) ;
+       return (Proj4Cache)GetPROJ4SRSCache(fcinfo);
 }
 
+#if 0
 static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo)
 {
-       PROJ4PortalCache *PROJ4Cache ;
+       PROJ4PortalCache *PROJ4Cache = (GetGeomCache(fcinfo))->proj;
 
        /*
         * If we have not already created PROJ4 cache for this portal
@@ -681,7 +661,7 @@ static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfo fcinfo)
 
        return PROJ4Cache ;
 }
-
+#endif
 
 int
 GetProjectionsUsingFCInfo(FunctionCallInfo fcinfo, int srid1, int srid2, projPJ *pj1, projPJ *pj2)
index 2fd681bcce39a49cba08fc37a5e8a7f2cc5ee443..9277f71070f615929fc84b187ce56928d55533ef 100644 (file)
@@ -36,7 +36,6 @@ PG_OBJS= \
        lwgeom_btree.o \
        lwgeom_box.o \
        lwgeom_box3d.o \
-       lwgeom_cache.o \
        lwgeom_geos.o \
        lwgeom_geos_prepared.o \
        lwgeom_geos_clean.o \
diff --git a/postgis/lwgeom_cache.c b/postgis/lwgeom_cache.c
deleted file mode 100644 (file)
index b53c776..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/**********************************************************************
- *
- * PostGIS - Spatial Types for PostgreSQL
- * http://postgis.refractions.net
- *
- * Copyright (C) 2012 Sandro Santilli <strk@keybit.net>
- *
- * 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 "fmgr.h"
-
-#include "../postgis_config.h"
-#include "lwgeom_cache.h"
-
-GeomCache* GetGeomCache(FunctionCallInfoData *fcinfo)
-{
-       MemoryContext old_context;
-       GeomCache* cache = fcinfo->flinfo->fn_extra;
-       if ( ! cache ) {
-               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-               cache = palloc(sizeof(GeomCache));
-               MemoryContextSwitchTo(old_context);
-               cache->prep = 0;
-               cache->rtree = 0;
-               fcinfo->flinfo->fn_extra = cache;
-       }
-       return cache;
-}
-
diff --git a/postgis/lwgeom_cache.h b/postgis/lwgeom_cache.h
deleted file mode 100644 (file)
index e687398..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/**********************************************************************
- *
- * PostGIS - Spatial Types for PostgreSQL
- * http://postgis.refractions.net
- *
- * Copyright (C) 2012 Sandro Santilli <strk@keybit.net>
- *
- * This is free software; you can redistribute and/or modify it under
- * the terms of the GNU General Public Licence. See the COPYING file.
- *
- **********************************************************************/
-
-#ifndef LWGEOM_GEOS_CACHE_H_
-#define LWGEOM_GEOS_CACHE_H_ 1
-
-#include "postgres.h"
-#include "fmgr.h"
-
-#include "lwgeom_pg.h"
-#include "lwgeom_rtree.h"
-#include "lwgeom_geos_prepared.h"
-
-typedef struct {
-       PrepGeomCache* prep;
-       RTREE_POLY_CACHE* rtree;
-} GeomCache;
-
-GeomCache* GetGeomCache(FunctionCallInfoData *fcinfo);
-
-#endif /* LWGEOM_GEOS_CACHE_H_ 1 */
index 3d2f78c51b673d091552723ece7a9f4187612379..94a76e180b1b2ae85606cfd306342956e8301318 100644 (file)
@@ -912,7 +912,7 @@ int point_in_ring_rtree(RTREE_NODE *root, POINT2D *point)
 
        POSTGIS_DEBUG(2, "point_in_ring called.");
 
-       lines = findLineSegments(root, point->y);
+       lines = RTreeFindLineSegments(root, point->y);
        if (!lines)
                return -1;
 
index 1a1c01c1b0e92851ea357d76a49edc905092dbde..b54535739510ddecd42621c1f994345fd0964351 100644 (file)
 
 #include "../postgis_config.h"
 #include "lwgeom_functions_analytic.h" /* for point_in_polygon */
-#include "lwgeom_cache.h"
 #include "lwgeom_geos.h"
 #include "liblwgeom_internal.h"
 #include "lwgeom_rtree.h"
-
-/*
-** GEOS prepared geometry is only available from GEOS 3.1 onwards
-*/
-#define PREPARED_GEOM
-
-#ifdef PREPARED_GEOM
 #include "lwgeom_geos_prepared.h" 
-#endif
+
 
 #include <string.h>
 #include <assert.h>
@@ -87,26 +79,6 @@ Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
 ** Prototypes end
 */
 
-static RTREE_POLY_CACHE *
-GetRtreeCache(FunctionCallInfoData *fcinfo, LWGEOM *lwgeom, GSERIALIZED *poly)
-{
-       MemoryContext old_context;
-       GeomCache* supercache = GetGeomCache(fcinfo);
-       RTREE_POLY_CACHE *poly_cache = supercache->rtree;
-
-       /*
-        * Switch the context to the function-scope context,
-        * retrieve the appropriate cache object, cache it for
-        * future use, then switch back to the local context.
-        */
-       old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-       poly_cache = retrieveCache(lwgeom, poly, poly_cache);
-       supercache->rtree = poly_cache;
-       MemoryContextSwitchTo(old_context);
-
-       return poly_cache;
-}
-
 
 PG_FUNCTION_INFO_V1(postgis_geos_version);
 Datum postgis_geos_version(PG_FUNCTION_ARGS)
@@ -1985,9 +1957,7 @@ Datum contains(PG_FUNCTION_ARGS)
        LWPOINT *point;
        RTREE_POLY_CACHE *poly_cache;
        bool result;
-#ifdef PREPARED_GEOM
        PrepGeomCache *prep_cache;
-#endif
 
        geom1 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
        geom2 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
@@ -2030,9 +2000,9 @@ Datum contains(PG_FUNCTION_ARGS)
 
                POSTGIS_DEBUGF(3, "Precall point_in_multipolygon_rtree %p, %p", lwgeom, point);
 
-               poly_cache = GetRtreeCache(fcinfo, lwgeom, geom1);
+               poly_cache = GetRtreeCache(fcinfo, geom1);
 
-               if ( poly_cache->ringIndices )
+               if ( poly_cache && poly_cache->ringIndices )
                {
                        result = point_in_multipolygon_rtree(poly_cache->ringIndices, poly_cache->polyCount, poly_cache->ringCounts, point);
                }
@@ -2070,7 +2040,6 @@ Datum contains(PG_FUNCTION_ARGS)
 
        initGEOS(lwnotice, lwgeom_geos_error);
 
-#ifdef PREPARED_GEOM
        prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
        if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
@@ -2086,7 +2055,6 @@ Datum contains(PG_FUNCTION_ARGS)
                GEOSGeom_destroy(g1);
        }
        else
-#endif
        {
                g1 = (GEOSGeometry *)POSTGIS2GEOS(geom1);
                if ( 0 == g1 )   /* exception thrown at construction */
@@ -2127,9 +2095,7 @@ Datum containsproperly(PG_FUNCTION_ARGS)
        GSERIALIZED *                           geom2;
        bool                                    result;
        GBOX                    box1, box2;
-#ifdef PREPARED_GEOM
        PrepGeomCache * prep_cache;
-#endif
 
        geom1 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
        geom2 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
@@ -2156,7 +2122,6 @@ Datum containsproperly(PG_FUNCTION_ARGS)
 
        initGEOS(lwnotice, lwgeom_geos_error);
 
-#ifdef PREPARED_GEOM
        prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
        if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
@@ -2171,7 +2136,6 @@ Datum containsproperly(PG_FUNCTION_ARGS)
                GEOSGeom_destroy(g);
        }
        else
-#endif
        {
                GEOSGeometry *g2;
                GEOSGeometry *g1;
@@ -2221,9 +2185,7 @@ Datum covers(PG_FUNCTION_ARGS)
        LWGEOM *lwgeom;
        LWPOINT *point;
        RTREE_POLY_CACHE *poly_cache;
-#ifdef PREPARED_GEOM
        PrepGeomCache *prep_cache;
-#endif
 
        geom1 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
        geom2 = (GSERIALIZED *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
@@ -2244,7 +2206,7 @@ Datum covers(PG_FUNCTION_ARGS)
                gserialized_get_gbox_p(geom2, &box2) )
        {
                if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-                       ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
                {
                        PG_RETURN_BOOL(FALSE);
                }
@@ -2264,9 +2226,9 @@ Datum covers(PG_FUNCTION_ARGS)
 
                POSTGIS_DEBUGF(3, "Precall point_in_multipolygon_rtree %p, %p", lwgeom, point);
 
-               poly_cache = GetRtreeCache(fcinfo, lwgeom, geom1);
+               poly_cache = GetRtreeCache(fcinfo, geom1);
 
-               if ( poly_cache->ringIndices )
+               if ( poly_cache && poly_cache->ringIndices )
                {
                        result = point_in_multipolygon_rtree(poly_cache->ringIndices, poly_cache->polyCount, poly_cache->ringCounts, point);
                }
@@ -2305,7 +2267,6 @@ Datum covers(PG_FUNCTION_ARGS)
 
        initGEOS(lwnotice, lwgeom_geos_error);
 
-#ifdef PREPARED_GEOM
        prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
        if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
@@ -2320,7 +2281,6 @@ Datum covers(PG_FUNCTION_ARGS)
                GEOSGeom_destroy(g1);
        }
        else
-#endif
        {
                GEOSGeometry *g1;
                GEOSGeometry *g2;
@@ -2421,9 +2381,9 @@ Datum coveredby(PG_FUNCTION_ARGS)
                point = lwgeom_as_lwpoint(lwgeom_from_gserialized(geom1));
                lwgeom = lwgeom_from_gserialized(geom2);
 
-               poly_cache = GetRtreeCache(fcinfo, lwgeom, geom2);
+               poly_cache = GetRtreeCache(fcinfo, geom2);
 
-               if ( poly_cache->ringIndices )
+               if ( poly_cache && poly_cache->ringIndices )
                {
                        result = point_in_multipolygon_rtree(poly_cache->ringIndices, poly_cache->polyCount, poly_cache->ringCounts, point);
                }
@@ -2574,9 +2534,7 @@ Datum intersects(PG_FUNCTION_ARGS)
        LWPOINT *point;
        LWGEOM *lwgeom;
        RTREE_POLY_CACHE *poly_cache;
-#ifdef PREPARED_GEOM
        PrepGeomCache *prep_cache;
-#endif
 
        geom1 = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
        geom2 = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
@@ -2609,7 +2567,7 @@ Datum intersects(PG_FUNCTION_ARGS)
        type1 = gserialized_get_type(geom1);
        type2 = gserialized_get_type(geom2);
        if ( (type1 == POINTTYPE && (type2 == POLYGONTYPE || type2 == MULTIPOLYGONTYPE)) ||
-               (type2 == POINTTYPE && (type1 == POLYGONTYPE || type1 == MULTIPOLYGONTYPE)))
+            (type2 == POINTTYPE && (type1 == POLYGONTYPE || type1 == MULTIPOLYGONTYPE)))
        {
                POSTGIS_DEBUG(3, "Point in Polygon test requested...short-circuiting.");
 
@@ -2628,9 +2586,9 @@ Datum intersects(PG_FUNCTION_ARGS)
                        polytype = type1;
                }
 
-               poly_cache = GetRtreeCache(fcinfo, lwgeom, serialized_poly);
+               poly_cache = GetRtreeCache(fcinfo, serialized_poly);
 
-               if ( poly_cache->ringIndices )
+               if ( poly_cache && poly_cache->ringIndices )
                {
                        result = point_in_multipolygon_rtree(poly_cache->ringIndices, poly_cache->polyCount, poly_cache->ringCounts, point);
                }
@@ -2664,7 +2622,6 @@ Datum intersects(PG_FUNCTION_ARGS)
        }
 
        initGEOS(lwnotice, lwgeom_geos_error);
-#ifdef PREPARED_GEOM
        prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
 
        if ( prep_cache && prep_cache->prepared_geom )
@@ -2693,7 +2650,6 @@ Datum intersects(PG_FUNCTION_ARGS)
                }
        }
        else
-#endif
        {
                GEOSGeometry *g1;
                GEOSGeometry *g2;
index c93e68dcd837e94de06a2c68fc9ef63272a4cd53..c59135f6ab659a4ab197c5dd3b134ef5a4f71321 100644 (file)
@@ -26,4 +26,4 @@ GEOSGeometry * POSTGIS2GEOS(GSERIALIZED *g);
 
 void errorIfGeometryCollection(GSERIALIZED *g1, GSERIALIZED *g2);
 
-#endif /* LWGEOM_GEOS_H_ */
+#endif /* LWGEOM_GEOS_H_ */
index 77391a7072bc1cc825a10ce3e55384646f7af98b..f722c6180d75e31f507bd4a636c456d82a32e555 100644 (file)
@@ -266,191 +266,149 @@ DeletePrepGeomHashEntry(MemoryContext mcxt)
                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.
+/**
+* Given a generic GeomCache, and a geometry to prepare,
+* prepare a PrepGeomCache and stick it into the GeomCache->index
+* slot. The PrepGeomCache includes the original GEOS geometry,
+* and the GEOS prepared geometry, and a pointer to the 
+* MemoryContext where the callback functions are registered. 
+* 
+* This function is passed into the generic GetGeomCache function
+* so that it can build an appropriate indexed structure in the case
+* of a cache hit when there is no indexed structure yet 
+* available to return.
 */
-PrepGeomCache*
-GetPrepGeomCache(FunctionCallInfoData *fcinfo, GSERIALIZED *pg_geom1, GSERIALIZED *pg_geom2)
+static int
+PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache)
 {
-       MemoryContext old_context;
-       GeomCache* supercache = GetGeomCache(fcinfo);
-       PrepGeomCache* cache = supercache->prep;
-       int copy_keys = 1;
-       size_t pg_geom1_size = 0;
-       size_t pg_geom2_size = 0;
-
-       assert ( ! cache || cache->type == 2 );
-
+       PrepGeomCache* prepcache;
+       PrepGeomHashEntry* pghe;
+       
+       /*
+       * First time through? allocate the global hash.
+       */
        if (!PrepGeomHash)
                CreatePrepGeomHash();
 
-       if ( pg_geom1 )
-               pg_geom1_size = VARSIZE(pg_geom1);
-
-       if ( pg_geom2 )
-               pg_geom2_size = VARSIZE(pg_geom2);
-
-       if ( cache == NULL)
+       /*
+       * No callback entry for this statement context yet? Set it up
+       */
+       if ( ! cache->context_callback )
        {
-               /*
-               ** 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->type = 2;
-               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(3, "GetPrepGeomCache: creating cache: %p", cache);
-
-               pghe.context = cache->context;
+               cache->context_callback = MemoryContextCreate(T_AllocSetContext, 8192,
+                                            &PreparedCacheContextMethods,
+                                            cache->context_statement,
+                                            "PostGIS Prepared Geometry Context");
+               pghe.context = cache->context_callback;
                pghe.geom = 0;
                pghe.prepared_geom = 0;
-               AddPrepGeomHashEntry( pghe );
-
-               supercache->prep = cache;
-
-               POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache);
+               AddPrepGeomHashEntry( pghe );           
        }
-       else if ( pg_geom1 &&
-                 cache->argnum != 2 &&
-                 cache->pg_geom1_size == pg_geom1_size &&
-                 memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0)
+       
+       /* 
+       * Hum, we shouldn't be asked to build a new cache on top of 
+       * an existing one. Error.
+       */
+       if ( cache->index )
        {
-               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(3, "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(3, "GetPrepGeomCache: cache hit, argument 1");
-               }
-               /* We don't need new keys until we have a cache miss */
-               copy_keys = 0;
+               lwerror("PrepGeomCacheBuilder asked to build new prepcache where one already exists.");
+               return LW_FAILURE;
        }
-       else if ( pg_geom2 &&
-                 cache->argnum != 1 &&
-                 cache->pg_geom2_size == pg_geom2_size &&
-                 memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0)
+       
+       /* 
+       * Allocate a new index structure 
+       */
+       cache->index = MemoryContextAlloc(cache->context_statement, sizeof(PrepGeomCache));
+       memset(cache->index, 0, sizeof(PrepGeomCache));
+       prepcache = (PrepGeomCache*)(cache->index);
+       
+       prepcache->geom = LWGEOM2GEOS( lwgeom );
+       if ( ! prepcache->geom ) return LW_FAILURE;
+       prepcache->prepared_geom = GEOSPrepare( prepcache->geom );
+       if ( ! prepcache->prepared_geom ) return LW_FAILURE;
+       prepcache->argnum = cache->argnum;
+       
+       /*
+       * In order to find the objects we need to destroy, we keep
+       * extra references in a global hash object.
+       */
+       pghe = GetPrepGeomHashEntry(cache->context_callback);
+       if ( ! pghe )
        {
-               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(3, "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(3, "GetPrepGeomCache: cache hit, argument 2");
-               }
-               /* We don't need new keys until we have a cache miss */
-               copy_keys = 0;
+               lwerror("PrepGeomCacheBuilder failed to find hash entry for context %p", cache->context_callback);
+               return LW_FAILURE;
        }
-       else if ( cache->prepared_geom )
-       {
-               /*
-               ** No cache hits, so this must be a miss.
-               ** Destroy the GEOS objects, empty the cache.
-               */
-               PrepGeomHashEntry* pghe;
+       
+       pghe->geom = prepcache->geom;
+       pghe->prepared_geom = prepcache->prepared_geom;
 
-               pghe = GetPrepGeomHashEntry(cache->context);
-               pghe->geom = 0;
-               pghe->prepared_geom = 0;
-
-               POSTGIS_DEBUGF(3, "GetPrepGeomCache: cache miss, argument %d", cache->argnum);
-               GEOSPreparedGeom_destroy( cache->prepared_geom );
-               GEOSGeom_destroy( (GEOSGeometry *)cache->geom );
-
-               cache->prepared_geom = 0;
-               cache->geom = 0;
-               cache->argnum = 0;
-
-       }
+       return LW_SUCCESS;
+}
 
-       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(3, "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 )
+/**
+* This function is passed into the generic GetGeomCache function
+* in the case of a cache miss, so that it can free the particular 
+* indexed structure being managed.
+*
+* In the case of prepared geometry, we want to leave the actual
+* PrepGeomCache allocated and in place, but ensure that the
+* GEOS Geometry and PreparedGeometry are freed so we don't leak
+* memory as we transition from cache hit to miss to hit, etc.
+*/
+static int
+PrepGeomCacheCleaner(GeomCache *cache)
+{
+       PrepGeomHashEntry* pghe = 0;
+       PrepGeomCache* prepcache = (PrepGeomCache*)(cache->index);
+
+       if ( ! prepcache )
+               return LW_FAILURE;
+
+       /* 
+       * Clear out the references to the soon-to-be-freed GEOS objects 
+       * from the callback hash entry
+       */
+       pghe = GetPrepGeomHashEntry(cache->context_callback);
+       if ( ! pghe )
        {
-               POSTGIS_DEBUG(3, "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;
+               lwerror("PrepGeomCacheCleaner failed to find hash entry for context %p", cache->context_callback);
+               return LW_FAILURE;
        }
+       pghe->geom = 0;
+       pghe->prepared_geom = 0;
+
+       /* 
+       * Free the GEOS objects and free the index tree
+       */
+       POSTGIS_DEBUGF(3, "PrepGeomCacheFreeer: freeing %p argnum %d", prepcache, prepcache->argnum);
+       GEOSPreparedGeom_destroy( prepcache->prepared_geom );
+       GEOSGeom_destroy( (GEOSGeometry *)prepcache->geom );
+       pfree(cache->index);
+       cache->index = 0;
+       
+       return LW_SUCCESS;
+}
+
+/**
+* Given a couple potential geometries and a function
+* call context, return a prepared structure for one
+* of them, if such a structure doesn't already exist.
+* If it doesn't exist, and there is a cache hit,
+* ensure that the structure is built for next time.
+* Most of the work is done by the GetGeomCache generic
+* function, but we pass in call-backs to handle building
+* and freeing the GEOS PreparedGeometry structures
+* we need for this particular caching strategy.
+*/
+PrepGeomCache*
+GetPrepGeomCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2)
+{
+       int argnum = 0;
+       PrepGeomCache* prepcache = NULL;
 
-       return cache;
+       prepcache = (PrepGeomCache*)GetGeomIndex(fcinfo, PREP_CACHE_ENTRY, PrepGeomCacheBuilder, PrepGeomCacheCleaner, g1, g2, &argnum);
 
+       return prepcache;
 }
 
index c32977ee6a0975c2324671e88d8f7c5310118dac..e100ba8febbe70481f2e53f7ecee9a2f9a4e9518 100644 (file)
@@ -33,7 +33,6 @@
 ** 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.
-*/
 typedef struct
 {
        char                          type;
@@ -47,6 +46,15 @@ typedef struct
        MemoryContext                 context;
 }
 PrepGeomCache;
+*/
+
+typedef struct
+{
+       int                           argnum;
+       const GEOSPreparedGeometry    *prepared_geom;
+       const GEOSGeometry            *geom;
+}
+PrepGeomCache;
 
 /*
 ** Get the current cache, given the input geometries.
@@ -58,4 +66,4 @@ PrepGeomCache;
 */
 PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, GSERIALIZED *pg_geom1, GSERIALIZED *pg_geom2);
 
-#endif /* LWGEOM_GEOS_PREPARED_H_ */
+#endif /* LWGEOM_GEOS_PREPARED_H_ */
index 0e36419912ebff2109c83c85ee294dcf053414bd..6ab5925f38c62899a11c842eb3bb6412b272feca 100644 (file)
@@ -833,7 +833,7 @@ Datum LWGEOM_asText(PG_FUNCTION_ARGS)
        /* Write to WKT and free the geometry */
        wkt = lwgeom_to_wkt(lwgeom, WKT_ISO, DBL_DIG, &wkt_size);
        lwgeom_free(lwgeom);
-       POSTGIS_DEBUGF(3, "WKT size = %d, WKT length = %d", wkt_size, strlen(wkt));
+       POSTGIS_DEBUGF(3, "WKT size = %u, WKT length = %u", (unsigned int)wkt_size, (unsigned int)strlen(wkt));
 
        /* Write to text and free the WKT */
        result = cstring2text(wkt);
index 259962642d4d60d15555db41208fa9106323908d..7bef46bb739cda1923378c357e994e4b5b6482cd 100644 (file)
 #include "lwgeom_pg.h"
 #include "liblwgeom.h"
 #include "liblwgeom_internal.h"         /* For FP comparators. */
+#include "lwgeom_cache.h"
 #include "lwgeom_rtree.h"
 
 
-Datum LWGEOM_polygon_index(PG_FUNCTION_ARGS);
-
+/* Prototypes */
+static void RTreeFree(RTREE_NODE* root);
 
 /**
- * Creates an rtree given a pointer to the point array.
- * Must copy the point array.
- */
-RTREE_NODE *createTree(POINTARRAY *pointArray)
+* Allocate a fresh clean RTREE_POLY_CACHE
+*/
+static RTREE_POLY_CACHE* 
+RTreeCacheCreate()
 {
-       RTREE_NODE *root;
-       RTREE_NODE** nodes = lwalloc(sizeof(RTREE_NODE*) * pointArray->npoints);
-       int i, nodeCount;
-       int childNodes, parentNodes;
-
-       POSTGIS_DEBUGF(2, "createTree called with pointarray %p", pointArray);
-
-       nodeCount = pointArray->npoints - 1;
+       RTREE_POLY_CACHE* result;
+       result = lwalloc(sizeof(RTREE_POLY_CACHE));
+       memset(result, 0, sizeof(RTREE_POLY_CACHE));
+       return result;
+}
 
-       POSTGIS_DEBUGF(3, "Total leaf nodes: %d", nodeCount);
+/**
+* Recursively frees the child nodes, the interval and the line before
+* freeing the root node.
+*/
+static void 
+RTreeFree(RTREE_NODE* root)
+{
+       POSTGIS_DEBUGF(2, "RTreeFree called for %p", root);
 
-       /*
-        * Create a leaf node for every line segment.
-        */
-       for (i = 0; i < nodeCount; i++)
+       if (root->leftNode)
+               RTreeFree(root->leftNode);
+       if (root->rightNode)
+               RTreeFree(root->rightNode);
+       lwfree(root->interval);
+       if (root->segment)
        {
-               nodes[i] = createLeafNode(pointArray, i);
+               lwline_free(root->segment);
        }
+       lwfree(root);
+}
 
-       /*
-        * Next we group nodes by pairs.  If there's an odd number of nodes,
-        * we bring the last node up a level as is.      Continue until we have
-        * a single top node.
-        */
-       childNodes = nodeCount;
-       parentNodes = nodeCount / 2;
-       while (parentNodes > 0)
+/**
+* Free the cache object and all the sub-objects properly.
+*/
+static void 
+RTreeCacheClear(RTREE_POLY_CACHE* cache)
+{
+       int g, r, i;
+       POSTGIS_DEBUGF(2, "RTreeCacheClear called for %p", cache);
+       i = 0;
+       for (g = 0; g < cache->polyCount; g++)
        {
-               POSTGIS_DEBUGF(3, "Merging %d children into %d parents.", childNodes, parentNodes);
-
-               i = 0;
-               while (i < parentNodes)
+               for (r = 0; r < cache->ringCounts[g]; r++)
                {
-                       nodes[i] = createInteriorNode(nodes[i*2], nodes[i*2+1]);
+                       RTreeFree(cache->ringIndices[i]);
                        i++;
                }
-               /*
-                * Check for an odd numbered final node.
-                */
-               if (parentNodes * 2 < childNodes)
-               {
-                       POSTGIS_DEBUGF(3, "Shuffling child %d to parent %d", childNodes - 1, i);
-
-                       nodes[i] = nodes[childNodes - 1];
-                       parentNodes++;
-               }
-               childNodes = parentNodes;
-               parentNodes = parentNodes / 2;
        }
+       lwfree(cache->ringIndices);
+       lwfree(cache->ringCounts);
+       cache->ringIndices = 0;
+       cache->ringCounts = 0;
+       cache->polyCount = 0;
+}
 
-       root = nodes[0];
-       lwfree(nodes);
-       POSTGIS_DEBUGF(3, "createTree returning %p", root);
 
-       return root;
+/**
+ * Returns 1 if min < value <= max, 0 otherwise. 
+*/
+static uint32 
+IntervalIsContained(RTREE_INTERVAL* interval, double value)
+{
+       return FP_CONTAINS_INCL(interval->min, value, interval->max) ? 1 : 0;
+}
+
+/**
+* Creates an interval with the total extents of the two given intervals.
+*/
+static RTREE_INTERVAL* 
+RTreeMergeIntervals(RTREE_INTERVAL *inter1, RTREE_INTERVAL *inter2)
+{
+       RTREE_INTERVAL *interval;
+
+       POSTGIS_DEBUGF(2, "RTreeMergeIntervals called with %p, %p", inter1, inter2);
+
+       interval = lwalloc(sizeof(RTREE_INTERVAL));
+       interval->max = FP_MAX(inter1->max, inter2->max);
+       interval->min = FP_MIN(inter1->min, inter2->min);
+
+       POSTGIS_DEBUGF(3, "interval min = %8.3f, max = %8.3f", interval->min, interval->max);
+
+       return interval;
 }
 
 /**
- * Creates an interior node given the children.
- */
-RTREE_NODE *createInteriorNode(RTREE_NODE *left, RTREE_NODE *right)
+* Creates an interval given the min and max values, in arbitrary order.
+*/
+static RTREE_INTERVAL*
+RTreeCreateInterval(double value1, double value2)
+{
+       RTREE_INTERVAL *interval;
+
+       POSTGIS_DEBUGF(2, "RTreeCreateInterval called with %8.3f, %8.3f", value1, value2);
+
+       interval = lwalloc(sizeof(RTREE_INTERVAL));
+       interval->max = FP_MAX(value1, value2);
+       interval->min = FP_MIN(value1, value2);
+
+       POSTGIS_DEBUGF(3, "interval min = %8.3f, max = %8.3f", interval->min, interval->max);
+
+       return interval;
+}
+
+/**
+* Creates an interior node given the children.
+*/
+static RTREE_NODE* 
+RTreeCreateInteriorNode(RTREE_NODE* left, RTREE_NODE* right)
 {
        RTREE_NODE *parent;
 
-       POSTGIS_DEBUGF(2, "createInteriorNode called for children %p, %p", left, right);
+       POSTGIS_DEBUGF(2, "RTreeCreateInteriorNode called for children %p, %p", left, right);
 
        parent = lwalloc(sizeof(RTREE_NODE));
        parent->leftNode = left;
        parent->rightNode = right;
-       parent->interval = mergeIntervals(left->interval, right->interval);
+       parent->interval = RTreeMergeIntervals(left->interval, right->interval);
        parent->segment = NULL;
 
-       POSTGIS_DEBUGF(3, "createInteriorNode returning %p", parent);
+       POSTGIS_DEBUGF(3, "RTreeCreateInteriorNode returning %p", parent);
 
        return parent;
 }
 
 /**
- * Creates a leaf node given the pointer to the start point of the segment.
- */
-RTREE_NODE *createLeafNode(POINTARRAY *pa, int startPoint)
+* Creates a leaf node given the pointer to the start point of the segment.
+*/
+static RTREE_NODE* 
+RTreeCreateLeafNode(POINTARRAY* pa, int startPoint)
 {
        RTREE_NODE *parent;
        LWLINE *line;
@@ -116,11 +161,11 @@ RTREE_NODE *createLeafNode(POINTARRAY *pa, int startPoint)
        POINT4D tmp;
        POINTARRAY *npa;
 
-       POSTGIS_DEBUGF(2, "createLeafNode called for point %d of %p", startPoint, pa);
+       POSTGIS_DEBUGF(2, "RTreeCreateLeafNode called for point %d of %p", startPoint, pa);
 
        if (pa->npoints < startPoint + 2)
        {
-               lwerror("createLeafNode: npoints = %d, startPoint = %d", pa->npoints, startPoint);
+               lwerror("RTreeCreateLeafNode: npoints = %d, startPoint = %d", pa->npoints, startPoint);
        }
 
        /*
@@ -141,179 +186,92 @@ RTREE_NODE *createLeafNode(POINTARRAY *pa, int startPoint)
        line = lwline_construct(SRID_UNKNOWN, NULL, npa);
        
        parent = lwalloc(sizeof(RTREE_NODE));
-       parent->interval = createInterval(value1, value2);
+       parent->interval = RTreeCreateInterval(value1, value2);
        parent->segment = line;
        parent->leftNode = NULL;
        parent->rightNode = NULL;
 
-       POSTGIS_DEBUGF(3, "createLeafNode returning %p", parent);
+       POSTGIS_DEBUGF(3, "RTreeCreateLeafNode returning %p", parent);
 
        return parent;
 }
 
 /**
- * Creates an interval with the total extents of the two given intervals.
- */
-INTERVAL *mergeIntervals(INTERVAL *inter1, INTERVAL *inter2)
+* Creates an rtree given a pointer to the point array.
+* Must copy the point array.
+*/
+static RTREE_NODE* 
+RTreeCreate(POINTARRAY* pointArray)
 {
-       INTERVAL *interval;
-
-       POSTGIS_DEBUGF(2, "mergeIntervals called with %p, %p", inter1, inter2);
-
-       interval = lwalloc(sizeof(INTERVAL));
-       interval->max = FP_MAX(inter1->max, inter2->max);
-       interval->min = FP_MIN(inter1->min, inter2->min);
-
-       POSTGIS_DEBUGF(3, "interval min = %8.3f, max = %8.3f", interval->min, interval->max);
-
-       return interval;
-}
-
-/**
- * Creates an interval given the min and max values, in arbitrary order.
- */
-INTERVAL *createInterval(double value1, double value2)
-{
-       INTERVAL *interval;
-
-       POSTGIS_DEBUGF(2, "createInterval called with %8.3f, %8.3f", value1, value2);
-
-       interval = lwalloc(sizeof(INTERVAL));
-       interval->max = FP_MAX(value1, value2);
-       interval->min = FP_MIN(value1, value2);
-
-       POSTGIS_DEBUGF(3, "interval min = %8.3f, max = %8.3f", interval->min, interval->max);
-
-       return interval;
-}
-
-/**
- * Recursively frees the child nodes, the interval and the line before
- * freeing the root node.
- */
-void freeTree(RTREE_NODE *root)
-{
-       POSTGIS_DEBUGF(2, "freeTree called for %p", root);
-
-       if (root->leftNode)
-               freeTree(root->leftNode);
-       if (root->rightNode)
-               freeTree(root->rightNode);
-       lwfree(root->interval);
-       if (root->segment)
-       {
-               lwline_free(root->segment);
-       }
-       lwfree(root);
-}
-
-
-/**
- * Free the cache object and all the sub-objects properly.
- */
-void clearCache(RTREE_POLY_CACHE *cache)
-{
-       int g, r, i;
-       POSTGIS_DEBUGF(2, "clearCache called for %p", cache);
-        i = 0;
-        for (g = 0; g < cache->polyCount; g++)
-        {
-               for (r = 0; r < cache->ringCounts[g]; r++)
-               {
-                       freeTree(cache->ringIndices[i]);
-                        i++;
-                }
-       }
-       lwfree(cache->ringIndices);
-        lwfree(cache->ringCounts);
-       lwfree(cache->poly);
-       cache->poly = 0;
-       cache->ringIndices = 0;
-       cache->ringCounts = 0;
-       cache->polyCount = 0;
-}
-
-
-/**
- * Retrieves a collection of line segments given the root and crossing value.
- * The collection is a multilinestring consisting of two point lines
- * representing the segments of the ring that may be crossed by the
- * horizontal projection line at the given y value.
- */
-LWMLINE *findLineSegments(RTREE_NODE *root, double value)
-{
-       LWMLINE *tmp, *result;
-       LWGEOM **lwgeoms;
-
-       POSTGIS_DEBUGF(2, "findLineSegments called for tree %p and value %8.3f", root, value);
+       RTREE_NODE* root;
+       RTREE_NODE** nodes = lwalloc(pointArray->npoints * sizeof(RTREE_NODE*));
+       int i, nodeCount;
+       int childNodes, parentNodes;
 
-       result = NULL;
+       POSTGIS_DEBUGF(2, "RTreeCreate called with pointarray %p", pointArray);
 
-       if (!isContained(root->interval, value))
-       {
-               POSTGIS_DEBUGF(3, "findLineSegments %p: not contained.", root);
+       nodeCount = pointArray->npoints - 1;
 
-               return NULL;
-       }
+       POSTGIS_DEBUGF(3, "Total leaf nodes: %d", nodeCount);
 
-       /* If there is a segment defined for this node, include it. */
-       if (root->segment)
+       /*
+        * Create a leaf node for every line segment.
+        */
+       for (i = 0; i < nodeCount; i++)
        {
-               POSTGIS_DEBUGF(3, "findLineSegments %p: adding segment %p %d.", root, root->segment, root->segment->type);
-
-               lwgeoms = lwalloc(sizeof(LWGEOM *));
-               lwgeoms[0] = (LWGEOM *)root->segment;
-
-               POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", root->segment, root->segment->type, FLAGS_GET_Z(root->segment->flags));
-
-               result = (LWMLINE *)lwcollection_construct(MULTILINETYPE, SRID_UNKNOWN, NULL, 1, lwgeoms);
+               nodes[i] = RTreeCreateLeafNode(pointArray, i);
        }
 
-       /* If there is a left child node, recursively include its results. */
-       if (root->leftNode)
+       /*
+        * Next we group nodes by pairs.  If there's an odd number of nodes,
+        * we bring the last node up a level as is.      Continue until we have
+        * a single top node.
+        */
+       childNodes = nodeCount;
+       parentNodes = nodeCount / 2;
+       while (parentNodes > 0)
        {
-               POSTGIS_DEBUGF(3, "findLineSegments %p: recursing left.", root);
+               POSTGIS_DEBUGF(3, "Merging %d children into %d parents.", childNodes, parentNodes);
 
-               tmp = findLineSegments(root->leftNode, value);
-               if (tmp)
+               i = 0;
+               while (i < parentNodes)
                {
-                       POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", tmp, tmp->type, FLAGS_GET_Z(tmp->flags));
-
-                       if (result)
-                               result = mergeMultiLines(result, tmp);
-                       else
-                               result = tmp;
+                       nodes[i] = RTreeCreateInteriorNode(nodes[i*2], nodes[i*2+1]);
+                       i++;
                }
-       }
-
-       /* Same for any right child. */
-       if (root->rightNode)
-       {
-               POSTGIS_DEBUGF(3, "findLineSegments %p: recursing right.", root);
-
-               tmp = findLineSegments(root->rightNode, value);
-               if (tmp)
+               /*
+                * Check for an odd numbered final node.
+                */
+               if (parentNodes * 2 < childNodes)
                {
-                       POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", tmp, tmp->type, FLAGS_GET_Z(tmp->flags));
+                       POSTGIS_DEBUGF(3, "Shuffling child %d to parent %d", childNodes - 1, i);
 
-                       if (result)
-                               result = mergeMultiLines(result, tmp);
-                       else
-                               result = tmp;
+                       nodes[i] = nodes[childNodes - 1];
+                       parentNodes++;
                }
+               childNodes = parentNodes;
+               parentNodes = parentNodes / 2;
        }
 
-       return result;
+       root = nodes[0];
+       lwfree(nodes);
+       POSTGIS_DEBUGF(3, "RTreeCreate returning %p", root);
+
+       return root;
 }
 
-/** Merges two multilinestrings into a single multilinestring. */
-LWMLINE *mergeMultiLines(LWMLINE *line1, LWMLINE *line2)
+
+/** 
+* Merges two multilinestrings into a single multilinestring. 
+*/
+static LWMLINE* 
+RTreeMergeMultiLines(LWMLINE *line1, LWMLINE *line2)
 {
        LWGEOM **geoms;
        LWCOLLECTION *col;
        int i, j, ngeoms;
 
-       POSTGIS_DEBUGF(2, "mergeMultiLines called on %p, %d, %d; %p, %d, %d", line1, line1->ngeoms, line1->type, line2, line2->ngeoms, line2->type);
+       POSTGIS_DEBUGF(2, "RTreeMergeMultiLines called on %p, %d, %d; %p, %d, %d", line1, line1->ngeoms, line1->type, line2, line2->ngeoms, line2->type);
 
        ngeoms = line1->ngeoms + line2->ngeoms;
        geoms = lwalloc(sizeof(LWGEOM *) * ngeoms);
@@ -329,101 +287,43 @@ LWMLINE *mergeMultiLines(LWMLINE *line1, LWMLINE *line2)
        }
        col = lwcollection_construct(MULTILINETYPE, SRID_UNKNOWN, NULL, ngeoms, geoms);
 
-       POSTGIS_DEBUGF(3, "mergeMultiLines returning %p, %d, %d", col, col->ngeoms, col->type);
+       POSTGIS_DEBUGF(3, "RTreeMergeMultiLines returning %p, %d, %d", col, col->ngeoms, col->type);
 
        return (LWMLINE *)col;
 }
 
-/**
- * Returns 1 if min < value <= max, 0 otherwise. */
-uint32 isContained(INTERVAL *interval, double value)
-{
-       return FP_CONTAINS_INCL(interval->min, value, interval->max) ? 1 : 0;
-}
-
-PG_FUNCTION_INFO_V1(LWGEOM_polygon_index);
-Datum LWGEOM_polygon_index(PG_FUNCTION_ARGS)
-{
-       GSERIALIZED *igeom, *result;
-       LWPOLY *poly;
-       LWMLINE *mline;
-       RTREE_NODE *root;
-       double yval;
-#if POSTGIS_DEBUG_LEVEL >= 3
-       int i = 0;
-#endif
-
-       POSTGIS_DEBUG(2, "polygon_index called.");
-
-       result = NULL;
-       igeom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       yval = PG_GETARG_FLOAT8(1);
-       if ( gserialized_get_type(igeom) != POLYGONTYPE )
-       {
-               PG_FREE_IF_COPY(igeom, 0);
-               PG_RETURN_NULL();
-       }
-       poly = lwgeom_as_lwpoly(lwgeom_from_gserialized(igeom));
-       root = createTree(poly->rings[0]);
-
-       mline = findLineSegments(root, yval);
-
-#if POSTGIS_DEBUG_LEVEL >= 3
-       POSTGIS_DEBUGF(3, "mline returned %p %d", mline, mline->type);
-       for (i = 0; i < mline->ngeoms; i++)
-       {
-               POSTGIS_DEBUGF(3, "geom[%d] %p %d", i, mline->geoms[i], mline->geoms[i]->type);
-       }
-#endif
-
-       if (mline)
-               result = geometry_serialize((LWGEOM *)mline);
-
-       POSTGIS_DEBUGF(3, "returning result %p", result);
-
-       lwfree(root);
-
-       lwpoly_free(poly);
-       lwmline_free(mline);
-       PG_FREE_IF_COPY(igeom, 0);
-       PG_RETURN_POINTER(result);
-
-}
-
-RTREE_POLY_CACHE * createCache()
-{
-       RTREE_POLY_CACHE *result;
-       result = lwalloc(sizeof(RTREE_POLY_CACHE));
-       result->polyCount = 0;
-       result->ringCounts = 0;
-       result->ringIndices = 0;
-       result->poly = 0;
-       result->type = 1;
-       return result;
-}
 
-void populateCache(RTREE_POLY_CACHE *currentCache, LWGEOM *lwgeom, GSERIALIZED *serializedPoly)
+/**
+* Callback function sent into the GetGeomCache generic caching system. Given a
+* LWGEOM* this function builds and stores an RTREE_POLY_CACHE into the provided
+* GeomCache object.
+*/
+static int 
+RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
 {
-       int i, p, r, length;
+       int i, p, r;
        LWMPOLY *mpoly;
        LWPOLY *poly;
        int nrings;
-
-       POSTGIS_DEBUGF(2, "populateCache called with cache %p geom %p", currentCache, lwgeom);
-
+       RTREE_POLY_CACHE* currentCache;
+       
+       if ( ! cache )
+               return LW_FAILURE;
+       
        if (lwgeom->type == MULTIPOLYGONTYPE)
        {
-               POSTGIS_DEBUG(2, "populateCache MULTIPOLYGON");
+               POSTGIS_DEBUG(2, "RTreeBuilder MULTIPOLYGON");
                mpoly = (LWMPOLY *)lwgeom;
                nrings = 0;
                /*
                ** Count the total number of rings.
                */
+               currentCache = RTreeCacheCreate();
                currentCache->polyCount = mpoly->ngeoms;
                currentCache->ringCounts = lwalloc(sizeof(int) * mpoly->ngeoms);
                for ( i = 0; i < mpoly->ngeoms; i++ )
                {
-                        currentCache->ringCounts[i] = mpoly->geoms[i]->nrings;
+                       currentCache->ringCounts[i] = mpoly->geoms[i]->nrings;
                        nrings += mpoly->geoms[i]->nrings;
                }
                currentCache->ringIndices = lwalloc(sizeof(RTREE_NODE *) * nrings);
@@ -436,15 +336,17 @@ void populateCache(RTREE_POLY_CACHE *currentCache, LWGEOM *lwgeom, GSERIALIZED *
                {
                        for ( r = 0; r < mpoly->geoms[p]->nrings; r++ )
                        {
-                               currentCache->ringIndices[i] = createTree(mpoly->geoms[p]->rings[r]);
+                               currentCache->ringIndices[i] = RTreeCreate(mpoly->geoms[p]->rings[r]);
                                i++;
                        }
                }
+               cache->index = (void*)currentCache;
        }
        else if ( lwgeom->type == POLYGONTYPE )
        {
-               POSTGIS_DEBUG(2, "populateCache POLYGON");
+               POSTGIS_DEBUG(2, "RTreeBuilder POLYGON");
                poly = (LWPOLY *)lwgeom;
+               currentCache = RTreeCacheCreate();
                currentCache->polyCount = 1;
                currentCache->ringCounts = lwalloc(sizeof(int));
                currentCache->ringCounts[0] = poly->nrings;
@@ -454,70 +356,122 @@ void populateCache(RTREE_POLY_CACHE *currentCache, LWGEOM *lwgeom, GSERIALIZED *
                currentCache->ringIndices = lwalloc(sizeof(RTREE_NODE *) * poly->nrings);
                for ( i = 0; i < poly->nrings; i++ )
                {
-                       currentCache->ringIndices[i] = createTree(poly->rings[i]);
+                       currentCache->ringIndices[i] = RTreeCreate(poly->rings[i]);
                }
+               cache->index = (void*)currentCache;
        }
        else
        {
                /* Uh oh, shouldn't be here. */
-               return;
+               lwerror("RTreeBuilder got asked to build index on non-polygon");
+               return LW_FAILURE;
        }
+       return LW_SUCCESS;      
+}
 
-       /*
-       ** Copy the serialized form of the polygon into the cache so
-       ** we can test for equality against subsequent polygons.
-       */
-       length = VARSIZE(serializedPoly);
-       currentCache->poly = lwalloc(length);
-       memcpy(currentCache->poly, serializedPoly, length);
-       POSTGIS_DEBUGF(3, "populateCache returning %p", currentCache);
+/**
+* Callback function sent into the GetGeomCache generic caching system. On a 
+* cache miss, this function clears the cached index object.
+*/
+static int
+RTreeFreer(GeomCache* cache)
+{
+       if ( ! cache )
+               return LW_FAILURE;
+       
+       if ( cache->index )
+       {
+               RTREE_POLY_CACHE* currentCache = (RTREE_POLY_CACHE*)(cache->index);
+               RTreeCacheClear(currentCache);
+               lwfree(currentCache);
+               cache->index = 0;
+       }
+       return LW_SUCCESS;
+}
+
+RTREE_POLY_CACHE*
+GetRtreeCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1)
+{
+       int argnum = 0;
+       RTREE_POLY_CACHE* index = NULL;
+
+       index = (RTREE_POLY_CACHE*)GetGeomIndex(fcinfo, RTREE_CACHE_ENTRY, RTreeBuilder, RTreeFreer, g1, 0, &argnum);
+
+       return index;
 }
 
+
 /**
- * Creates a new cachable index if needed, or returns the current cache if
- * it is applicable to the current polygon.
- * The memory context must be changed to function scope before calling this
- * method.     The method will allocate memory for the cache it creates,
- * as well as freeing the memory of any cache that is no longer applicable.
- */
-RTREE_POLY_CACHE *retrieveCache(LWGEOM *lwgeom, GSERIALIZED *serializedPoly, RTREE_POLY_CACHE *currentCache)
+* Retrieves a collection of line segments given the root and crossing value.
+* The collection is a multilinestring consisting of two point lines
+* representing the segments of the ring that may be crossed by the
+* horizontal projection line at the given y value.
+*/
+LWMLINE *RTreeFindLineSegments(RTREE_NODE *root, double value)
 {
-       int length;
+       LWMLINE *tmp, *result;
+       LWGEOM **lwgeoms;
 
-       POSTGIS_DEBUGF(2, "retrieveCache called with %p %p %p", lwgeom, serializedPoly, currentCache);
+       POSTGIS_DEBUGF(2, "RTreeFindLineSegments called for tree %p and value %8.3f", root, value);
 
-       assert ( ! currentCache || currentCache->type == 1 );
+       result = NULL;
 
-       if (!currentCache)
+       if (!IntervalIsContained(root->interval, value))
        {
-               POSTGIS_DEBUG(3, "No existing cache, create one.");
-               return createCache();
+               POSTGIS_DEBUGF(3, "RTreeFindLineSegments %p: not contained.", root);
+
+               return NULL;
        }
-       if (!(currentCache->poly))
+
+       /* If there is a segment defined for this node, include it. */
+       if (root->segment)
        {
-               POSTGIS_DEBUG(3, "Cache contains no polygon, populating it.");
-               populateCache(currentCache, lwgeom, serializedPoly);
-               return currentCache;
-       }
+               POSTGIS_DEBUGF(3, "RTreeFindLineSegments %p: adding segment %p %d.", root, root->segment, root->segment->type);
 
-       length = VARSIZE(serializedPoly);
+               lwgeoms = lwalloc(sizeof(LWGEOM *));
+               lwgeoms[0] = (LWGEOM *)root->segment;
 
-       if (VARSIZE(currentCache->poly) != length)
-       {
-               POSTGIS_DEBUG(3, "Polygon size mismatch, creating new cache.");
-               clearCache(currentCache);
-               return currentCache;
+               POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", root->segment, root->segment->type, FLAGS_GET_Z(root->segment->flags));
+
+               result = (LWMLINE *)lwcollection_construct(MULTILINETYPE, SRID_UNKNOWN, NULL, 1, lwgeoms);
        }
-       if ( memcmp(serializedPoly, currentCache->poly, length) )
+
+       /* If there is a left child node, recursively include its results. */
+       if (root->leftNode)
        {
-               POSTGIS_DEBUG(3, "Polygon mismatch, creating new cache.");
-               clearCache(currentCache);
-               return currentCache;
+               POSTGIS_DEBUGF(3, "RTreeFindLineSegments %p: recursing left.", root);
+
+               tmp = RTreeFindLineSegments(root->leftNode, value);
+               if (tmp)
+               {
+                       POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", tmp, tmp->type, FLAGS_GET_Z(tmp->flags));
+
+                       if (result)
+                               result = RTreeMergeMultiLines(result, tmp);
+                       else
+                               result = tmp;
+               }
        }
 
-       POSTGIS_DEBUGF(3, "Polygon match, retaining current cache, %p.",
-                         currentCache);
+       /* Same for any right child. */
+       if (root->rightNode)
+       {
+               POSTGIS_DEBUGF(3, "RTreeFindLineSegments %p: recursing right.", root);
+
+               tmp = RTreeFindLineSegments(root->rightNode, value);
+               if (tmp)
+               {
+                       POSTGIS_DEBUGF(3, "Found geom %p, type %d, dim %d", tmp, tmp->type, FLAGS_GET_Z(tmp->flags));
+
+                       if (result)
+                               result = RTreeMergeMultiLines(result, tmp);
+                       else
+                               result = tmp;
+               }
+       }
 
-       return currentCache;
+       return result;
 }
 
+
+
index 42100775480d65be6de278697669da9ae65743da..bf1ae9d768028d04b4feae2612e38c22fa3ff129 100644 (file)
@@ -1,72 +1,55 @@
 #ifndef _LWGEOM_RTREE_H
-#define _LWGEOM_RTREE_H
+#define _LWGEOM_RTREE_H 1
 
 #include "liblwgeom.h"
 
+/**
+* Representation for the y-axis interval spanned by an edge.
+*/
 typedef struct
 {
        double min;
        double max;
 }
-INTERVAL;
+RTREE_INTERVAL;
 
-/* Returns 1 if min < value <= max, 0 otherwise */
-uint32 isContained(INTERVAL *interval, double value);
-/* Creates an interval given the min and max values, in whatever order. */
-INTERVAL *createInterval(double value1, double value2);
-/* Creates an interval with the total extents of the two given intervals. */
-INTERVAL *mergeIntervals(INTERVAL *inter1, INTERVAL *inter2);
-
-/*
- * The following struct and methods are used for a 1D RTree implementation,
- * described at:
- *  http://lin-ear-th-inking.blogspot.com/2007/06/packed-1-dimensional-r-tree.html
- */
+/**
+* The following struct and methods are used for a 1D RTree implementation,
+* described at:
+*  http://lin-ear-th-inking.blogspot.com/2007/06/packed-1-dimensional-r-tree.html
+*/
 typedef struct rtree_node
 {
-       INTERVAL *interval;
+       RTREE_INTERVAL *interval;
        struct rtree_node *leftNode;
        struct rtree_node *rightNode;
        LWLINE *segment;
 }
 RTREE_NODE;
 
-/* Creates an interior node given the children. */
-RTREE_NODE *createInteriorNode(RTREE_NODE *left, RTREE_NODE *right);
-/* Creates a leaf node given the pointer to the start point of the segment. */
-RTREE_NODE *createLeafNode(POINTARRAY *pa, int startPoint);
-/*
- * Creates an rtree given a pointer to the point array.
- * Must copy the point array.
- */
-RTREE_NODE *createTree(POINTARRAY *pointArray);
-/* Frees the tree. */
-void freeTree(RTREE_NODE *root);
-/* Retrieves a collection of line segments given the root and crossing value. */
-LWMLINE *findLineSegments(RTREE_NODE *root, double value);
-/* Merges two multilinestrings into a single multilinestring. */
-LWMLINE *mergeMultiLines(LWMLINE *line1, LWMLINE *line2);
-
+/**
+* The tree structure used for fast P-i-P tests by point_in_multipolygon_rtree()
+*/
 typedef struct
 {
-       char type;
        RTREE_NODE **ringIndices;
        int* ringCounts;
        int polyCount;
-       GSERIALIZED *poly;
 }
 RTREE_POLY_CACHE;
 
-/*
- * Creates a new cachable index if needed, or returns the current cache if
- * it is applicable to the current polygon.
- */
-RTREE_POLY_CACHE *retrieveCache(LWGEOM *lwgeom, GSERIALIZED *serializedPoly, RTREE_POLY_CACHE *currentCache);
-RTREE_POLY_CACHE *createCache(void);
-/* Frees the cache. */
-void populateCache(RTREE_POLY_CACHE *cache, LWGEOM *lwgeom, GSERIALIZED *serializedPoly);
-void clearCache(RTREE_POLY_CACHE *cache);
+/**
+* Retrieves a collection of line segments given the root and crossing value. 
+*/
+LWMLINE *RTreeFindLineSegments(RTREE_NODE *root, double value);
+
 
+/**
+* Checks for a cache hit against the provided geometry and returns
+* a pre-built index structure (RTREE_POLY_CACHE) if one exists. Otherwise
+* builds a new one and returns that.
+*/
+RTREE_POLY_CACHE* GetRtreeCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1);
 
 
-#endif /* !defined _LIBLWGEOM_H */
+#endif /* !defined _LWGEOM_RTREE_H */