]> granicus.if.org Git - postgis/commitdiff
Replace variable-length-key 2D index with fixed-length-key for GSERIALIZED case.
authorPaul Ramsey <pramsey@cleverelephant.ca>
Thu, 17 Mar 2011 13:44:27 +0000 (13:44 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Thu, 17 Mar 2011 13:44:27 +0000 (13:44 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@6917 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/liblwgeom.h
postgis/Makefile.in
postgis/geography.h
postgis/geography.sql.in.c
postgis/geography_btree.c
postgis/gserialized_gist.c [new file with mode: 0644]
postgis/gserialized_gist.h [new file with mode: 0644]
postgis/gserialized_gist_2d.c [new file with mode: 0644]
postgis/gserialized_gist_nd.c [moved from postgis/geography_gist.c with 72% similarity]
postgis/postgis.sql.in.c

index 81da1db24d0aa420096b57db36c1c87d343a9606..83a853e419f36b8065dbc676825e1d3702e93fe7 100644 (file)
@@ -1806,7 +1806,7 @@ extern char* lwgeom_to_gml3(const LWGEOM *geom, char *srs, int precision, int op
 extern char* lwgeom_to_kml2(const LWGEOM *geom, int precision, const char *prefix);
 extern char* lwgeom_to_geojson(const LWGEOM *geo, char *srs, int precision, int has_bbox);
 extern char* lwgeom_to_svg(const LWGEOM *geom, int precision, int relative);
-
+extern char* lwgeom_to_x3d3(const LWGEOM *geom, char *srs, int precision, int opts, const char *defid);
 
 
 /**
index caae9c4616657934f411355e3f1cb832ac7379b3..31ab27d8b3ba1220b591572c6e9512a568eb98b4 100644 (file)
@@ -54,7 +54,9 @@ PG_OBJS=lwgeom_pg.o \
        lwgeom_sqlmm.o \
        lwgeom_rtree.o \
        geography_inout.o \
-       geography_gist.o \
+       gserialized_gist.o \
+       gserialized_gist_2d.o \
+       gserialized_gist_nd.o \
        geography_btree.o \
        geography_estimate.o \
        geography_measurement.o \
index abf9bf9708aa4ffb31a9919215bda50753408589..6a3af7619d3f51b69d0acf66bc03e5339ec34854 100644 (file)
@@ -30,7 +30,8 @@
 #define SRID_DEFAULT 4326
 
 /**********************************************************************
-**  Useful functions for all GEOGRAPHY handlers. 
+**  Useful functions for all GSERIALIZED handlers. 
+**  XXX move to gserialized.h eventually
 */
 
 /* Convert lwgeom to newly allocated gserialized */
@@ -40,77 +41,6 @@ void geography_valid_typmod(LWGEOM *lwgeom, int32 typmod);
 /* Check that the type is legal in geography (no curves please!) */
 void geography_valid_type(uchar type);
 
-
-
-/**********************************************************************
-**  GIDX structure. 
-**
-**  This is an n-dimensional (practically, the 
-**  implementation is 2-4 dimensions) box used for index keys. The 
-**  coordinates are anonymous, so we can have any number of dimensions.
-**  The sizeof a GIDX is 1 + 2 * ndims * sizeof(float).
-**  The order of values in a GIDX is
-**  xmin,xmax,ymin,ymax,zmin,zmax...
-*/
-typedef struct
-{
-       int32 varsize;
-       float c[1];
-} GIDX;
-
-/*
-** For some GiST support functions, it is more efficient to allocate
-** memory for our GIDX off the stack and cast that memory into a GIDX.
-** But, GIDX is variable length, what to do? We'll bake in the assumption
-** that no GIDX is more than 4-dimensional for now, and ensure that much
-** space is available.
-** 4 bytes varsize + 4 dimensions * 2 ordinates * 4 bytes float size = 36 bytes
-*/
-#define GIDX_MAX_SIZE 36
-
-/*********************************************************************************
-** GIDX support functions.
-**
-** We put the GIDX support here rather than libgeom because it is a specialized 
-** type only used for indexing purposes. It also makes use of some PgSQL
-** infrastructure like the VARSIZE() macros.
-*/
-
-/* Returns number of dimensions for this GIDX */
-#define GIDX_NDIMS(gidx) ((VARSIZE((gidx)) - VARHDRSZ) / (2 * sizeof(float)))
-/* Minimum accessor. */
-#define GIDX_GET_MIN(gidx, dimension) ((gidx)->c[2*(dimension)])
-/* Maximum accessor. */
-#define GIDX_GET_MAX(gidx, dimension) ((gidx)->c[2*(dimension)+1])
-/* Minimum setter. */
-#define GIDX_SET_MIN(gidx, dimension, value) ((gidx)->c[2*(dimension)] = (value))
-/* Maximum setter. */
-#define GIDX_SET_MAX(gidx, dimension, value) ((gidx)->c[2*(dimension)+1] = (value))
-/* Returns the size required to store a GIDX of requested dimension */
-#define GIDX_SIZE(dimensions) (sizeof(int32) + 2*(dimensions)*sizeof(float))
-/* Allocate a new gidx */
-GIDX* gidx_new(int ndims);
-/* Convert a gidx to a gbox */
-void gbox_from_gidx(GIDX *gidx, GBOX *gbox);
-/* Convert a gbox to a new gidx */
-GIDX* gidx_from_gbox(GBOX box);
-
-
-/* Pull out the gidx bounding box with a absolute minimum system overhead */
-int gserialized_datum_get_gidx_p(Datum gserialized_datum, GIDX *gidx);
-/* Pull out the gidx bounding box from an already de-toasted geography */
-int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx);
-/* Copy a new bounding box into an existing gserialized */
-GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx);
 /* Expand the embedded bounding box in a #GSERIALIZED */
 GSERIALIZED* gserialized_expand(GSERIALIZED *g, double distance);
 
-/* Pull out a gbox bounding box as fast as possible. */
-int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox);
-/* Given two datums, do they overlap? Computed very fast using embedded boxes. */
-int gserialized_datum_overlaps(Datum gs1, Datum gs2);
-/* Remove the box from a disk serialization */
-GSERIALIZED* gserialized_drop_gidx(GSERIALIZED *g);
-
-
-
index 64b18ff0b95e413a5cc2ca49b09dad43cfed774f..ce25393bb29c94aa4edc8ae1955d95f08359fbe9 100644 (file)
@@ -759,232 +759,5 @@ CREATE OR REPLACE FUNCTION ST_Intersection(text, text)
 
 -----------------------------------------------------------------------------
 
