From: Paul Ramsey Date: Thu, 17 Mar 2011 13:44:27 +0000 (+0000) Subject: Replace variable-length-key 2D index with fixed-length-key for GSERIALIZED case. X-Git-Tag: 2.0.0alpha1~1885 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=19f60054cb1ff919a444601bf04989461841041b;p=postgis Replace variable-length-key 2D index with fixed-length-key for GSERIALIZED case. git-svn-id: http://svn.osgeo.org/postgis/trunk@6917 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/liblwgeom.h b/liblwgeom/liblwgeom.h index 81da1db24..83a853e41 100644 --- a/liblwgeom/liblwgeom.h +++ b/liblwgeom/liblwgeom.h @@ -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); /** diff --git a/postgis/Makefile.in b/postgis/Makefile.in index caae9c461..31ab27d8b 100644 --- a/postgis/Makefile.in +++ b/postgis/Makefile.in @@ -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 \ diff --git a/postgis/geography.h b/postgis/geography.h index abf9bf970..6a3af7619 100644 --- a/postgis/geography.h +++ b/postgis/geography.h @@ -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); - - - diff --git a/postgis/geography.sql.in.c b/postgis/geography.sql.in.c index 64b18ff0b..ce25393bb 100644 --- a/postgis/geography.sql.in.c +++ b/postgis/geography.sql.in.c @@ -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 diff --git a/postgis/geography_btree.c b/postgis/geography_btree.c index 04b67d6c7..128458bd9 100644 --- a/postgis/geography_btree.c +++ b/postgis/geography_btree.c @@ -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 index 000000000..4aba4aa9d --- /dev/null +++ b/postgis/gserialized_gist.c @@ -0,0 +1,136 @@ +/********************************************************************** + * $Id$ + * + * PostGIS - Spatial Types for PostgreSQL + * Copyright 2009 Paul Ramsey + * + * 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 WARNING 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. +* WARNING 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 index 000000000..f86398495 --- /dev/null +++ b/postgis/gserialized_gist.h @@ -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 index 000000000..965c6edf5 --- /dev/null +++ b/postgis/gserialized_gist_2d.c @@ -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 + * + * 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(""); + + 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); +} diff --git a/postgis/geography_gist.c b/postgis/gserialized_gist_nd.c similarity index 72% rename from postgis/geography_gist.c rename to postgis/gserialized_gist_nd.c index 92fce9c53..91b24d5e9 100644 --- a/postgis/geography_gist.c +++ b/postgis/gserialized_gist_nd.c @@ -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 @@ -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 WARNING 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. -* WARNING 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 diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index 099162115..e7efa4a6e 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -509,9 +509,256 @@ CREATE OPERATOR CLASS btree_geometry_ops OPERATOR 5 > , FUNCTION 1 geometry_cmp (geometry, geometry); -#ifndef GSERIALIZED_ON + + +#ifdef GSERIALIZED_ON +----------------------------------------------------------------------------- +-- GiST GEOMETRY-over-GSERIALIZED +----------------------------------------------------------------------------- +-- +-- Box2Df type is used by the GiST index bindings. +-- In/out functions are stubs, as all access should be internal. +--- +-- Availability: 1.5.0 +CREATE OR REPLACE FUNCTION box2df_in(cstring) + RETURNS box2df + AS 'MODULE_PATHNAME','box2df_in' + LANGUAGE 'C' IMMUTABLE STRICT; + +-- Availability: 1.5.0 +CREATE OR REPLACE FUNCTION box2df_out(box2df) + RETURNS cstring + AS 'MODULE_PATHNAME','box2df_out' + LANGUAGE 'C' IMMUTABLE STRICT; + +-- Availability: 1.5.0 +CREATE TYPE box2df ( + internallength = 16, + input = box2df_in, + output = box2df_out, + storage = plain, + alignment = double +); + +-- 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_compress_2d(internal) + RETURNS internal + AS 'MODULE_PATHNAME','gserialized_gist_compress_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_gist_penalty_2d(internal,internal,internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_gist_penalty_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_gist_picksplit_2d(internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_gist_picksplit_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_gist_union_2d(bytea, internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_gist_union_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_gist_same_2d(geometry, geometry, internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_gist_same_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_gist_decompress_2d(internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_gist_decompress_2d' + LANGUAGE 'C'; + +-- Availability: 2.0.0 +--CREATE OR REPLACE FUNCTION geometry_gist_sel_2d (internal, oid, internal, int4) +-- RETURNS float8 +-- AS 'MODULE_PATHNAME', 'gserialized_gist_sel_2d' +-- LANGUAGE 'C'; + +-- Availability: 2.0.0 +--CREATE OR REPLACE FUNCTION geometry_gist_joinsel_2d(internal, oid, internal, smallint) +-- RETURNS float8 +-- AS 'MODULE_PATHNAME', 'gserialized_gist_joinsel_2d' +-- LANGUAGE 'C'; + +----------------------------------------------------------------------------- +-- GEOMETRY Operators +----------------------------------------------------------------------------- + +-- Availability: 2.0.0 +CREATE OR REPLACE FUNCTION geometry_overlaps(geometry, geometry) + RETURNS boolean + AS 'MODULE_PATHNAME' ,'gserialized_overlaps_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR && ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overlaps, + COMMUTATOR = '&&' + ,RESTRICT = contsel, JOIN = contjoinsel +-- ,RESTRICT = geometry_gist_sel, JOIN = geometry_gist_joinsel +); + +CREATE OR REPLACE FUNCTION geometry_same(geometry, geometry) + RETURNS boolean + AS 'MODULE_PATHNAME' ,'gserialized_same_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR ~= ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_same, + RESTRICT = contsel, JOIN = contjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_contains(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_contains_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION geometry_within(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_within' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR @ ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_within, + COMMUTATOR = '~', + RESTRICT = contsel, JOIN = contjoinsel +); + +CREATE OPERATOR ~ ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_contains, + COMMUTATOR = '@', + RESTRICT = contsel, JOIN = contjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_left(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_left_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR << ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_left, + COMMUTATOR = '>>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_overleft(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_overleft_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR &< ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overleft, + COMMUTATOR = '&>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_below(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_below_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR <<| ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_below, + COMMUTATOR = '|>>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_overbelow(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_overbelow_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR &<| ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overbelow, + COMMUTATOR = '|&>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_overright(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_overright_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR &> ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overright, + COMMUTATOR = '&<', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_right(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_right_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR >> ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_right, + COMMUTATOR = '<<', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_overabove(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_overabove_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR |&> ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overabove, + COMMUTATOR = '&<|', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OR REPLACE FUNCTION geometry_above(geometry, geometry) + RETURNS bool + AS 'MODULE_PATHNAME', 'gserialized_above_2d' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OPERATOR |>> ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_above, + COMMUTATOR = '<<|', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +-- Availability: 2.0.0 +CREATE OPERATOR CLASS gist_geometry_ops + DEFAULT FOR TYPE geometry USING GIST AS + STORAGE box2df, + 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_2d (bytea, internal), + FUNCTION 3 geometry_gist_compress_2d (internal), + FUNCTION 4 geometry_gist_decompress_2d (internal), + FUNCTION 5 geometry_gist_penalty_2d (internal, internal, internal), + FUNCTION 6 geometry_gist_picksplit_2d (internal, internal), + FUNCTION 7 geometry_gist_same_2d (geometry, geometry, internal); + +#else + ------------------------------------------------------------------- --- GiST indexes +-- Original geometry GiST indexes ------------------------------------------------------------------- -- Deprecation in 1.5.0 -- is this deprecated? 2011-01-05 robe CREATE OR REPLACE FUNCTION geometry_same(geometry, geometry) @@ -1104,7 +1351,6 @@ CREATE OR REPLACE FUNCTION ST_MakeBox3d(geometry, geometry) AS 'MODULE_PATHNAME', 'BOX3D_construct' LANGUAGE 'C' IMMUTABLE STRICT; - -- Availability: 1.4.0 CREATE OR REPLACE FUNCTION ST_MakeLine (geometry[]) RETURNS geometry