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);
/**
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 \
#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 */
/* 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);
-
-
-
-----------------------------------------------------------------------------
-#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
#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);
--- /dev/null
+/**********************************************************************
+ * $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;
+}
+
+
+
+
+
--- /dev/null
+
+/**********************************************************************
+** 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);
+
+
+
--- /dev/null
+/**********************************************************************
+ * $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);
+}
/**********************************************************************
- * $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>
#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
#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);
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);
return c;
}
+
/* Ensure all minimums are below maximums. */
static inline void gidx_validate(GIDX *b)
{
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;
}
}
}
+
+
/* Calculate the volume of the union of the boxes. Avoids creating an intermediate box. */
static float gidx_union_volume(GIDX *a, GIDX *b)
{
return a;
}
-
void gbox_from_gidx(GIDX *a, GBOX *gbox)
{
gbox->xmin = (double)GIDX_GET_MIN(a,0);
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.
**
/*
** 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;
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
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
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
*/
PG_RETURN_POINTER(entry_out);
}
-
/*
** GiST support function.
** Decompress an entry. Unused for geography, so we return.
/*
** 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);
}
{
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.
}
+
+
/*
** GiST support function. Test equality of keys.
*/
}
+
/*
** Utility function to add entries to the axis partition lists in the
** picksplit function.
** 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),
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
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
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