-#ifdef GSERIALIZED_ON
------------------------------------------------------------------------------
--- GiST GEOMETRY-over-GSERIALIZED
------------------------------------------------------------------------------
-
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_consistent_2d(internal,geometry,int4) 
-       RETURNS bool 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_consistent_2d'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_consistent(internal,geometry,int4) 
-       RETURNS bool 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_consistent'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_compress(internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME','gserialized_gist_compress'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_penalty(internal,internal,internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_penalty'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_picksplit(internal, internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_picksplit'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_union(bytea, internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_union'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_same(box2d, box2d, internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_same'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_decompress(internal) 
-       RETURNS internal 
-       AS 'MODULE_PATHNAME' ,'gserialized_gist_decompress'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_sel (internal, oid, internal, int4)
-       RETURNS float8
-       AS 'MODULE_PATHNAME', 'geometry_gist_sel'
-       LANGUAGE 'C';
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_gist_joinsel(internal, oid, internal, smallint)
-       RETURNS float8
-       AS 'MODULE_PATHNAME', 'geometry_gist_joinsel'
-       LANGUAGE 'C';
-
------------------------------------------------------------------------------
--- GEOMETRY Operators
------------------------------------------------------------------------------
-
-
--- Availability: 2.0.0
-CREATE OR REPLACE FUNCTION geometry_2d_overlaps(geometry, geometry) 
-       RETURNS boolean 
-       AS 'MODULE_PATHNAME' ,'gserialized_2d_overlaps'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR && (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_overlaps,
-       COMMUTATOR = '&&'
-       ,RESTRICT = contsel, JOIN = contjoinsel
---     ,RESTRICT = geometry_gist_sel, JOIN = geometry_gist_joinsel     
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_same(geometry, geometry) 
-       RETURNS boolean 
-       AS 'MODULE_PATHNAME' ,'gserialized_2d_same'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR ~= (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_same,
-       RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_contains(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_contains'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION geometry_2d_within(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_within'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR @ (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_within,
-       COMMUTATOR = '~',
-       RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_contains,
-       COMMUTATOR = '@',
-       RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_left(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_left'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR << (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_left,
-       COMMUTATOR = '>>',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_overleft(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_overleft'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR &< (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_overleft,
-       COMMUTATOR = '&>',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_below(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_below'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR <<| (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_below,
-       COMMUTATOR = '|>>',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_overbelow(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_overbelow'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR &<| (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_overbelow,
-       COMMUTATOR = '|&>',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_overright(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_overright'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR &> (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_overright,
-       COMMUTATOR = '&<',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_right(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_right'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR >> (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_right,
-       COMMUTATOR = '<<',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
 
-CREATE OR REPLACE FUNCTION geometry_2d_overabove(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_overabove'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR |&> (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_overabove,
-       COMMUTATOR = '&<|',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
-CREATE OR REPLACE FUNCTION geometry_2d_above(geometry, geometry)
-       RETURNS bool
-       AS 'MODULE_PATHNAME', 'gserialized_2d_above'
-       LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OPERATOR |>> (
-       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_2d_above,
-       COMMUTATOR = '<<|',
-       RESTRICT = positionsel, JOIN = positionjoinsel
-);
-
--- Availability: 2.0.0
-CREATE OPERATOR CLASS gist_geometry_ops
-       DEFAULT FOR TYPE geometry USING GIST AS
-       STORAGE gidx,
-       OPERATOR        1        <<  ,
-       OPERATOR        2        &<      ,
-       OPERATOR        3        &&  ,
-       OPERATOR        4        &>      ,
-       OPERATOR        5        >>      ,
---     OPERATOR        6        ~=      ,
-       OPERATOR        7        ~       ,
-       OPERATOR        8        @       ,
-       OPERATOR        9        &<| ,
-       OPERATOR        10       <<| ,
-       OPERATOR        11       |>> ,
-       OPERATOR        12       |&> ,
-       FUNCTION        1        geometry_gist_consistent_2d (internal, geometry, int4),
-       FUNCTION        2        geometry_gist_union (bytea, internal),
-       FUNCTION        3        geometry_gist_compress (internal),
-       FUNCTION        4        geometry_gist_decompress (internal),
-       FUNCTION        5        geometry_gist_penalty (internal, internal, internal),
-       FUNCTION        6        geometry_gist_picksplit (internal, internal),
-       FUNCTION        7        geometry_gist_same (box2d, box2d, internal);
-#endif
 
index 04b67d6c7705e794ebd27eab29d810474e970bdf..128458bd9ade346f9986548381a8733722276ae2 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "liblwgeom.h"         /* For standard geometry types. */
 #include "lwgeom_pg.h"       /* For debugging macros. */
+#include "gserialized_gist.h"
 #include "geography.h"      /* For utility functions. */
 
 Datum geography_lt(PG_FUNCTION_ARGS);
diff --git a/postgis/gserialized_gist.c b/postgis/gserialized_gist.c
new file mode 100644 (file)
index 0000000..4aba4aa
--- /dev/null
@@ -0,0 +1,136 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * Copyright 2009 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 "postgres.h"
+#include "access/gist.h"    /* For GiST */
+#include "access/itup.h"
+#include "access/skey.h"
+
+#include "../postgis_config.h"
+
+#include "liblwgeom.h"         /* For standard geometry types. */
+#include "lwgeom_pg.h"       /* For debugging macros. */
+#include "gserialized_gist.h"
+
+
+
+/**
+* Given a #GSERIALIZED datum, as quickly as possible (peaking into the top
+* of the memory) return the gbox extents. Does not deserialize the geometry,
+* but <em>WARNING</em> returns a slightly larger bounding box than actually
+* encompasses the objects. For geography objects returns geocentric bounding
+* box, for geometry objects returns cartesian bounding box.
+*/
+int 
+gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
+{
+       char gboxmem[GIDX_MAX_SIZE];
+       GIDX *gidx = (GIDX*)gboxmem;
+       
+       if( LW_FAILURE == gserialized_datum_get_gidx_p(gsdatum, gidx) )
+               return LW_FAILURE;
+               
+       gbox_from_gidx(gidx, gbox);
+       return LW_SUCCESS;
+}
+
+
+/**
+* Update the bounding box of a #GSERIALIZED, allocating a fresh one
+* if there is not enough space to just write the new box in. 
+* <em>WARNING</em> if a new object needs to be created, the 
+* input pointer will have to be freed by the caller! Check
+* to see if input == output. Returns null if there's a problem
+* like mismatched dimensions.
+*/
+GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx)
+{
+       int g_ndims = (FLAGS_GET_GEODETIC(g->flags) ? 3 : FLAGS_NDIMS(g->flags));
+       int box_ndims = GIDX_NDIMS(gidx);
+       GSERIALIZED *g_out = NULL;
+       size_t box_size = 2 * g_ndims * sizeof(float);
+
+       /* The dimensionality of the inputs has to match or we are SOL. */
+       if ( g_ndims != box_ndims )
+       {
+               return NULL;
+       }
+
+       /* Serialized already has room for a box. */
+       if ( FLAGS_GET_BBOX(g->flags) )
+       {
+               g_out = g;
+       }
+       /* Serialized has no box. We need to allocate enough space for the old
+          data plus the box, and leave a gap in the memory segment to write
+          the new values into.
+       */
+       else
+       {
+               size_t varsize_new = VARSIZE(g) + box_size;
+               uchar *ptr;
+               g_out = palloc(varsize_new);
+               /* Copy the head of g into place */
+               memcpy(g_out, g, 8);
+               /* Copy the body of g into place after leaving space for the box */
+               ptr = g_out->data;
+               ptr += box_size;
+               memcpy(ptr, g->data, VARSIZE(g) - 8);
+               FLAGS_SET_BBOX(g_out->flags, 1);
+               SET_VARSIZE(g_out, varsize_new);
+       }
+
+       /* Now write the gidx values into the memory segement */
+       memcpy(g_out->data, gidx->c, box_size);
+
+       return g_out;
+}
+
+
+/**
+* Remove the bounding box from a #GSERIALIZED. Returns a freshly
+* allocated #GSERIALIZED every time.
+*/
+GSERIALIZED* gserialized_drop_gidx(GSERIALIZED *g)
+{
+       int g_ndims = (FLAGS_GET_GEODETIC(g->flags) ? 3 : FLAGS_NDIMS(g->flags));
+       size_t box_size = 2 * g_ndims * sizeof(float);
+       size_t g_out_size = VARSIZE(g) - box_size;
+       GSERIALIZED *g_out = palloc(g_out_size);
+
+       /* Copy the contents while omitting the box */
+       if ( FLAGS_GET_BBOX(g->flags) )
+       {
+               uchar *outptr = (uchar*)g_out;
+               uchar *inptr = (uchar*)g;
+               /* Copy the header (size+type) of g into place */
+               memcpy(outptr, inptr, 8);
+               outptr += 8;
+               inptr += 8 + box_size;
+               /* Copy parts after the box into place */
+               memcpy(outptr, inptr, g_out_size - 8);
+               FLAGS_SET_BBOX(g_out->flags, 0);
+               SET_VARSIZE(g_out, g_out_size);
+       }
+       /* No box? Nothing to do but copy and return. */
+       else
+       {
+               memcpy(g_out, g, g_out_size);
+       }
+
+       return g_out;
+}
+
+
+
+
+
diff --git a/postgis/gserialized_gist.h b/postgis/gserialized_gist.h
new file mode 100644 (file)
index 0000000..f863984
--- /dev/null
@@ -0,0 +1,93 @@
+
+/**********************************************************************
+**  GIDX structure. 
+**
+**  This is an n-dimensional (practically, the 
+**  implementation is 2-4 dimensions) box used for index keys. The 
+**  coordinates are anonymous, so we can have any number of dimensions.
+**  The sizeof a GIDX is 1 + 2 * ndims * sizeof(float).
+**  The order of values in a GIDX is
+**  xmin,xmax,ymin,ymax,zmin,zmax...
+*/
+typedef struct
+{
+       int32 varsize;
+       float c[1];
+} GIDX;
+
+/*
+** For some GiST support functions, it is more efficient to allocate
+** memory for our GIDX off the stack and cast that memory into a GIDX.
+** But, GIDX is variable length, what to do? We'll bake in the assumption
+** that no GIDX is more than 4-dimensional for now, and ensure that much
+** space is available.
+** 4 bytes varsize + 4 dimensions * 2 ordinates * 4 bytes float size = 36 bytes
+*/
+#define GIDX_MAX_SIZE 36
+
+
+/**********************************************************************
+**  BOX2DF structure. 
+**
+**  This is a 2-dimensional key for simple cartesian indexes, 
+**  with backwards compatible behavior to previous indexes in
+**  PostGIS
+*/
+
+typedef struct
+{
+       float xmin, xmax, ymin, ymax;
+} BOX2DF;
+
+
+/*********************************************************************************
+** GIDX support functions.
+**
+** We put the GIDX support here rather than libgeom because it is a specialized 
+** type only used for indexing purposes. It also makes use of some PgSQL
+** infrastructure like the VARSIZE() macros.
+*/
+
+/* Convert a gidx to a gbox */
+void gbox_from_gidx(GIDX *gidx, GBOX *gbox);
+/* Convert a gbox to a new gidx */
+GIDX* gidx_from_gbox(GBOX box);
+/* Increase the size of a GIDX */
+void gidx_expand(GIDX *a, float d);
+
+/* Returns number of dimensions for this GIDX */
+#define GIDX_NDIMS(gidx) ((VARSIZE((gidx)) - VARHDRSZ) / (2 * sizeof(float)))
+/* Minimum accessor. */
+#define GIDX_GET_MIN(gidx, dimension) ((gidx)->c[2*(dimension)])
+/* Maximum accessor. */
+#define GIDX_GET_MAX(gidx, dimension) ((gidx)->c[2*(dimension)+1])
+/* Minimum setter. */
+#define GIDX_SET_MIN(gidx, dimension, value) ((gidx)->c[2*(dimension)] = (value))
+/* Maximum setter. */
+#define GIDX_SET_MAX(gidx, dimension, value) ((gidx)->c[2*(dimension)+1] = (value))
+/* Returns the size required to store a GIDX of requested dimension */
+#define GIDX_SIZE(dimensions) (sizeof(int32) + 2*(dimensions)*sizeof(float))
+
+/*********************************************************************************
+** GSERIALIZED support functions.
+**
+** Fast functions for pulling boxes out of serializations.
+*/
+
+/* Pull out the #GIDX bounding box with a absolute minimum system overhead */
+int gserialized_datum_get_gidx_p(Datum gserialized_datum, GIDX *gidx);
+
+/* Pull out the gidx bounding box from an already de-toasted geography */
+int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx);
+/* Copy a new bounding box into an existing gserialized */
+GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx);
+
+/* Pull out a gbox bounding box as fast as possible. */
+int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox);
+/* Given two datums, do they overlap? Computed very fast using embedded boxes. */
+int gserialized_datum_overlaps(Datum gs1, Datum gs2);
+/* Remove the box from a disk serialization */
+GSERIALIZED* gserialized_drop_gidx(GSERIALIZED *g);
+
+
+
diff --git a/postgis/gserialized_gist_2d.c b/postgis/gserialized_gist_2d.c
new file mode 100644 (file)
index 0000000..965c6ed
--- /dev/null
@@ -0,0 +1,1103 @@
+/**********************************************************************
+ * $Id: gserialized_gist_2d.c 6519 2010-12-28 00:54:19Z pramsey $
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * Copyright 2009 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.
+ *
+ **********************************************************************/
+
+/*
+** R-Tree Bibliography
+**
+** [1] A. Guttman. R-tree: a dynamic index structure for spatial searching.
+**     Proceedings of the ACM SIGMOD Conference, pp 47-57, June 1984.
+** [2] C.-H. Ang and T. C. Tan. New linear node splitting algorithm for
+**     R-Trees. Advances in Spatial Databases - 5th International Symposium,
+**     1997
+** [3] N. Beckmann, H.-P. Kriegel, R. Schneider, B. Seeger. The R*tree: an
+**     efficient and robust access method for points and rectangles.
+**     Proceedings of the ACM SIGMOD Conference. June 1990.
+*/
+
+#include "postgres.h"
+#include "access/gist.h"    /* For GiST */
+#include "access/itup.h"
+#include "access/skey.h"
+
+#include "../postgis_config.h"
+
+#include "liblwgeom.h"         /* For standard geometry types. */
+#include "lwgeom_pg.h"       /* For debugging macros. */
+#include "gserialized_gist.h"       /* For utility functions. */
+
+/*
+** When is a node split not so good? If more than 90% of the entries
+** end up in one of the children.
+*/
+#define LIMIT_RATIO 0.1
+
+/*
+** For debugging
+*/
+#if POSTGIS_DEBUG_LEVEL > 0
+static int g2d_counter_leaf = 0;
+static int g2d_counter_internal = 0;
+#endif
+
+/*
+** GiST 2D key stubs 
+*/
+Datum box2df_out(PG_FUNCTION_ARGS);
+Datum box2df_in(PG_FUNCTION_ARGS);
+
+/*
+** GiST 2D index function prototypes
+*/
+Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS);
+Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS);
+
+/*
+** GiST 2D operator prototypes
+*/
+Datum gserialized_same_2d(PG_FUNCTION_ARGS);
+Datum gserialized_within_2d(PG_FUNCTION_ARGS);
+Datum gserialized_contains_2d(PG_FUNCTION_ARGS);
+Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS);
+Datum gserialized_left_2d(PG_FUNCTION_ARGS);
+Datum gserialized_right_2d(PG_FUNCTION_ARGS);
+Datum gserialized_above_2d(PG_FUNCTION_ARGS);
+Datum gserialized_below_2d(PG_FUNCTION_ARGS);
+Datum gserialized_overleft_2d(PG_FUNCTION_ARGS);
+Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
+Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
+Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
+
+/*
+** true/false test function type
+*/
+typedef bool (*box2df_predicate)(BOX2DF *a, BOX2DF *b);
+
+
+#if POSTGIS_DEBUG_LEVEL > 0
+static char* box2df_to_string(const BOX2DF *a)
+{
+       char *rv = NULL;
+
+       if ( a == NULL )
+               return pstrdup("<NULLPTR>");
+
+       rv = palloc(128);
+       sprintf(rv, "BOX2DF(%.12g %.12g, %.12g %.12g)", a->xmin, a->ymin, a->xmax, a->ymax);
+       return rv;
+}
+#endif
+
+
+/* Allocate a new copy of BOX2DF */
+static BOX2DF* box2df_copy(BOX2DF *b)
+{
+       BOX2DF *c = (BOX2DF*)palloc(sizeof(BOX2DF));
+       memcpy((void*)c, (void*)b, sizeof(BOX2DF));
+       POSTGIS_DEBUGF(5, "copied box2df (%p) to box2df (%p)", b, c);
+       return c;
+}
+
+
+
+/* Enlarge b_union to contain b_new. If b_new contains more
+   dimensions than b_union, expand b_union to contain those dimensions. */
+static void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
+{
+
+       POSTGIS_DEBUGF(5, "merging %s with %s", box2df_to_string(b_union), box2df_to_string(b_new));
+       /* Adjust minimums */
+       b_union->xmin = Min(b_union->xmin, b_new->xmin);
+       b_union->ymin = Min(b_union->ymin, b_new->ymin);
+       /* Adjust maximums */
+       b_union->xmax = Max(b_union->xmax, b_new->xmax);
+       b_union->ymax = Max(b_union->ymax, b_new->ymax);
+
+       POSTGIS_DEBUGF(5, "merge complete %s", box2df_to_string(b_union));
+       return;
+}
+
+
+
+static bool box2df_intersection(const BOX2DF *a, const BOX2DF *b, BOX2DF *n)
+{
+       POSTGIS_DEBUGF(5, "calculating intersection of %s with %s", box2df_to_string(a), box2df_to_string(b));
+
+       if( a == NULL || b == NULL || n == NULL ) 
+               return FALSE;
+               
+       n->xmax = Min(a->xmax, b->xmax);
+       n->ymax = Min(a->ymax, b->ymax);
+       n->xmin = Max(a->xmin, b->xmin);
+       n->ymin = Max(a->ymin, b->ymin);
+
+       POSTGIS_DEBUGF(5, "intersection is %s", box2df_to_string(n));
+
+       if ( (n->xmax < n->xmin) || (n->ymax < n->ymin) )
+               return FALSE;
+
+       return TRUE;
+}
+
+static float box2df_size(const BOX2DF *a)
+{
+       float result;
+
+       if ( a == NULL ) 
+               return (float)0.0;
+               
+       if ( (a->xmax <= a->xmin) || (a->ymax <= a->ymin) )
+       {
+               result =  (float) 0.0;
+       }
+       else
+       {
+               result = (((double) a->xmax)-((double) a->xmin)) * (((double) a->ymax)-((double) a->ymin));
+       }
+
+       return result;
+}
+
+static float box2df_union_size(const BOX2DF *a, const BOX2DF *b)
+{
+       float result;
+
+       POSTGIS_DEBUG(5,"entered function");
+
+       if ( a == NULL && b == NULL )
+       {
+               elog(ERROR, "box2df_union_size received two null arguments");
+               return 0.0;
+       }
+       
+       if ( a == NULL )
+               return box2df_size(b);
+
+       if ( b == NULL )
+               return box2df_size(a);
+
+       result = ((double)Max(a->xmax,b->xmax) - (double)Min(a->xmin,b->xmin)) * 
+                ((double)Max(a->ymax,b->ymax) - (double)Min(a->ymin,b->ymin));
+
+       POSTGIS_DEBUGF(5, "union size of %s and %s is %.8g", box2df_to_string(a), box2df_to_string(b), result);
+
+       return result;
+}
+
+
+/* Convert a double-based GBOX into a float-based BOX2DF,
+   ensuring the float box is larger than the double box */
+static inline int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
+{
+       a->xmin = next_float_down(box->xmin);
+       a->xmax = next_float_up(box->xmax);
+       a->ymin = next_float_down(box->ymin);
+       a->ymax = next_float_up(box->ymax);
+       return LW_SUCCESS;
+}
+
+/***********************************************************************
+** BOX3DF tests for 2D index operators.
+*/
+
+/* Ensure all minimums are below maximums. */
+static inline void box2df_validate(BOX2DF *b)
+{
+       float tmp;
+       POSTGIS_DEBUGF(5,"validating box2df (%s)", box2df_to_string(b));
+       if ( b->xmax < b->xmin ) 
+       {
+               tmp = b->xmin;
+               b->xmin = b->xmax;
+               b->xmax = tmp;
+       }
+       if ( b->ymax < b->ymin ) 
+       {
+               tmp = b->ymin;
+               b->ymin = b->ymax;
+               b->ymax = tmp;
+       }
+       return;
+}
+
+static bool box2df_overlaps(BOX2DF *a, BOX2DF *b)
+{
+       if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
+            (a->ymin > b->ymax) || (b->ymin > a->ymax) )
+       {
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static bool box2df_contains(BOX2DF *a, BOX2DF *b)
+{
+       if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
+            (a->ymin > b->ymin) || (a->ymax < b->ymax) )
+       {
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static bool box2df_within(BOX2DF *a, BOX2DF *b)
+{
+       POSTGIS_DEBUG(5, "entered function");
+       return box2df_contains(b,a);
+}
+
+static bool box2df_equals(BOX2DF *a, BOX2DF *b)
+{
+       if ( (a->xmin != b->xmin) || (a->xmax != b->xmax) ||
+            (a->ymin != b->ymin) || (a->ymax != b->ymax) )
+       {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static bool box2df_overleft(BOX2DF *a, BOX2DF *b)
+{
+       /* a.xmax <= b.xmax */
+       return a->xmax <= b->xmax;
+}
+
+static bool box2df_left(BOX2DF *a, BOX2DF *b)
+{
+       /* a.xmax < b.xmin */
+       return a->xmax < b->xmin;
+}
+
+static bool box2df_right(BOX2DF *a, BOX2DF *b)
+{
+       /* a.xmin > b.xmax */
+       return a->xmin > b->xmax;
+}
+
+static bool box2df_overright(BOX2DF *a, BOX2DF *b)
+{
+       /* a.xmin >= b.xmin */
+       return a->xmin >= b->xmin;
+}
+
+static bool box2df_overbelow(BOX2DF *a, BOX2DF *b)
+{
+       /* a.ymax <= b.ymax */
+       return a->ymax <= b->ymax;
+}
+
+static bool box2df_below(BOX2DF *a, BOX2DF *b)
+{
+       /* a.ymax < b.ymin */
+       return a->ymax < b->ymin;
+}
+
+static bool box2df_above(BOX2DF *a, BOX2DF *b)
+{
+       /* a.ymin > b.ymax */
+       return a->ymin > b->ymax;
+}
+
+static bool box2df_overabove(BOX2DF *a, BOX2DF *b)
+{
+       /* a.ymin >= b.ymin */
+       return a->ymin >= b->ymin;
+}
+
+
+/**
+* Peak into a #GSERIALIZED datum to find the bounding box. If the
+* box is there, copy it out and return it. If not, calculate the box from the
+* full object and return the box based on that. If no box is available,
+* return #LW_FAILURE, otherwise #LW_SUCCESS.
+*/
+static int 
+gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
+{
+       GSERIALIZED *gpart;
+       uchar flags;
+       int result = LW_SUCCESS;
+
+       POSTGIS_DEBUG(4, "entered function");
+
+       /*
+       ** The most info we need is the 8 bytes of serialized header plus the 
+       ** of floats necessary to hold the bounding box.
+       */
+       gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(gsdatum, 0, 8 + sizeof(BOX2DF));
+       flags = gpart->flags;
+
+       POSTGIS_DEBUGF(4, "got flags %d", gpart->flags);
+
+       /* Do we even have a serialized bounding box? */
+       if ( FLAGS_GET_BBOX(flags) )
+       {
+               /* Yes! Copy it out into the box! */
+               POSTGIS_DEBUG(4, "copying box out of serialization");
+               memcpy(box2df, gpart->data, sizeof(BOX2DF));
+               result = LW_SUCCESS;
+       }
+       else
+       {
+               /* No, we need to calculate it from the full object. */
+               GBOX gbox;
+               GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum);
+               LWGEOM *lwgeom = lwgeom_from_gserialized(g);
+               if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE )
+               {
+                       POSTGIS_DEBUG(4, "could not calculate bbox, returning failure");
+                       lwgeom_free(lwgeom);
+                       return LW_FAILURE;
+               }
+               lwgeom_free(lwgeom);
+               result = box2df_from_gbox_p(&gbox, box2df);
+       }
+       
+       if ( result == LW_SUCCESS )
+       {
+               POSTGIS_DEBUGF(4, "got box2df %s", box2df_to_string(box2df));
+       }
+
+       return result;
+}
+
+
+/**
+* Support function. Based on two datums return true if
+* they satisfy the predicate and false otherwise.
+*/
+static int 
+gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
+{
+       BOX2DF b1, b2;
+       
+       POSTGIS_DEBUG(3, "entered function");
+
+       /* Must be able to build box for each arguement (ie, not empty geometry)
+          and overlap boxes to return true. */
+       if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
+            (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) &&
+             predicate(&b1, &b2) )
+       {
+               POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
+               return LW_TRUE;
+       }
+       return LW_FALSE;
+}
+
+
+
+
+/***********************************************************************
+* GiST 2-D Index Operator Functions
+*/
+
+PG_FUNCTION_INFO_V1(gserialized_same_2d);
+Datum gserialized_same_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_equals) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_within_2d);
+Datum gserialized_within_2d(PG_FUNCTION_ARGS)
+{
+       POSTGIS_DEBUG(3, "entered function");
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_contains_2d);
+Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
+{
+       POSTGIS_DEBUG(3, "entered function");
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_overlaps_2d);
+Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_left_2d);
+Datum gserialized_left_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_left) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_right_2d);
+Datum gserialized_right_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_right) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_above_2d);
+Datum gserialized_above_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_above) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_below_2d);
+Datum gserialized_below_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_below) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_overleft_2d);
+Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overleft) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_overright_2d);
+Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overright) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_overabove_2d);
+Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overabove) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_overbelow_2d);
+Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
+{
+       if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overbelow) == LW_TRUE )
+               PG_RETURN_BOOL(TRUE);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+
+/***********************************************************************
+* GiST Index  Support Functions
+*/
+
+/*
+** GiST support function. Given a geography, return a "compressed"
+** version. In this case, we convert the geography into a geocentric
+** bounding box. If the geography already has the box embedded in it
+** we pull that out and hand it back.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_compress_2d);
+Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0);
+       GISTENTRY *entry_out = NULL;
+       BOX2DF bbox_out;
+       int result = LW_SUCCESS;
+
+       POSTGIS_DEBUG(4, "[GIST] 'compress' function called");
+
+       /*
+       ** Not a leaf key? There's nothing to do.
+       ** Return the input unchanged.
+       */
+       if ( ! entry_in->leafkey )
+       {
+               POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered");
+               PG_RETURN_POINTER(entry_in);
+       }
+
+       POSTGIS_DEBUG(4, "[GIST] processing leafkey input");
+       entry_out = palloc(sizeof(GISTENTRY));
+
+       /*
+       ** Null key? Make a copy of the input entry and
+       ** return.
+       */
+       if ( DatumGetPointer(entry_in->key) == NULL )
+       {
+               POSTGIS_DEBUG(4, "[GIST] leafkey is null");
+               gistentryinit(*entry_out, (Datum) 0, entry_in->rel,
+                             entry_in->page, entry_in->offset, FALSE);
+               POSTGIS_DEBUG(4, "[GIST] returning copy of input");
+               PG_RETURN_POINTER(entry_out);
+       }
+
+       /* Extract our index key from the GiST entry. */
+       result = gserialized_datum_get_box2df_p(entry_in->key, &bbox_out);
+
+       /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
+       if ( result == LW_FAILURE )
+       {
+               POSTGIS_DEBUG(4, "[GIST] empty geometry!");
+               PG_RETURN_POINTER(entry_in);
+       }
+
+       POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
+
+       /* Check all the dimensions for finite values */
+       if ( ! finite(bbox_out.xmax) || ! finite(bbox_out.xmin) ||
+            ! finite(bbox_out.ymax) || ! finite(bbox_out.ymin) )
+       {
+               POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
+               PG_RETURN_POINTER(entry_in);
+       }
+
+       /* Enure bounding box has minimums below maximums. */
+       box2df_validate(&bbox_out);
+
+       /* Prepare GISTENTRY for return. */
+       gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
+                     entry_in->rel, entry_in->page, entry_in->offset, FALSE);
+
+       /* Return GISTENTRY. */
+       POSTGIS_DEBUG(4, "[GIST] 'compress' function complete");
+       PG_RETURN_POINTER(entry_out);
+}
+
+
+/*
+** GiST support function.
+** Decompress an entry. Unused for geography, so we return.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_decompress_2d);
+Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
+{
+       POSTGIS_DEBUG(5, "[GIST] 'decompress' function called");
+       /* We don't decompress. Just return the input. */
+       PG_RETURN_POINTER(PG_GETARG_POINTER(0));
+}
+
+
+
+/*
+** GiST support function. Called from gserialized_gist_consistent below.
+*/
+static inline bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
+{
+       bool retval;
+
+       POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
+                      strategy, geog_counter_leaf++);
+
+       switch (strategy)
+       {
+       case RTOverlapStrategyNumber:
+               retval = (bool) box2df_overlaps(key, query);
+               break;
+       case RTSameStrategyNumber:
+               retval = (bool) box2df_equals(key, query);
+               break;
+       case RTContainsStrategyNumber:
+       case RTOldContainsStrategyNumber:
+               retval = (bool) box2df_contains(key, query);
+               break;
+       case RTContainedByStrategyNumber:
+       case RTOldContainedByStrategyNumber:
+               retval = (bool) box2df_contains(query, key);
+               break;
+       default:
+               retval = FALSE;
+       }
+
+       return (retval);
+}
+
+/*
+** GiST support function. Called from gserialized_gist_consistent below.
+*/
+static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
+{
+       bool retval;
+
+       POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
+                      strategy, geog_counter_internal++, box2df_to_string(query), box2df_to_string(key) );
+
+       switch (strategy)
+       {
+       case RTOverlapStrategyNumber:
+               retval = (bool) box2df_overlaps(key, query);
+               break;
+       case RTSameStrategyNumber:
+       case RTContainsStrategyNumber:
+       case RTOldContainsStrategyNumber:
+               retval = (bool) box2df_contains(key, query);
+               break;
+       case RTContainedByStrategyNumber:
+       case RTOldContainedByStrategyNumber:
+               retval = (bool) box2df_overlaps(key, query);
+               break;
+       default:
+               retval = FALSE;
+       }
+
+       return (retval);
+}
+
+/*
+** GiST support function. Take in a query and an entry and see what the
+** relationship is, based on the query strategy.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_consistent_2d);
+Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
+       StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+       bool result;
+       BOX2DF query_gbox_index;
+
+#if POSTGIS_PGSQL_VERSION >= 84
+       /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
+          rather than being supplied as part of the operator class definition */
+       bool *recheck = (bool *) PG_GETARG_POINTER(4);
+
+       /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
+          out during index scans. For cases when the geometries are large, rechecking
+          can make things twice as slow. */
+       *recheck = false;
+#endif
+
+       POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
+
+       /* Quick sanity check on query argument. */
+       if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
+       {
+               POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
+               PG_RETURN_BOOL(FALSE); /* NULL query! This is screwy! */
+       }
+
+       /* Quick sanity check on entry key. */
+       if ( DatumGetPointer(entry->key) == NULL )
+       {
+               POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
+               PG_RETURN_BOOL(FALSE); /* NULL entry! */
+       }
+
+       /* Null box should never make this far. */
+       if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_gbox_index) == LW_FAILURE )
+       {
+               POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       /* Treat leaf node tests different from internal nodes */
+       if (GIST_LEAF(entry))
+       {
+               result = gserialized_gist_consistent_leaf_2d(
+                            (BOX2DF*)DatumGetPointer(entry->key),
+                            &query_gbox_index, strategy);
+       }
+       else
+       {
+               result = gserialized_gist_consistent_internal_2d(
+                            (BOX2DF*)DatumGetPointer(entry->key),
+                            &query_gbox_index, strategy);
+       }
+
+       PG_RETURN_BOOL(result);
+}
+
+/*
+** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
+** Calculate the change in volume of the old entry once the new entry is added.
+** TODO: Re-evaluate this in light of R*Tree penalty approaches.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_penalty_2d);
+Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0);
+       GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1);
+       float *result = (float*) PG_GETARG_POINTER(2);
+       BOX2DF *gbox_index_orig, *gbox_index_new;
+       float size_union, size_orig;
+
+       POSTGIS_DEBUG(4, "[GIST] 'penalty' function called");
+
+       gbox_index_orig = (BOX2DF*)DatumGetPointer(origentry->key);
+       gbox_index_new = (BOX2DF*)DatumGetPointer(newentry->key);
+
+       /* Drop out if we're dealing with null inputs. Shouldn't happen. */
+       if ( (gbox_index_orig == NULL) && (gbox_index_new == NULL) )
+       {
+               POSTGIS_DEBUG(4, "[GIST] both inputs NULL! returning penalty of zero");
+               *result = 0.0;
+               PG_RETURN_FLOAT8(*result);
+       }
+
+       /* Calculate the size difference of the boxes. */
+       size_union = box2df_union_size(gbox_index_orig, gbox_index_new);
+       size_orig = box2df_size(gbox_index_orig);
+       *result = size_union - size_orig;
+
+       POSTGIS_DEBUGF(4, "[GIST] 'penalty', union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result);
+
+       PG_RETURN_POINTER(result);
+}
+
+/*
+** GiST support function. Merge all the boxes in a page into one master box.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_union_2d);
+Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
+{
+       GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+       int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */
+       int     numranges, i;
+       BOX2DF *box_cur, *box_union;
+
+       POSTGIS_DEBUG(4, "[GIST] 'union' function called");
+
+       numranges = entryvec->n;
+
+       box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[0].key);
+
+       box_union = box2df_copy(box_cur);
+
+       for ( i = 1; i < numranges; i++ )
+       {
+               box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
+               box2df_merge(box_union, box_cur);
+       }
+
+       *sizep = sizeof(BOX2DF);
+
+       POSTGIS_DEBUGF(4, "[GIST] 'union', numranges(%i), pageunion %s", numranges, box2df_to_string(box_union));
+
+       PG_RETURN_POINTER(box_union);
+
+}
+
+/*
+** GiST support function. Test equality of keys.
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_same_2d);
+Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
+{
+       BOX2DF *b1 = (BOX2DF*)PG_GETARG_POINTER(0);
+       BOX2DF *b2 = (BOX2DF*)PG_GETARG_POINTER(1);
+       bool *result = (bool*)PG_GETARG_POINTER(2);
+
+       POSTGIS_DEBUG(4, "[GIST] 'same' function called");
+
+       *result = box2df_equals(b1, b2);
+
+       PG_RETURN_POINTER(result);
+}
+
+typedef struct
+{
+       BOX2DF *key;
+       int pos;
+}
+KBsort;
+
+static int
+compare_KB(const void* a, const void* b)
+{
+       BOX2DF *abox = ((KBsort*)a)->key;
+       BOX2DF *bbox = ((KBsort*)b)->key;
+       float sa = (abox->xmax - abox->xmin) * (abox->ymax - abox->ymin);
+       float sb = (bbox->xmax - bbox->xmin) * (bbox->ymax - bbox->ymin);
+
+       if ( sa==sb ) return 0;
+       return ( sa>sb ) ? 1 : -1;
+}
+
+/**
+** The GiST PickSplit method
+** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
+** C.H.Ang and T.C.Tan
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_picksplit_2d);
+Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
+{
+       GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+
+       GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+       OffsetNumber i;
+       OffsetNumber *listL, *listR, *listB, *listT;
+       BOX2DF *unionL, *unionR, *unionB, *unionT;
+       int posL, posR, posB, posT;
+       BOX2DF pageunion;
+       BOX2DF *cur;
+       char direction = ' ';
+       bool allisequal = true;
+       OffsetNumber maxoff;
+       int nbytes;
+
+       POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
+
+       posL = posR = posB = posT = 0;
+
+       maxoff = entryvec->n - 1;
+       cur = (BOX2DF*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);
+
+       memcpy((void *) &pageunion, (void *) cur, sizeof(BOX2DF));
+
+       /* find MBR */
+       for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
+       {
+               cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
+
+               if ( allisequal == true &&  (
+                           pageunion.xmax != cur->xmax ||
+                           pageunion.ymax != cur->ymax ||
+                           pageunion.xmin != cur->xmin ||
+                           pageunion.ymin != cur->ymin
+                       ) )
+                       allisequal = false;
+
+               if (pageunion.xmax < cur->xmax)
+                       pageunion.xmax = cur->xmax;
+               if (pageunion.xmin > cur->xmin)
+                       pageunion.xmin = cur->xmin;
+               if (pageunion.ymax < cur->ymax)
+                       pageunion.ymax = cur->ymax;
+               if (pageunion.ymin > cur->ymin)
+                       pageunion.ymin = cur->ymin;
+       }
+
+       POSTGIS_DEBUGF(4, "pageunion is %s", box2df_to_string(&pageunion));
+
+       nbytes = (maxoff + 2) * sizeof(OffsetNumber);
+       listL = (OffsetNumber *) palloc(nbytes);
+       listR = (OffsetNumber *) palloc(nbytes);
+       unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
+       unionR = (BOX2DF *) palloc(sizeof(BOX2DF));
+
+       if (allisequal)
+       {
+               POSTGIS_DEBUG(4, " AllIsEqual!");
+
+               cur = (BOX2DF*) DatumGetPointer(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
+
+
+               if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX2DF)) == 0)
+               {
+                       v->spl_left = listL;
+                       v->spl_right = listR;
+                       v->spl_nleft = v->spl_nright = 0;
+                       memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX2DF));
+                       memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX2DF));
+
+                       for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+                       {
+                               if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
+                               {
+                                       v->spl_left[v->spl_nleft] = i;
+                                       v->spl_nleft++;
+                               }
+                               else
+                               {
+                                       v->spl_right[v->spl_nright] = i;
+                                       v->spl_nright++;
+                               }
+                       }
+                       v->spl_ldatum = PointerGetDatum(unionL);
+                       v->spl_rdatum = PointerGetDatum(unionR);
+
+                       PG_RETURN_POINTER(v);
+               }
+       }
+
+       listB = (OffsetNumber *) palloc(nbytes);
+       listT = (OffsetNumber *) palloc(nbytes);
+       unionB = (BOX2DF *) palloc(sizeof(BOX2DF));
+       unionT = (BOX2DF *) palloc(sizeof(BOX2DF));
+
+#define ADDLIST( list, unionD, pos, num ) do { \
+       if ( pos ) { \
+               if ( unionD->xmax < cur->xmax )    unionD->xmax = cur->xmax; \
+               if ( unionD->xmin       > cur->xmin  ) unionD->xmin     = cur->xmin; \
+               if ( unionD->ymax < cur->ymax )    unionD->ymax = cur->ymax; \
+               if ( unionD->ymin       > cur->ymin  ) unionD->ymin     = cur->ymin; \
+       } else { \
+                       memcpy( (void*)unionD, (void*) cur, sizeof( BOX2DF ) );  \
+       } \
+       list[pos] = num; \
+       (pos)++; \
+} while(0)
+
+       for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+       {
+               cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
+
+               if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
+                       ADDLIST(listL, unionL, posL,i);
+               else
+                       ADDLIST(listR, unionR, posR,i);
+               if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
+                       ADDLIST(listB, unionB, posB,i);
+               else
+                       ADDLIST(listT, unionT, posT,i);
+       }
+
+       POSTGIS_DEBUGF(4, "unionL is %s", box2df_to_string(unionL));
+       POSTGIS_DEBUGF(4, "unionR is %s", box2df_to_string(unionR));
+       POSTGIS_DEBUGF(4, "unionT is %s", box2df_to_string(unionT));
+       POSTGIS_DEBUGF(4, "unionB is %s", box2df_to_string(unionB));
+
+       /* bad disposition, sort by ascending and resplit */
+       if ( (posR==0 || posL==0) && (posT==0 || posB==0) )
+       {
+               KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff );
+               posL = posR = posB = posT = 0;
+               for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+               {
+                       arr[i-1].key = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
+                       arr[i-1].pos = i;
+               }
+               qsort( arr, maxoff, sizeof(KBsort), compare_KB );
+               for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+               {
+                       cur = arr[i-1].key;
+                       if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
+                               ADDLIST(listL, unionL, posL,arr[i-1].pos);
+                       else if ( cur->xmin - pageunion.xmin == pageunion.xmax - cur->xmax )
+                       {
+                               if ( posL>posR )
+                                       ADDLIST(listR, unionR, posR,arr[i-1].pos);
+                               else
+                                       ADDLIST(listL, unionL, posL,arr[i-1].pos);
+                       }
+                       else
+                               ADDLIST(listR, unionR, posR,arr[i-1].pos);
+
+                       if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
+                               ADDLIST(listB, unionB, posB,arr[i-1].pos);
+                       else if ( cur->ymin - pageunion.ymin == pageunion.ymax - cur->ymax )
+                       {
+                               if ( posB>posT )
+                                       ADDLIST(listT, unionT, posT,arr[i-1].pos);
+                               else
+                                       ADDLIST(listB, unionB, posB,arr[i-1].pos);
+                       }
+                       else
+                               ADDLIST(listT, unionT, posT,arr[i-1].pos);
+               }
+               pfree(arr);
+       }
+
+       /* which split more optimal? */
+       if (Max(posL, posR) < Max(posB, posT))
+               direction = 'x';
+       else if (Max(posL, posR) > Max(posB, posT))
+               direction = 'y';
+       else
+       {
+               float sizeLR, sizeBT;
+               BOX2DF interLR, interBT;
+               
+               if ( box2df_intersection(unionL, unionR, &interLR) == FALSE )
+                       sizeLR = 0.0;
+               else
+                       sizeLR = box2df_size(&interLR);
+
+               if ( box2df_intersection(unionB, unionT, &interBT) == FALSE )
+                       sizeBT = 0.0;
+               else
+                       sizeBT = box2df_size(&interBT);
+               
+               if (sizeLR < sizeBT)
+                       direction = 'x';
+               else
+                       direction = 'y';
+       }
+
+       POSTGIS_DEBUGF(4, "split direction '%c'", direction);
+
+       if (direction == 'x')
+       {
+               pfree(unionB);
+               pfree(listB);
+               pfree(unionT);
+               pfree(listT);
+
+               v->spl_left = listL;
+               v->spl_right = listR;
+               v->spl_nleft = posL;
+               v->spl_nright = posR;
+               v->spl_ldatum = PointerGetDatum(unionL);
+               v->spl_rdatum = PointerGetDatum(unionR);
+       }
+       else
+       {
+               pfree(unionR);
+               pfree(listR);
+               pfree(unionL);
+               pfree(listL);
+
+               v->spl_left = listB;
+               v->spl_right = listT;
+               v->spl_nleft = posB;
+               v->spl_nright = posT;
+               v->spl_ldatum = PointerGetDatum(unionB);
+               v->spl_rdatum = PointerGetDatum(unionT);
+       }
+       
+       POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
+       
+       PG_RETURN_POINTER(v);
+}
+
+
+/*
+** The BOX32DF key must be defined as a PostgreSQL type, even though it is only
+** ever used internally. These no-op stubs are used to bind the type.
+*/
+PG_FUNCTION_INFO_V1(box2df_in);
+Datum box2df_in(PG_FUNCTION_ARGS)
+{
+       ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                      errmsg("function box2df_in not implemented")));
+       PG_RETURN_POINTER(NULL);
+}
+
+PG_FUNCTION_INFO_V1(box2df_out);
+Datum box2df_out(PG_FUNCTION_ARGS)
+{
+       ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                      errmsg("function box2df_out not implemented")));
+       PG_RETURN_POINTER(NULL);
+}
similarity index 72%
rename from postgis/geography_gist.c
rename to postgis/gserialized_gist_nd.c
index 92fce9c53d750db51ca1e4357c922ec215239087..91b24d5e99ab07ef0786374c79fad4d800a011da 100644 (file)
@@ -1,5 +1,5 @@
 /**********************************************************************
- * $Id$
+ * $Id: gserialized_gist_nd.c 6519 2010-12-28 00:54:19Z pramsey $
  *
  * PostGIS - Spatial Types for PostgreSQL
  * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
@@ -31,7 +31,8 @@
 
 #include "liblwgeom.h"         /* For standard geometry types. */
 #include "lwgeom_pg.h"       /* For debugging macros. */
-#include "geography.h"      /* For utility functions. */
+#include "gserialized_gist.h"       /* For utility functions. */
+#include "geography.h"
 
 /*
 ** When is a node split not so good? If more than 90% of the entries
@@ -48,10 +49,15 @@ static int geog_counter_internal = 0;
 #endif
 
 /*
-** GiST prototypes
+** ND Index key type stub prototypes
+*/
+Datum gidx_out(PG_FUNCTION_ARGS);
+Datum gidx_in(PG_FUNCTION_ARGS);
+
+/*
+** ND GiST prototypes
 */
 Datum gserialized_gist_consistent(PG_FUNCTION_ARGS);
-Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
 Datum gserialized_gist_compress(PG_FUNCTION_ARGS);
 Datum gserialized_gist_decompress(PG_FUNCTION_ARGS);
 Datum gserialized_gist_penalty(PG_FUNCTION_ARGS);
@@ -60,69 +66,21 @@ Datum gserialized_gist_union(PG_FUNCTION_ARGS);
 Datum gserialized_gist_same(PG_FUNCTION_ARGS);
 
 /*
-** Index key type stub prototypes
-*/
-Datum gidx_out(PG_FUNCTION_ARGS);
-Datum gidx_in(PG_FUNCTION_ARGS);
-
-/*
-** Operator prototypes
+** ND Operator prototypes
 */
 Datum gserialized_overlaps(PG_FUNCTION_ARGS);
 Datum gserialized_contains(PG_FUNCTION_ARGS);
 Datum gserialized_within(PG_FUNCTION_ARGS);
 
 /*
-** 2D Operator prototypes
-*/
-Datum gserialized_2d_same(PG_FUNCTION_ARGS);
-Datum gserialized_2d_within(PG_FUNCTION_ARGS);
-Datum gserialized_2d_contains(PG_FUNCTION_ARGS);
-Datum gserialized_2d_overlaps(PG_FUNCTION_ARGS);
-Datum gserialized_2d_left(PG_FUNCTION_ARGS);
-Datum gserialized_2d_right(PG_FUNCTION_ARGS);
-Datum gserialized_2d_above(PG_FUNCTION_ARGS);
-Datum gserialized_2d_below(PG_FUNCTION_ARGS);
-Datum gserialized_2d_overleft(PG_FUNCTION_ARGS);
-Datum gserialized_2d_overright(PG_FUNCTION_ARGS);
-Datum gserialized_2d_overabove(PG_FUNCTION_ARGS);
-Datum gserialized_2d_overbelow(PG_FUNCTION_ARGS);
-
-
-/*
-** GIDX true/fast test function type
+** GIDX true/false test function type
 */
 typedef bool (*gidx_predicate)(GIDX *a, GIDX *b);
 
 
-/*********************************************************************************
-** GIDX support functions.
-**
-** We put the GIDX support here rather than liblwgeom because it is a 
-** specialized type only used for indexing purposes. It also makes use 
-** of some PgSQL infrastructure like the VARSIZE() macros.
-*/
-
-/*
-** Inline some of the most common and simplest utility functions.
-*/
-
-/* Returns number of dimensions for this GIDX */
-#define GIDX_NDIMS(gidx) ((VARSIZE((gidx)) - VARHDRSZ) / (2 * sizeof(float)))
-/* Minimum accessor. */
-#define GIDX_GET_MIN(gidx, dimension) ((gidx)->c[2*(dimension)])
-/* Maximum accessor. */
-#define GIDX_GET_MAX(gidx, dimension) ((gidx)->c[2*(dimension)+1])
-/* Minimum setter. */
-#define GIDX_SET_MIN(gidx, dimension, value) ((gidx)->c[2*(dimension)] = (value))
-/* Maximum setter. */
-#define GIDX_SET_MAX(gidx, dimension, value) ((gidx)->c[2*(dimension)+1] = (value))
-/* Returns the size required to store a GIDX of requested dimension */
-#define GIDX_SIZE(dimensions) (sizeof(int32) + 2*(dimensions)*sizeof(float))
-
 
 /* Allocates a new GIDX on the heap of the requested dimensionality */
-GIDX* gidx_new(int ndims)
+static GIDX* gidx_new(int ndims)
 {
        size_t size = GIDX_SIZE(ndims);
        GIDX *g = (GIDX*)palloc(size);
@@ -166,6 +124,7 @@ static GIDX* gidx_copy(GIDX *b)
        return c;
 }
 
+
 /* Ensure all minimums are below maximums. */
 static inline void gidx_validate(GIDX *b)
 {
@@ -232,7 +191,7 @@ static float gidx_volume(GIDX *a)
        result = GIDX_GET_MAX(a,0) - GIDX_GET_MIN(a,0);
        for ( i = 1; i < GIDX_NDIMS(a); i++ )
                result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i));
-       POSTGIS_DEBUGF(5, "calculated volume of %s as %.12g", gidx_to_string(a), result);
+       POSTGIS_DEBUGF(5, "calculated volume of %s as %.8g", gidx_to_string(a), result);
        return result;
 }
 
@@ -247,6 +206,8 @@ static void gidx_dimensionality_check(GIDX **a, GIDX **b)
        }
 }
 
+
+
 /* Calculate the volume of the union of the boxes. Avoids creating an intermediate box. */
 static float gidx_union_volume(GIDX *a, GIDX *b)
 {
@@ -386,7 +347,6 @@ GIDX* gidx_from_gbox(GBOX box)
        return a;
 }
 
-
 void gbox_from_gidx(GIDX *a, GBOX *gbox)
 {
        gbox->xmin = (double)GIDX_GET_MIN(a,0);
@@ -397,120 +357,6 @@ void gbox_from_gidx(GIDX *a, GBOX *gbox)
        gbox->zmax = (double)GIDX_GET_MAX(a,2);
 }
 
-
-/***********************************************************************
-** GIDX tests for 2D index operators.
-*/
-
-static bool gidx_2d_overlaps(GIDX *a, GIDX *b)
-{
-       int i;
-       POSTGIS_DEBUG(5, "entered function");
-       if ( (a == NULL) || (b == NULL) ) return FALSE;
-
-       for ( i = 0; i < 2; i++ )
-       {
-               if ( GIDX_GET_MIN(a,i) > GIDX_GET_MAX(b,i) )
-                       return FALSE;
-               if ( GIDX_GET_MIN(b,i) > GIDX_GET_MAX(a,i) )
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-static bool gidx_2d_contains(GIDX *a, GIDX *b)
-{
-       int i;
-       POSTGIS_DEBUG(5, "entered function");
-       if ( (a == NULL) || (b == NULL) ) return FALSE;
-
-       /* a must be beyond b in all dimensions and directions */
-       for ( i = 0; i < 2; i++ )
-       {
-               if ( GIDX_GET_MIN(a,i) > GIDX_GET_MIN(b,i) )
-                       return FALSE;
-               if ( GIDX_GET_MAX(a,i) < GIDX_GET_MAX(b,i) )
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-static bool gidx_2d_within(GIDX *a, GIDX *b)
-{
-       POSTGIS_DEBUG(5, "entered function");
-       return gidx_2d_contains(b,a);
-}
-
-static bool gidx_2d_equals(GIDX *a, GIDX *b)
-{
-       int i;
-       POSTGIS_DEBUG(5, "entered function");
-       if ( (a == NULL) || (b == NULL) ) return FALSE;
-
-       /* a must be beyond b in all dimensions and directions */
-       for ( i = 0; i < 2; i++ )
-       {
-               if ( GIDX_GET_MIN(a,i) != GIDX_GET_MIN(b,i) )
-                       return FALSE;
-               if ( GIDX_GET_MAX(a,i) != GIDX_GET_MAX(b,i) )
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-static bool gidx_2d_overleft(GIDX *a, GIDX *b)
-{
-       /* a.xmax <= b.xmax */
-       return GIDX_GET_MAX(a,0) <= GIDX_GET_MAX(b,0);
-}
-
-static bool gidx_2d_left(GIDX *a, GIDX *b)
-{
-       /* a.xmax < b.xmin */
-       return GIDX_GET_MAX(a,0) < GIDX_GET_MIN(b,0);
-}
-
-static bool gidx_2d_right(GIDX *a, GIDX *b)
-{
-       /* a.xmin > b.xmax */
-       return GIDX_GET_MIN(a,0) > GIDX_GET_MAX(b,0);
-}
-
-static bool gidx_2d_overright(GIDX *a, GIDX *b)
-{
-       /* a.xmin >= b.xmin */
-       return GIDX_GET_MIN(a,0) >= GIDX_GET_MIN(b,0);
-}
-
-static bool gidx_2d_overbelow(GIDX *a, GIDX *b)
-{
-       /* a.ymax <= b.ymax */
-       return GIDX_GET_MAX(a,1) <= GIDX_GET_MAX(b,1);
-}
-
-static bool gidx_2d_below(GIDX *a, GIDX *b)
-{
-       /* a.ymax < b.ymin */
-       return GIDX_GET_MAX(a,1) < GIDX_GET_MIN(b,1);
-}
-
-static bool gidx_2d_above(GIDX *a, GIDX *b)
-{
-       /* a.ymin > b.ymax */
-       return GIDX_GET_MIN(a,1) > GIDX_GET_MAX(b,1);
-}
-
-static bool gidx_2d_overabove(GIDX *a, GIDX *b)
-{
-       /* a.ymin >= b.ymin */
-       return GIDX_GET_MIN(a,1) >= GIDX_GET_MIN(b,1);
-}
-
-
-
 /*
 ** Overlapping GIDX box test.
 **
@@ -551,7 +397,7 @@ static bool gidx_overlaps(GIDX *a, GIDX *b)
 /*
 ** GIDX expansion, make d units bigger in all dimensions.
 */
-static void gidx_expand(GIDX *a, float d)
+void gidx_expand(GIDX *a, float d)
 {
        int i;
 
@@ -645,6 +491,82 @@ static bool gidx_equals(GIDX *a, GIDX *b)
        return TRUE;
 }
 
+/**
+* Support function. Based on two datums return true if
+* they satisfy the predicate and false otherwise.
+*/
+static int 
+gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate)
+{
+       /* Put aside some stack memory and use it for GIDX pointers. */
+       char boxmem1[GIDX_MAX_SIZE];
+       char boxmem2[GIDX_MAX_SIZE];
+       GIDX *gidx1 = (GIDX*)boxmem1;
+       GIDX *gidx2 = (GIDX*)boxmem2;
+
+       POSTGIS_DEBUG(3, "entered function");
+
+       /* Must be able to build box for each arguement (ie, not empty geometry)
+          and overlap boxes to return true. */
+       if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
+            (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
+             predicate(gidx1, gidx2) )
+       {
+               POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(gidx1), gidx_to_string(gidx2));
+               return LW_TRUE;
+       }
+       return LW_FALSE;
+}
+
+/**
+* Support function. Based on two datums return true if
+* they overlap and false otherwise. Useful for fast exiting
+* in functions doing geocalculation.
+*/
+int 
+gserialized_datum_overlaps(Datum gs1, Datum gs2)
+{
+       /* Put aside some stack memory and use it for GIDX pointers. */
+       char boxmem1[GIDX_MAX_SIZE];
+       char boxmem2[GIDX_MAX_SIZE];
+       GIDX *gidx1 = (GIDX*)boxmem1;
+       GIDX *gidx2 = (GIDX*)boxmem2;
+
+       /* Must be able to build box for each arguement (ie, not empty geometry)
+          and overlap boxes to return true. */
+       if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
+            (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
+             gidx_overlaps(gidx1, gidx2) )
+       {
+               return LW_TRUE;
+       }
+
+       return LW_FALSE;
+}
+
+
+
+/**
+* Return a #GSERIALIZED with an expanded bounding box.
+*/
+GSERIALIZED* 
+gserialized_expand(GSERIALIZED *g, double distance)
+{
+       char boxmem[GIDX_MAX_SIZE];
+       GIDX *gidx = (GIDX*)boxmem;
+       float fdistance = (float)distance;
+
+       /* Get our bounding box out of the geography, return right away if
+          input is an EMPTY geometry. */
+       if ( gserialized_get_gidx_p(g, gidx) == LW_FAILURE )
+       {
+               return g;
+       }
+       
+       gidx_expand(gidx, fdistance);
+
+       return gserialized_set_gidx(g, gidx);
+}
 
 /**
 * Peak into a #GSERIALIZED datum to find the bounding box. If the
@@ -705,26 +627,6 @@ gserialized_datum_get_gidx_p(Datum gsdatum, GIDX *gidx)
        return result;
 }
 
-/**
-* Given a #GSERIALIZED datum, as quickly as possible (peaking into the top
-* of the memory) return the gbox extents. Does not deserialize the geometry,
-* but <em>WARNING</em> returns a slightly larger bounding box than actually
-* encompasses the objects. For geography objects returns geocentric bounding
-* box, for geometry objects returns cartesian bounding box.
-*/
-int 
-gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
-{
-       char gboxmem[GIDX_MAX_SIZE];
-       GIDX *gidx = (GIDX*)gboxmem;
-       
-       if( LW_FAILURE == gserialized_datum_get_gidx_p(gsdatum, gidx) )
-               return LW_FAILURE;
-               
-       gbox_from_gidx(gidx, gbox);
-       return LW_SUCCESS;
-}
-
 
 /*
 ** Peak into a geography to find the bounding box. If the
@@ -770,282 +672,6 @@ int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx)
        return result;
 }
 
-/**
-* Update the bounding box of a #GSERIALIZED, allocating a fresh one
-* if there is not enough space to just write the new box in. 
-* <em>WARNING</em> if a new object needs to be created, the 
-* input pointer will have to be freed by the caller! Check
-* to see if input == output. Returns null if there's a problem
-* like mismatched dimensions.
-*/
-GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx)
-{
-       int g_ndims = (FLAGS_GET_GEODETIC(g->flags) ? 3 : FLAGS_NDIMS(g->flags));
-       int box_ndims = GIDX_NDIMS(gidx);
-       GSERIALIZED *g_out = NULL;
-       size_t box_size = 2 * g_ndims * sizeof(float);
-
-       /* The dimensionality of the inputs has to match or we are SOL. */
-       if ( g_ndims != box_ndims )
-       {
-               return NULL;
-       }
-
-       /* Serialized already has room for a box. */
-       if ( FLAGS_GET_BBOX(g->flags) )
-       {
-               g_out = g;
-       }
-       /* Serialized has no box. We need to allocate enough space for the old
-          data plus the box, and leave a gap in the memory segment to write
-          the new values into.
-       */
-       else
-       {
-               size_t varsize_new = VARSIZE(g) + box_size;
-               uchar *ptr;
-               g_out = palloc(varsize_new);
-               /* Copy the head of g into place */
-               memcpy(g_out, g, 8);
-               /* Copy the body of g into place after leaving space for the box */
-               ptr = g_out->data;
-               ptr += box_size;
-               memcpy(ptr, g->data, VARSIZE(g) - 8);
-               FLAGS_SET_BBOX(g_out->flags, 1);
-               SET_VARSIZE(g_out, varsize_new);
-       }
-
-       /* Now write the gidx values into the memory segement */
-       memcpy(g_out->data, gidx->c, box_size);
-
-       return g_out;
-}
-
-/**
-* Remove the bounding box from a #GSERIALIZED. Returns a freshly
-* allocated #GSERIALIZED every time.
-*/
-GSERIALIZED* gserialized_drop_gidx(GSERIALIZED *g)
-{
-       int g_ndims = (FLAGS_GET_GEODETIC(g->flags) ? 3 : FLAGS_NDIMS(g->flags));
-       size_t box_size = 2 * g_ndims * sizeof(float);
-       size_t g_out_size = VARSIZE(g) - box_size;
-       GSERIALIZED *g_out = palloc(g_out_size);
-
-       /* Copy the contents while omitting the box */
-       if ( FLAGS_GET_BBOX(g->flags) )
-       {
-               uchar *outptr = (uchar*)g_out;
-               uchar *inptr = (uchar*)g;
-               /* Copy the header (size+type) of g into place */
-               memcpy(outptr, inptr, 8);
-               outptr += 8;
-               inptr += 8 + box_size;
-               /* Copy parts after the box into place */
-               memcpy(outptr, inptr, g_out_size - 8);
-               FLAGS_SET_BBOX(g_out->flags, 0);
-               SET_VARSIZE(g_out, g_out_size);
-       }
-       /* No box? Nothing to do but copy and return. */
-       else
-       {
-               memcpy(g_out, g, g_out_size);
-       }
-
-       return g_out;
-}
-
-
-/**
-* Support function. Based on two datums return true if
-* they overlap and false otherwise. Useful for fast exiting
-* in functions doing geocalculation.
-*/
-int 
-gserialized_datum_overlaps(Datum gs1, Datum gs2)
-{
-       /* Put aside some stack memory and use it for GIDX pointers. */
-       char boxmem1[GIDX_MAX_SIZE];
-       char boxmem2[GIDX_MAX_SIZE];
-       GIDX *gidx1 = (GIDX*)boxmem1;
-       GIDX *gidx2 = (GIDX*)boxmem2;
-
-       /* Must be able to build box for each arguement (ie, not empty geometry)
-          and overlap boxes to return true. */
-       if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
-            (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
-             gidx_overlaps(gidx1, gidx2) )
-       {
-               return LW_TRUE;
-       }
-
-       return LW_FALSE;
-}
-
-/**
-* Support function. Based on two datums return true if
-* they satisfy the predicate and false otherwise.
-*/
-static int 
-gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate)
-{
-       /* Put aside some stack memory and use it for GIDX pointers. */
-       char boxmem1[GIDX_MAX_SIZE];
-       char boxmem2[GIDX_MAX_SIZE];
-       GIDX *gidx1 = (GIDX*)boxmem1;
-       GIDX *gidx2 = (GIDX*)boxmem2;
-
-       POSTGIS_DEBUG(3, "entered function");
-
-       /* Must be able to build box for each arguement (ie, not empty geometry)
-          and overlap boxes to return true. */
-       if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
-            (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
-             predicate(gidx1, gidx2) )
-       {
-               POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(gidx1), gidx_to_string(gidx2));
-               return LW_TRUE;
-       }
-       return LW_FALSE;
-}
-
-/**
-* Return a #GSERIALIZED with an expanded bounding box.
-*/
-GSERIALIZED* 
-gserialized_expand(GSERIALIZED *g, double distance)
-{
-       char boxmem[GIDX_MAX_SIZE];
-       GIDX *gidx = (GIDX*)boxmem;
-       float fdistance = (float)distance;
-
-       /* Get our bounding box out of the geography, return right away if
-          input is an EMPTY geometry. */
-       if ( gserialized_get_gidx_p(g, gidx) == LW_FAILURE )
-       {
-               return g;
-       }
-       
-       gidx_expand(gidx, fdistance);
-
-       return gserialized_set_gidx(g, gidx);
-}
-
-/***********************************************************************
-* GiST 2-D Index Operator Functions
-*/
-
-PG_FUNCTION_INFO_V1(gserialized_2d_same);
-Datum gserialized_2d_same(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_equals) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_within);
-Datum gserialized_2d_within(PG_FUNCTION_ARGS)
-{
-       POSTGIS_DEBUG(3, "entered function");
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_within) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_contains);
-Datum gserialized_2d_contains(PG_FUNCTION_ARGS)
-{
-       POSTGIS_DEBUG(3, "entered function");
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_contains) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_overlaps);
-Datum gserialized_2d_overlaps(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_overlaps) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_left);
-Datum gserialized_2d_left(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_left) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_right);
-Datum gserialized_2d_right(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_right) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_above);
-Datum gserialized_2d_above(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_above) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_below);
-Datum gserialized_2d_below(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_below) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_overleft);
-Datum gserialized_2d_overleft(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_overleft) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_overright);
-Datum gserialized_2d_overright(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_overright) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_overabove);
-Datum gserialized_2d_overabove(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_overabove) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-PG_FUNCTION_INFO_V1(gserialized_2d_overbelow);
-Datum gserialized_2d_overbelow(PG_FUNCTION_ARGS)
-{
-       if ( gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_2d_overbelow) == LW_TRUE )
-               PG_RETURN_BOOL(TRUE);
-
-       PG_RETURN_BOOL(FALSE);
-}
-
-
 /***********************************************************************
 * GiST N-D Index Operator Functions
 */
@@ -1178,7 +804,6 @@ Datum gserialized_gist_compress(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(entry_out);
 }
 
-
 /*
 ** GiST support function.
 ** Decompress an entry. Unused for geography, so we return.
@@ -1194,112 +819,64 @@ Datum gserialized_gist_decompress(PG_FUNCTION_ARGS)
 /*
 ** GiST support function. Called from gserialized_gist_consistent below.
 */
-static inline bool gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, StrategyNumber strategy, int dims)
+static inline bool gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, StrategyNumber strategy)
 {
-       bool            retval;
+       bool retval;
 
-       POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i], bounds[%.12g %.12g %.12g, %.12g %.12g %.12g]",
-                      strategy, geog_counter_leaf++,
-                      GIDX_GET_MIN(query, 0), GIDX_GET_MIN(query, 1), GIDX_GET_MIN(query, 2),
-                      GIDX_GET_MAX(query, 0), GIDX_GET_MAX(query, 1), GIDX_GET_MAX(query, 2) );
+       POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
+                      strategy, geog_counter_leaf++);
 
-       if ( dims == 2 )
-       {
-               switch (strategy)
-               {
-               case RTOverlapStrategyNumber:
-                       retval = (bool) gidx_2d_overlaps(key, query);
-                       break;
-               case RTSameStrategyNumber:
-                       retval = (bool) gidx_2d_equals(key, query);
-                       break;
-               case RTContainsStrategyNumber:
-               case RTOldContainsStrategyNumber:
-                       retval = (bool) gidx_2d_contains(key, query);
-                       break;
-               case RTContainedByStrategyNumber:
-               case RTOldContainedByStrategyNumber:
-                       retval = (bool) gidx_2d_contains(query, key);
-                       break;
-               default:
-                       retval = FALSE;
-               }
-       }
-       else
+       switch (strategy)
        {
-               switch (strategy)
-               {
-               case RTOverlapStrategyNumber:
-                       retval = (bool) gidx_overlaps(key, query);
-                       break;
-               case RTSameStrategyNumber:
-                       retval = (bool) gidx_equals(key, query);
-                       break;
-               case RTContainsStrategyNumber:
-               case RTOldContainsStrategyNumber:
-                       retval = (bool) gidx_contains(key, query);
-                       break;
-               case RTContainedByStrategyNumber:
-               case RTOldContainedByStrategyNumber:
-                       retval = (bool) gidx_contains(query, key);
-                       break;
-               default:
-                       retval = FALSE;
-               }
+       case RTOverlapStrategyNumber:
+               retval = (bool) gidx_overlaps(key, query);
+               break;
+       case RTSameStrategyNumber:
+               retval = (bool) gidx_equals(key, query);
+               break;
+       case RTContainsStrategyNumber:
+       case RTOldContainsStrategyNumber:
+               retval = (bool) gidx_contains(key, query);
+               break;
+       case RTContainedByStrategyNumber:
+       case RTOldContainedByStrategyNumber:
+               retval = (bool) gidx_contains(query, key);
+               break;
+       default:
+               retval = FALSE;
        }
+
        return (retval);
 }
 
 /*
 ** GiST support function. Called from gserialized_gist_consistent below.
 */
-static inline bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy, int dims)
+static inline bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy)
 {
        bool            retval;
 
        POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
                       strategy, geog_counter_internal++, gidx_to_string(query), gidx_to_string(key) );
 
-       if ( dims == 2 )
-       {
-               switch (strategy)
-               {
-               case RTOverlapStrategyNumber:
-                       retval = (bool) gidx_2d_overlaps(key, query);
-                       break;
-               case RTSameStrategyNumber:
-               case RTContainsStrategyNumber:
-               case RTOldContainsStrategyNumber:
-                       retval = (bool) gidx_2d_contains(key, query);
-                       break;
-               case RTContainedByStrategyNumber:
-               case RTOldContainedByStrategyNumber:
-                       retval = (bool) gidx_2d_overlaps(key, query);
-                       break;
-               default:
-                       retval = FALSE;
-               }
-       }
-       else
-       {
-               switch (strategy)
-               {
-               case RTOverlapStrategyNumber:
-                       retval = (bool) gidx_overlaps(key, query);
-                       break;
-               case RTSameStrategyNumber:
-               case RTContainsStrategyNumber:
-               case RTOldContainsStrategyNumber:
-                       retval = (bool) gidx_contains(key, query);
-                       break;
-               case RTContainedByStrategyNumber:
-               case RTOldContainedByStrategyNumber:
-                       retval = (bool) gidx_overlaps(key, query);
-                       break;
-               default:
-                       retval = FALSE;
-               }       
-       }
+       switch (strategy)
+       {
+       case RTOverlapStrategyNumber:
+               retval = (bool) gidx_overlaps(key, query);
+               break;
+       case RTSameStrategyNumber:
+       case RTContainsStrategyNumber:
+       case RTOldContainsStrategyNumber:
+               retval = (bool) gidx_contains(key, query);
+               break;
+       case RTContainedByStrategyNumber:
+       case RTOldContainedByStrategyNumber:
+               retval = (bool) gidx_overlaps(key, query);
+               break;
+       default:
+               retval = FALSE;
+       }       
+
        return (retval);
 }
 
@@ -1355,83 +932,19 @@ Datum gserialized_gist_consistent(PG_FUNCTION_ARGS)
        {
                result = gserialized_gist_consistent_leaf(
                             (GIDX*)DatumGetPointer(entry->key),
-                            query_gbox_index, strategy, 0);
+                            query_gbox_index, strategy);
        }
        else
        {
                result = gserialized_gist_consistent_internal(
                             (GIDX*)DatumGetPointer(entry->key),
-                            query_gbox_index, strategy, 0);
+                            query_gbox_index, strategy);
        }
 
        PG_RETURN_BOOL(result);
 }
 
 
-/*
-** GiST support function. Take in a query and an entry and see what the
-** relationship is, based on the query strategy.
-*/
-PG_FUNCTION_INFO_V1(gserialized_gist_consistent_2d);
-Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
-{
-       GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
-       StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
-       bool result;
-       char gidxmem[GIDX_MAX_SIZE];
-       GIDX *query_gbox_index = (GIDX*)gidxmem;
-
-#if POSTGIS_PGSQL_VERSION >= 84
-       /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
-          rather than being supplied as part of the operator class definition */
-       bool *recheck = (bool *) PG_GETARG_POINTER(4);
-
-       /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
-          out during index scans. For cases when the geometries are large, rechecking
-          can make things twice as slow. */
-       *recheck = false;
-#endif
-
-       POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
-
-       /* Quick sanity check on query argument. */
-       if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
-       {
-               POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
-               PG_RETURN_BOOL(FALSE); /* NULL query! This is screwy! */
-       }
-
-       /* Quick sanity check on entry key. */
-       if ( DatumGetPointer(entry->key) == NULL )
-       {
-               POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
-               PG_RETURN_BOOL(FALSE); /* NULL entry! */
-       }
-
-       /* Null box should never make this far. */
-       if ( gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_gbox_index) == LW_FAILURE )
-       {
-               POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
-               PG_RETURN_BOOL(FALSE);
-       }
-
-       /* Treat leaf node tests different from internal nodes */
-       if (GIST_LEAF(entry))
-       {
-               result = gserialized_gist_consistent_leaf(
-                            (GIDX*)DatumGetPointer(entry->key),
-                            query_gbox_index, strategy, 2);
-       }
-       else
-       {
-               result = gserialized_gist_consistent_internal(
-                            (GIDX*)DatumGetPointer(entry->key),
-                            query_gbox_index, strategy, 2);
-       }
-
-       PG_RETURN_BOOL(result);
-}
-
 /*
 ** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
 ** Calculate the change in volume of the old entry once the new entry is added.
@@ -1515,6 +1028,8 @@ Datum gserialized_gist_union(PG_FUNCTION_ARGS)
 
 }
 
+
+
 /*
 ** GiST support function. Test equality of keys.
 */
@@ -1533,6 +1048,7 @@ Datum gserialized_gist_same(PG_FUNCTION_ARGS)
 }
 
 
+
 /*
 ** Utility function to add entries to the axis partition lists in the
 ** picksplit function.
@@ -1944,7 +1460,7 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS)
 ** The GIDX key must be defined as a PostgreSQL type, even though it is only
 ** ever used internally. These no-op stubs are used to bind the type.
 */
-PG_FUNCTION_INFO_V1(gidx_in);
+PG_FUNCTION_INFO_V1(gidx_in); 
 Datum gidx_in(PG_FUNCTION_ARGS)
 {
        ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1952,10 +1468,10 @@ Datum gidx_in(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(NULL);
 }
 
-PG_FUNCTION_INFO_V1(gidx_out);
+PG_FUNCTION_INFO_V1(gidx_out); 
 Datum gidx_out(PG_FUNCTION_ARGS)
 {
        ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                       errmsg("function gidx_out not implemented")));
        PG_RETURN_POINTER(NULL);
-}
+}
\ No newline at end of file
index 099162115d1880905319046b70eb42769ba80409..e7efa4a6eadfecfbc49cd4fbc38d710e4e773b45 100644 (file)
@@ -509,9 +509,256 @@ CREATE OPERATOR CLASS btree_geometry_ops
        OPERATOR        5       > ,\r
        FUNCTION        1       geometry_cmp (geometry, geometry);\r
 \r
-#ifndef GSERIALIZED_ON\r
+\r
+\r
+#ifdef GSERIALIZED_ON\r
+-----------------------------------------------------------------------------\r
+-- GiST GEOMETRY-over-GSERIALIZED\r
+-----------------------------------------------------------------------------\r
+--\r
+-- Box2Df type is used by the GiST index bindings. \r
+-- In/out functions are stubs, as all access should be internal.\r
+---\r
+-- Availability: 1.5.0\r
+CREATE OR REPLACE FUNCTION box2df_in(cstring)\r
+       RETURNS box2df\r
+       AS 'MODULE_PATHNAME','box2df_in'\r
+       LANGUAGE 'C' IMMUTABLE STRICT; \r
+\r
+-- Availability: 1.5.0\r
+CREATE OR REPLACE FUNCTION box2df_out(box2df)\r
+       RETURNS cstring\r
+       AS 'MODULE_PATHNAME','box2df_out'\r
+       LANGUAGE 'C' IMMUTABLE STRICT; \r
+\r
+-- Availability: 1.5.0\r
+CREATE TYPE box2df (\r
+       internallength = 16,\r
+       input = box2df_in,\r
+       output = box2df_out,\r
+       storage = plain,\r
+       alignment = double\r
+);\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_consistent_2d(internal,geometry,int4) \r
+       RETURNS bool \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_consistent_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_compress_2d(internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME','gserialized_gist_compress_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_penalty_2d(internal,internal,internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_penalty_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_picksplit_2d(internal, internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_picksplit_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_union_2d(bytea, internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_union_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_same_2d(geometry, geometry, internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_same_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_gist_decompress_2d(internal) \r
+       RETURNS internal \r
+       AS 'MODULE_PATHNAME' ,'gserialized_gist_decompress_2d'\r
+       LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+--CREATE OR REPLACE FUNCTION geometry_gist_sel_2d (internal, oid, internal, int4)\r
+--     RETURNS float8\r
+--     AS 'MODULE_PATHNAME', 'gserialized_gist_sel_2d'\r
+--     LANGUAGE 'C';\r
+\r
+-- Availability: 2.0.0\r
+--CREATE OR REPLACE FUNCTION geometry_gist_joinsel_2d(internal, oid, internal, smallint)\r
+--     RETURNS float8\r
+--     AS 'MODULE_PATHNAME', 'gserialized_gist_joinsel_2d'\r
+--     LANGUAGE 'C';\r
+\r
+-----------------------------------------------------------------------------\r
+-- GEOMETRY Operators\r
+-----------------------------------------------------------------------------\r
+\r
+-- Availability: 2.0.0\r
+CREATE OR REPLACE FUNCTION geometry_overlaps(geometry, geometry) \r
+       RETURNS boolean \r
+       AS 'MODULE_PATHNAME' ,'gserialized_overlaps_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR && (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overlaps,\r
+       COMMUTATOR = '&&'\r
+       ,RESTRICT = contsel, JOIN = contjoinsel\r
+--     ,RESTRICT = geometry_gist_sel, JOIN = geometry_gist_joinsel     \r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_same(geometry, geometry) \r
+       RETURNS boolean \r
+       AS 'MODULE_PATHNAME' ,'gserialized_same_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR ~= (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_same,\r
+       RESTRICT = contsel, JOIN = contjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_contains(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_contains_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OR REPLACE FUNCTION geometry_within(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_within'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR @ (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_within,\r
+       COMMUTATOR = '~',\r
+       RESTRICT = contsel, JOIN = contjoinsel\r
+);\r
+\r
+CREATE OPERATOR ~ (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_contains,\r
+       COMMUTATOR = '@',\r
+       RESTRICT = contsel, JOIN = contjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_left(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_left_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR << (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_left,\r
+       COMMUTATOR = '>>',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_overleft(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_overleft_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR &< (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overleft,\r
+       COMMUTATOR = '&>',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_below(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_below_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR <<| (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_below,\r
+       COMMUTATOR = '|>>',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_overbelow(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_overbelow_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR &<| (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overbelow,\r
+       COMMUTATOR = '|&>',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_overright(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_overright_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR &> (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overright,\r
+       COMMUTATOR = '&<',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_right(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_right_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR >> (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_right,\r
+       COMMUTATOR = '<<',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_overabove(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_overabove_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR |&> (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overabove,\r
+       COMMUTATOR = '&<|',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+CREATE OR REPLACE FUNCTION geometry_above(geometry, geometry)\r
+       RETURNS bool\r
+       AS 'MODULE_PATHNAME', 'gserialized_above_2d'\r
+       LANGUAGE 'C' IMMUTABLE STRICT;\r
+\r
+CREATE OPERATOR |>> (\r
+       LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_above,\r
+       COMMUTATOR = '<<|',\r
+       RESTRICT = positionsel, JOIN = positionjoinsel\r
+);\r
+\r
+-- Availability: 2.0.0\r
+CREATE OPERATOR CLASS gist_geometry_ops\r
+       DEFAULT FOR TYPE geometry USING GIST AS\r
+       STORAGE box2df,\r
+       OPERATOR        1        <<  ,\r
+       OPERATOR        2        &<      ,\r
+       OPERATOR        3        &&  ,\r
+       OPERATOR        4        &>      ,\r
+       OPERATOR        5        >>      ,\r
+--     OPERATOR        6        ~=      ,\r
+       OPERATOR        7        ~       ,\r
+       OPERATOR        8        @       ,\r
+       OPERATOR        9        &<| ,\r
+       OPERATOR        10       <<| ,\r
+       OPERATOR        11       |>> ,\r
+       OPERATOR        12       |&> ,\r
+       FUNCTION        1        geometry_gist_consistent_2d (internal, geometry, int4),\r
+       FUNCTION        2        geometry_gist_union_2d (bytea, internal),\r
+       FUNCTION        3        geometry_gist_compress_2d (internal),\r
+       FUNCTION        4        geometry_gist_decompress_2d (internal),\r
+       FUNCTION        5        geometry_gist_penalty_2d (internal, internal, internal),\r
+       FUNCTION        6        geometry_gist_picksplit_2d (internal, internal),\r
+       FUNCTION        7        geometry_gist_same_2d (geometry, geometry, internal);\r
+       \r
+#else\r
+\r
 -------------------------------------------------------------------\r
--- GiST indexes\r
+-- Original geometry GiST indexes\r
 -------------------------------------------------------------------\r
 -- Deprecation in 1.5.0 -- is this deprecated? 2011-01-05 robe\r
 CREATE OR REPLACE FUNCTION geometry_same(geometry, geometry)\r
@@ -1104,7 +1351,6 @@ CREATE OR REPLACE FUNCTION ST_MakeBox3d(geometry, geometry)
        AS 'MODULE_PATHNAME', 'BOX3D_construct'\r
        LANGUAGE 'C' IMMUTABLE STRICT;\r
 \r
-\r
 -- Availability: 1.4.0\r
 CREATE OR REPLACE FUNCTION ST_MakeLine (geometry[])\r
        RETURNS geometry\r