From: Darafei Praliaskouski Date: Sun, 11 Nov 2018 10:39:57 +0000 (+0000) Subject: GiST and SP-GiST n-D indexes (based on GIDX) with overlaps, contains, within, and... X-Git-Tag: 3.0.0alpha1~293 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2f3f798cbe1f944f1988b1c5162b5efcd930f85f;p=postgis GiST and SP-GiST n-D indexes (based on GIDX) with overlaps, contains, within, and equals operators Patch by Esteban Zimányi and Arthur Lesuisse from Université Libre de Bruxelles (ULB) Closes https://github.com/postgis/postgis/pull/327 Closes https://github.com/postgis/postgis/pull/293 Closes #4230 git-svn-id: http://svn.osgeo.org/postgis/trunk@17001 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/.travis.yml b/.travis.yml index 262c73c0e..0cb00034b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,7 @@ env: - tag=pg11-geos37-gdal23-proj52 - tag=pg10-geos36-gdal23-proj49 - tag=pg96-geos36-gdal22-proj49 - - tag=pg95-geos36-gdal22-proj49 - - tag=pg94-geos35-gdal111-proj48 + - tag=pg95-geos35-gdal111-proj48 matrix: allow_failures: diff --git a/NEWS b/NEWS index 4d70a3ed3..79b676b0d 100644 --- a/NEWS +++ b/NEWS @@ -2,33 +2,39 @@ PostGIS 3.0.0 2019/xx/xx * Breaking Changes * - #3888, Raster support now available as a separate extension - (Sandro Santilli) + (Sandro Santilli) - #3807, Extension library files no longer include the minor version. - Use New configure switch --with-library-minor-version - if you need the old behavior (Regina Obe) + Use New configure switch --with-library-minor-version + if you need the old behavior (Regina Obe) + - #4230, ND box operators (overlaps, contains, within, equals) now don't look on + dimentions that aren't present in both operands. + Please REINDEX your ND indexes after upgrade. * New Features * - #2902, postgis_geos_noop (Sandro Santilli) - #4128, ST_AsMVT support for Feature ID (Stepan Kuzmin) + - #4230, SP-GiST and GiST support for ND box operators overlaps, contains, + within, equals (Esteban Zimányi and Arthur Lesuisse from Université + Libre de Bruxelles (ULB), Darafei Praliaskouski) * Enhancements and fixes * - #4153, ST_Segmentize now splits segments proportionally (Darafei - Praliaskouski). + Praliaskouski). - #4162, ST_DWithin documentation examples for storing geometry and - radius in table (Darafei Praliaskouski, github user Boscop). + radius in table (Darafei Praliaskouski, github user Boscop). - #4161, MVT: Drop geometries smaller than the resolution (Raúl Marín) - #4176, ST_Intersects supports GEOMETRYCOLLECTION (Darafei Praliaskouski) - #4181, St_AsMVTGeom: Avoid type changes due to validation (Raúl Marín) - #4183, St_AsMVTGeom: Drop invalid geometries after simplification (Raúl Marín) - - #4188, Avoid division by zero in kmeans (Raúl Marín) + - #4188, Avoid division by zero in KMeans (Raúl Marín) - #4189, Fix undefined behaviour in SADFWrite (Raúl Marín) - #4191, Fix undefined behaviour in ptarray_clone_deep (Raúl Marín) - #4211, Fix ST_Subdivide for minimal exterior ring with minimal hole (Darafei - Praliaskouski) + Praliaskouski) - #3457, Fix raster envelope shortcut in ST_Clip (Sai-bot) - #4215, Use floating point compare in ST_DumpAsPolygons (Darafei Praliaskouski) - #4155, Support for GEOMETRYCOLLECTION, POLYGON, TIN, TRIANGLE in - ST_LocateBetween and ST_LocateBetweenElevations (Darafei Praliaskouski) + ST_LocateBetween and ST_LocateBetweenElevations (Darafei Praliaskouski) - #2767, Documentation for AddRasterConstraint optional parameters (Sunveer Singh) PostGIS 2.5.0 @@ -39,9 +45,9 @@ PostgreSQL 9.4 - PostgreSQL 12 (in development) GEOS >= 3.5 * New Features * - - #1847, spgist 2d and 3d support for PG 11+ - (Esteban Zimányi and Arthur Lesuisse from Université Libre de Bruxelles (ULB), - Darafei Praliaskouski) + - #1847, SP-GiST 2D and 3D support for PostgreSQL 11+ (Esteban Zimányi and + Arthur Lesuisse from Université Libre de Bruxelles (ULB), Darafei + Praliaskouski) - #4056, ST_FilterByM (Nicklas Avén) - #4050, ST_ChaikinSmoothing (Nicklas Avén) - #3989, ST_Buffer single sided option (Stephen Knox) @@ -62,7 +68,7 @@ GEOS >= 3.5 * Breaking Changes * - #4054, ST_SimplifyVW changed from > tolerance to >= tolerance - #3885, version number removed from address_standardize lib file - (Regina Obe) + (Regina Obe) - #3893, raster support functions can only be loaded in the same schema with core PostGIS functions. (Sandro Santilli) - #4035, remove dummy pgis_abs type from aggregate/collect routines. diff --git a/configure.ac b/configure.ac index 2ef7eaeb1..cdfa41800 100644 --- a/configure.ac +++ b/configure.ac @@ -483,9 +483,9 @@ if test "x$LIBLWGEOM_ONLY" = "xno"; then PGSQL_SHAREDIR=`"$PG_CONFIG" --sharedir` AC_MSG_RESULT([checking PostgreSQL version... $PGSQL_FULL_VERSION]) - dnl Ensure that we are using PostgreSQL >= 9.4 - if test $POSTGIS_PGSQL_VERSION -lt 94; then - AC_MSG_ERROR([PostGIS requires PostgreSQL >= 9.4]) + dnl Ensure that we are using PostgreSQL >= 9.5 + if test $POSTGIS_PGSQL_VERSION -lt 95; then + AC_MSG_ERROR([PostGIS requires PostgreSQL >= 9.5]) fi HAVE_BRIN=no diff --git a/libpgcommon/gserialized_gist.h b/libpgcommon/gserialized_gist.h index cfbced067..0efba3c53 100644 --- a/libpgcommon/gserialized_gist.h +++ b/libpgcommon/gserialized_gist.h @@ -26,15 +26,15 @@ typedef struct #define GIDX_MAX_SIZE 36 #define GIDX_MAX_DIM 4 - /* * This macro is based on PG_FREE_IF_COPY, except that it accepts two pointers. * See PG_FREE_IF_COPY comment in src/include/fmgr.h in postgres source code * for more details. */ #define POSTGIS_FREE_IF_COPY_P(ptrsrc, ptrori) \ - do { \ - if ((Pointer) (ptrsrc) != (Pointer) (ptrori)) \ + do \ + { \ + if ((Pointer)(ptrsrc) != (Pointer)(ptrori)) \ pfree(ptrsrc); \ } while (0) @@ -51,7 +51,6 @@ typedef struct float xmin, xmax, ymin, ymax; } BOX2DF; - /********************************************************************************* ** GIDX support functions. ** @@ -61,7 +60,7 @@ typedef struct */ /* allocate a new gidx object on the heap */ -GIDX* gidx_new(int ndims) ; +GIDX *gidx_new(int ndims); /* Increase the size of a GIDX */ void gidx_expand(GIDX *a, float d); @@ -70,7 +69,7 @@ void gidx_expand(GIDX *a, float d); bool gidx_is_unknown(const GIDX *a); /* Generate human readable form for GIDX. */ -char* gidx_to_string(GIDX *a) ; +char *gidx_to_string(GIDX *a); /* typedef to correct array-bounds checking for casts to GIDX - do not use this ANYWHERE except in the casts below */ @@ -79,24 +78,24 @@ typedef float _gidx_float_array[sizeof(float) * 2 * 4]; /* 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_float_array *)(&(gidx)->c)))[2*(dimension)] +#define GIDX_GET_MIN(gidx, dimension) (*((_gidx_float_array *)(&(gidx)->c)))[2 * (dimension)] /* Maximum accessor. */ -#define GIDX_GET_MAX(gidx, dimension) (*((_gidx_float_array *)(&(gidx)->c)))[2*(dimension)+1] +#define GIDX_GET_MAX(gidx, dimension) (*((_gidx_float_array *)(&(gidx)->c)))[2 * (dimension) + 1] /* Minimum setter. */ -#define GIDX_SET_MIN(gidx, dimension, value) ((gidx)->c[2*(dimension)] = (value)) +#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)) +#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)) +#define GIDX_SIZE(dimensions) (sizeof(int32) + 2 * (dimensions) * sizeof(float)) /* Allocate a copy of the box */ -BOX2DF* box2df_copy(BOX2DF *b); +BOX2DF *box2df_copy(BOX2DF *b); /* Grow the first argument to contain the second */ void box2df_merge(BOX2DF *b_union, BOX2DF *b_new); /* Allocate a copy of the box */ -GIDX* gidx_copy(GIDX *b); +GIDX *gidx_copy(GIDX *b); /* Grow the first argument to contain the second */ void gidx_merge(GIDX **b_union, GIDX *b_new); @@ -122,16 +121,14 @@ 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(const GSERIALIZED *g, GIDX *gidx); /* Copy a new bounding box into an existing gserialized */ -GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx); +GSERIALIZED *gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx); /* 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); +GSERIALIZED *gserialized_drop_gidx(GSERIALIZED *g); bool box2df_contains(const BOX2DF *a, const BOX2DF *b); - - void box2df_set_empty(BOX2DF *a); void box2df_set_finite(BOX2DF *a); void box2df_validate(BOX2DF *b); @@ -146,7 +143,9 @@ bool box2df_below(const BOX2DF *a, const BOX2DF *b); bool box2df_above(const BOX2DF *a, const BOX2DF *b); bool box2df_overabove(const BOX2DF *a, const BOX2DF *b); - +void gidx_validate(GIDX *b); +void gidx_set_unknown(GIDX *a); +bool gidx_overlaps(GIDX *a, GIDX *b); +bool gidx_equals(GIDX *a, GIDX *b); bool gidx_contains(GIDX *a, GIDX *b); int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df); - diff --git a/postgis/Makefile.in b/postgis/Makefile.in index 49736eec3..f32fd9db0 100644 --- a/postgis/Makefile.in +++ b/postgis/Makefile.in @@ -61,7 +61,7 @@ BRIN_OBJ= brin_2d.o brin_nd.o brin_common.o endif ifeq (@HAVE_SPGIST@,yes) -SPGIST_OBJ= gserialized_spgist_2d.o gserialized_spgist_3d.o +SPGIST_OBJ= gserialized_spgist_2d.o gserialized_spgist_3d.o gserialized_spgist_nd.o endif ifeq (@HAVE_PROTOBUF@,yes) diff --git a/postgis/gserialized_gist_nd.c b/postgis/gserialized_gist_nd.c index a33dc0d69..9916983de 100644 --- a/postgis/gserialized_gist_nd.c +++ b/postgis/gserialized_gist_nd.c @@ -23,7 +23,6 @@ * **********************************************************************/ - /* ** R-Tree Bibliography ** @@ -38,29 +37,26 @@ */ #include "postgres.h" -#include "access/gist.h" /* For GiST */ +#include "access/gist.h" /* For GiST */ #include "access/itup.h" #include "access/skey.h" #include "../postgis_config.h" -/*#define POSTGIS_DEBUG_LEVEL 4*/ - -#include "liblwgeom.h" /* For standard geometry types. */ -#include "lwgeom_pg.h" /* For debugging macros. */ -#include "gserialized_gist.h" /* For utility functions. */ +#include "liblwgeom.h" /* For standard geometry types. */ +#include "lwgeom_pg.h" /* For debugging macros. */ +#include "gserialized_gist.h" /* For utility functions. */ #include "geography.h" #include - /* Fall back to older finite() if necessary */ #ifndef HAVE_ISFINITE -# ifdef HAVE_GNU_ISFINITE -# define _GNU_SOURCE -# else -# define isfinite finite -# endif +#ifdef HAVE_GNU_ISFINITE +#define _GNU_SOURCE +#else +#define isfinite finite +#endif #endif /* @@ -100,52 +96,49 @@ Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS); ** ND Operator prototypes */ Datum gserialized_overlaps(PG_FUNCTION_ARGS); +Datum gserialized_gidx_geom_overlaps(PG_FUNCTION_ARGS); +Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS); Datum gserialized_contains(PG_FUNCTION_ARGS); -#if POSTGIS_PGSQL_VERSION > 94 Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS); Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS); -#endif Datum gserialized_within(PG_FUNCTION_ARGS); -#if POSTGIS_PGSQL_VERSION > 94 Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS); Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS); -#endif -Datum gserialized_distance_nd(PG_FUNCTION_ARGS); -#if POSTGIS_PGSQL_VERSION > 94 +Datum gserialized_same(PG_FUNCTION_ARGS); Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS); Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS); -#endif +Datum gserialized_distance_nd(PG_FUNCTION_ARGS); /* ** GIDX true/false test function type */ typedef bool (*gidx_predicate)(GIDX *a, GIDX *b); - /* Allocate a new copy of GIDX */ -GIDX* gidx_copy(GIDX *b) +GIDX * +gidx_copy(GIDX *b) { - GIDX *c = (GIDX*)palloc(VARSIZE(b)); + GIDX *c = (GIDX *)palloc(VARSIZE(b)); POSTGIS_DEBUGF(5, "copied gidx (%p) to gidx (%p)", b, c); - memcpy((void*)c, (void*)b, VARSIZE(b)); + memcpy((void *)c, (void *)b, VARSIZE(b)); return c; } - /* Ensure all minimums are below maximums. */ -static inline void gidx_validate(GIDX *b) +void +gidx_validate(GIDX *b) { uint32_t i; Assert(b); - POSTGIS_DEBUGF(5,"validating gidx (%s)", gidx_to_string(b)); - for ( i = 0; i < GIDX_NDIMS(b); i++ ) + POSTGIS_DEBUGF(5, "validating gidx (%s)", gidx_to_string(b)); + for (i = 0; i < GIDX_NDIMS(b); i++) { - if ( GIDX_GET_MIN(b,i) > GIDX_GET_MAX(b,i) ) + if (GIDX_GET_MIN(b, i) > GIDX_GET_MAX(b, i)) { float tmp; - tmp = GIDX_GET_MIN(b,i); - GIDX_SET_MIN(b,i,GIDX_GET_MAX(b,i)); - GIDX_SET_MAX(b,i,tmp); + tmp = GIDX_GET_MIN(b, i); + GIDX_SET_MIN(b, i, GIDX_GET_MAX(b, i)); + GIDX_SET_MAX(b, i, tmp); } } return; @@ -154,23 +147,26 @@ static inline void gidx_validate(GIDX *b) /* An "unknown" GIDX is used to represent the bounds of an EMPTY geometry or other-wise unindexable geometry (like one with NaN or Inf bounds) */ -inline bool gidx_is_unknown(const GIDX *a) +inline bool +gidx_is_unknown(const GIDX *a) { size_t size = VARSIZE(a) - VARHDRSZ; /* "unknown" gidx objects have a too-small size of one float */ - if ( size <= 0.0 ) + if (size <= 0.0) return true; return false; } -static inline void gidx_set_unknown(GIDX *a) +void +gidx_set_unknown(GIDX *a) { SET_VARSIZE(a, VARHDRSZ); } /* Enlarge b_union to contain b_new. If b_new contains more dimensions than b_union, expand b_union to contain those dimensions. */ -void gidx_merge(GIDX **b_union, GIDX *b_new) +void +gidx_merge(GIDX **b_union, GIDX *b_new) { int i, dims_union, dims_new; Assert(b_union); @@ -178,11 +174,11 @@ void gidx_merge(GIDX **b_union, GIDX *b_new) Assert(b_new); /* Can't merge an unknown into any thing */ - if( gidx_is_unknown(b_new) ) + if (gidx_is_unknown(b_new)) return; /* Merge of unknown and known is known */ - if( gidx_is_unknown(*b_union) ) + if (gidx_is_unknown(*b_union)) { *b_union = b_new; return; @@ -193,20 +189,20 @@ void gidx_merge(GIDX **b_union, GIDX *b_new) POSTGIS_DEBUGF(4, "merging gidx (%s) into gidx (%s)", gidx_to_string(b_new), gidx_to_string(*b_union)); - if ( dims_new > dims_union ) + if (dims_new > dims_union) { POSTGIS_DEBUGF(5, "reallocating b_union from %d dims to %d dims", dims_union, dims_new); - *b_union = (GIDX*)repalloc(*b_union, GIDX_SIZE(dims_new)); + *b_union = (GIDX *)repalloc(*b_union, GIDX_SIZE(dims_new)); SET_VARSIZE(*b_union, VARSIZE(b_new)); dims_union = dims_new; } - for ( i = 0; i < dims_new; i++ ) + for (i = 0; i < dims_new; i++) { /* Adjust minimums */ - GIDX_SET_MIN(*b_union, i, Min(GIDX_GET_MIN(*b_union,i),GIDX_GET_MIN(b_new,i))); + GIDX_SET_MIN(*b_union, i, Min(GIDX_GET_MIN(*b_union, i), GIDX_GET_MIN(b_new, i))); /* Adjust maximums */ - GIDX_SET_MAX(*b_union, i, Max(GIDX_GET_MAX(*b_union,i),GIDX_GET_MAX(b_new,i))); + GIDX_SET_MAX(*b_union, i, Max(GIDX_GET_MAX(*b_union, i), GIDX_GET_MAX(b_new, i))); } POSTGIS_DEBUGF(5, "merge complete (%s)", gidx_to_string(*b_union)); @@ -214,42 +210,40 @@ void gidx_merge(GIDX **b_union, GIDX *b_new) } /* Calculate the volume (in n-d units) of the GIDX */ -static float gidx_volume(GIDX *a) +static float +gidx_volume(GIDX *a) { float result; uint32_t i; - if ( a == NULL || gidx_is_unknown(a) ) - { - /* elog(ERROR, "gidx_volume received a null argument"); */ + if (!a || gidx_is_unknown(a)) return 0.0; - } - 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)); + 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 %.8g", gidx_to_string(a), result); return result; } /* Calculate the edge of the GIDX */ -static float gidx_edge(GIDX *a) +static float +gidx_edge(GIDX *a) { float result; uint32_t i; - if ( a == NULL || gidx_is_unknown(a) ) - { + if (!a || gidx_is_unknown(a)) return 0.0; - } - 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)); + 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 edge of %s as %.8g", gidx_to_string(a), result); return result; } /* Ensure the first argument has the higher dimensionality. */ -static void gidx_dimensionality_check(GIDX **a, GIDX **b) +static void +gidx_dimensionality_check(GIDX **a, GIDX **b) { - if ( GIDX_NDIMS(*a) < GIDX_NDIMS(*b) ) + if (GIDX_NDIMS(*a) < GIDX_NDIMS(*b)) { GIDX *tmp = *b; *b = *a; @@ -258,30 +252,29 @@ 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) +static float +gidx_union_volume(GIDX *a, GIDX *b) { float result; int i; int ndims_a, ndims_b; - POSTGIS_DEBUG(5,"entered function"); + POSTGIS_DEBUG(5, "entered function"); - if ( a == NULL && b == NULL ) + if (!a && !b) { elog(ERROR, "gidx_union_volume received two null arguments"); return 0.0; } - if ( a == NULL || gidx_is_unknown(a) ) + if (!a || gidx_is_unknown(a)) return gidx_volume(b); - if ( b == NULL || gidx_is_unknown(b) ) + if (!b || gidx_is_unknown(b)) return gidx_volume(a); - if ( gidx_is_unknown(a) && gidx_is_unknown(b) ) - { + if (gidx_is_unknown(a) && gidx_is_unknown(b)) return 0.0; - } /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); @@ -290,19 +283,15 @@ static float gidx_union_volume(GIDX *a, GIDX *b) ndims_b = GIDX_NDIMS(b); /* Initialize with maximal length of first dimension. */ - result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); + result = Max(GIDX_GET_MAX(a, 0), GIDX_GET_MAX(b, 0)) - Min(GIDX_GET_MIN(a, 0), GIDX_GET_MIN(b, 0)); /* Multiply by maximal length of remaining dimensions. */ - for ( i = 1; i < ndims_b; i++ ) - { - result *= (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i))); - } + for (i = 1; i < ndims_b; i++) + result *= (Max(GIDX_GET_MAX(a, i), GIDX_GET_MAX(b, i)) - Min(GIDX_GET_MIN(a, i), GIDX_GET_MIN(b, i))); /* Add in dimensions of higher dimensional box. */ - for ( i = ndims_b; i < ndims_a; i++ ) - { - result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); - } + for (i = ndims_b; i < ndims_a; i++) + result *= (GIDX_GET_MAX(a, i) - GIDX_GET_MIN(a, i)); POSTGIS_DEBUGF(5, "volume( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result); @@ -310,30 +299,29 @@ static float gidx_union_volume(GIDX *a, GIDX *b) } /* Calculate the edge of the union of the boxes. Avoids creating an intermediate box. */ -static float gidx_union_edge(GIDX *a, GIDX *b) +static float +gidx_union_edge(GIDX *a, GIDX *b) { float result; int i; int ndims_a, ndims_b; - POSTGIS_DEBUG(5,"entered function"); + POSTGIS_DEBUG(5, "entered function"); - if ( a == NULL && b == NULL ) + if (!a && !b) { elog(ERROR, "gidx_union_edge received two null arguments"); return 0.0; } - if ( a == NULL || gidx_is_unknown(a) ) + if (!a || gidx_is_unknown(a)) return gidx_volume(b); - if ( b == NULL || gidx_is_unknown(b) ) + if (!b || gidx_is_unknown(b)) return gidx_volume(a); - if ( gidx_is_unknown(a) && gidx_is_unknown(b) ) - { + if (gidx_is_unknown(a) && gidx_is_unknown(b)) return 0.0; - } /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); @@ -342,19 +330,15 @@ static float gidx_union_edge(GIDX *a, GIDX *b) ndims_b = GIDX_NDIMS(b); /* Initialize with maximal length of first dimension. */ - result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); + result = Max(GIDX_GET_MAX(a, 0), GIDX_GET_MAX(b, 0)) - Min(GIDX_GET_MIN(a, 0), GIDX_GET_MIN(b, 0)); /* Add maximal length of remaining dimensions. */ - for ( i = 1; i < ndims_b; i++ ) - { - result += (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i))); - } + for (i = 1; i < ndims_b; i++) + result += (Max(GIDX_GET_MAX(a, i), GIDX_GET_MAX(b, i)) - Min(GIDX_GET_MIN(a, i), GIDX_GET_MIN(b, i))); /* Add in dimensions of higher dimensional box. */ - for ( i = ndims_b; i < ndims_a; i++ ) - { - result += (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); - } + for (i = ndims_b; i < ndims_a; i++) + result += (GIDX_GET_MAX(a, i) - GIDX_GET_MIN(a, i)); POSTGIS_DEBUGF(5, "edge( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result); @@ -362,38 +346,39 @@ static float gidx_union_edge(GIDX *a, GIDX *b) } /* Calculate the volume of the intersection of the boxes. */ -static float gidx_inter_volume(GIDX *a, GIDX *b) +static float +gidx_inter_volume(GIDX *a, GIDX *b) { uint32_t i; float result; - POSTGIS_DEBUG(5,"entered function"); + POSTGIS_DEBUG(5, "entered function"); - if ( a == NULL || b == NULL ) + if (!a || !b) { elog(ERROR, "gidx_inter_volume received a null argument"); return 0.0; } - if ( gidx_is_unknown(a) || gidx_is_unknown(b) ) - { + if (gidx_is_unknown(a) || gidx_is_unknown(b)) return 0.0; - } /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); /* Initialize with minimal length of first dimension. */ - result = Min(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Max(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); + result = Min(GIDX_GET_MAX(a, 0), GIDX_GET_MAX(b, 0)) - Max(GIDX_GET_MIN(a, 0), GIDX_GET_MIN(b, 0)); /* If they are disjoint (max < min) then return zero. */ - if ( result < 0.0 ) return 0.0; + if (result < 0.0) + return 0.0; /* Continue for remaining dimensions. */ - for ( i = 1; i < GIDX_NDIMS(b); i++ ) + for (i = 1; i < GIDX_NDIMS(b); i++) { - float width = Min(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Max(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i)); - if ( width < 0.0 ) return 0.0; + float width = Min(GIDX_GET_MAX(a, i), GIDX_GET_MAX(b, i)) - Max(GIDX_GET_MIN(a, i), GIDX_GET_MIN(b, i)); + if (width < 0.0) + return 0.0; /* Multiply by minimal length of remaining dimensions. */ result *= width; } @@ -414,29 +399,34 @@ static float gidx_inter_volume(GIDX *a, GIDX *b) ** ** Empty boxes never overlap. */ -static bool gidx_overlaps(GIDX *a, GIDX *b) +bool +gidx_overlaps(GIDX *a, GIDX *b) { - int i; - int ndims_b; - POSTGIS_DEBUG(5, "entered function"); + int i, dims_a, dims_b; - if ( (a == NULL) || (b == NULL) ) return false; + POSTGIS_DEBUG(5, "entered function"); - if ( gidx_is_unknown(a) || gidx_is_unknown(b) ) + if (!a || !b) return false; - /* Ensure 'a' has the most dimensions. */ - gidx_dimensionality_check(&a, &b); + if (gidx_is_unknown(a) || gidx_is_unknown(b)) + return false; - ndims_b = GIDX_NDIMS(b); + dims_a = GIDX_NDIMS(a); + dims_b = GIDX_NDIMS(b); - /* compare only up to dimensions of (b), missing dimensions always overlap */ - for ( i = 0; i < ndims_b; i++ ) + /* For all shared dimensions min(a) > max(b) and min(b) > max(a) + Unshared dimensions do not matter */ + for (i = 0; i < Min(dims_a, dims_b); 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; + /* If the missing dimension was not padded with -+FLT_MAX */ + if (GIDX_GET_MAX(a, i) != FLT_MAX && GIDX_GET_MAX(b, i) != FLT_MAX) + { + 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; @@ -447,44 +437,34 @@ static bool gidx_overlaps(GIDX *a, GIDX *b) ** ** Box(A) CONTAINS Box(B) IFF (pt(A)LL < pt(B)LL) && (pt(A)UR > pt(B)UR) */ -bool gidx_contains(GIDX *a, GIDX *b) +bool +gidx_contains(GIDX *a, GIDX *b) { - int i, dims_a, dims_b; + uint32_t i, dims_a, dims_b; - POSTGIS_DEBUG(5, "entered function"); - - if ( (a == NULL) || (b == NULL) ) return false; + if (!a || !b) + return false; - if ( gidx_is_unknown(a) || gidx_is_unknown(b) ) + if (gidx_is_unknown(a) || gidx_is_unknown(b)) return false; dims_a = GIDX_NDIMS(a); dims_b = GIDX_NDIMS(b); - if ( dims_a < dims_b ) + /* For all shared dimensions min(a) > min(b) and max(a) < max(b) + Unshared dimensions do not matter */ + for (i = 0; i < Min(dims_a, dims_b); i++) { - /* - ** If (b) is of higher dimensionality than (a) it can only be contained - ** if those higher dimensions are zeroes. - */ - for (i = dims_a; i < dims_b; i++) + /* If the missing dimension was not padded with -+FLT_MAX */ + if (GIDX_GET_MAX(a, i) != FLT_MAX && GIDX_GET_MAX(b, i) != FLT_MAX) { - if ( GIDX_GET_MIN(b,i) != 0 ) + if (GIDX_GET_MIN(a, i) > GIDX_GET_MIN(b, i)) return false; - if ( GIDX_GET_MAX(b,i) != 0 ) + if (GIDX_GET_MAX(a, i) < GIDX_GET_MAX(b, i)) return false; } } - /* Excess dimensions of (a), don't matter, it just has to contain (b) in (b)'s dimensions */ - for (i = 0; i < Min(dims_a, dims_b); 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; } @@ -493,63 +473,60 @@ bool gidx_contains(GIDX *a, GIDX *b) ** ** Box(A) EQUALS Box(B) IFF (pt(A)LL == pt(B)LL) && (pt(A)UR == pt(B)UR) */ -static bool gidx_equals(GIDX *a, GIDX *b) +bool +gidx_equals(GIDX *a, GIDX *b) { - uint32_t i; + uint32_t i, dims_a, dims_b; - POSTGIS_DEBUG(5, "entered function"); - - if ( (a == NULL) && (b == NULL) ) return true; - if ( (a == NULL) || (b == NULL) ) return false; + if (!a && !b) + return true; + if (!a || !b) + return false; - if ( gidx_is_unknown(a) && gidx_is_unknown(b) ) + if (gidx_is_unknown(a) && gidx_is_unknown(b)) return true; - if ( gidx_is_unknown(a) || gidx_is_unknown(b) ) + if (gidx_is_unknown(a) || gidx_is_unknown(b)) return false; - /* Ensure 'a' has the most dimensions. */ - gidx_dimensionality_check(&a, &b); + dims_a = GIDX_NDIMS(a); + dims_b = GIDX_NDIMS(b); - /* For all shared dimensions min(a) == min(b), max(a) == max(b) */ - for (i = 0; i < GIDX_NDIMS(b); 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; - } - /* For all unshared dimensions min(a) == 0.0, max(a) == 0.0 */ - for (i = GIDX_NDIMS(b); i < GIDX_NDIMS(a); i++) + /* For all shared dimensions min(a) == min(b), max(a) == max(b) + Unshared dimensions do not matter */ + for (i = 0; i < Min(dims_a, dims_b); i++) { - if ( GIDX_GET_MIN(a,i) != 0.0 ) - return false; - if ( GIDX_GET_MAX(a,i) != 0.0 ) - return false; + /* If the missing dimension was not padded with -+FLT_MAX */ + if (GIDX_GET_MAX(a, i) != FLT_MAX && GIDX_GET_MAX(b, i) != FLT_MAX) + { + 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; } /** -* Support function. Based on two datums return true if -* they satisfy the predicate and false otherwise. -*/ + * 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; + 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 predicate function 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) ) + 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; @@ -557,187 +534,98 @@ gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate) return LW_FALSE; } -#if POSTGIS_PGSQL_VERSION > 94 static int gserialized_datum_predicate_gidx_geom(GIDX *gidx1, Datum gs2, gidx_predicate predicate) { - /* Put aside some stack memory and use it for GIDX pointers. */ - char boxmem2[GIDX_MAX_SIZE]; - GIDX *gidx2 = (GIDX*)boxmem2; - - POSTGIS_DEBUG(3, "entered function"); - - /* Must be able to build box for gs2 arguement (ie, not empty geometry) - and predicate function to return true. */ - if ( (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; + /* Put aside some stack memory and use it for GIDX pointers. */ + char boxmem2[GIDX_MAX_SIZE]; + GIDX *gidx2 = (GIDX *)boxmem2; + + POSTGIS_DEBUG(3, "entered function"); + + /* Must be able to build box for gs2 arguement (ie, not empty geometry) + and predicate function to return true. */ + if ((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; } static int gserialized_datum_predicate_geom_gidx(Datum gs1, GIDX *gidx2, gidx_predicate predicate) { - /* Put aside some stack memory and use it for GIDX pointers. */ - char boxmem2[GIDX_MAX_SIZE]; - GIDX *gidx1 = (GIDX*)boxmem2; - - POSTGIS_DEBUG(3, "entered function"); - - /* Must be able to build box for gs2 arguement (ie, not empty geometry) - and predicate function to return true. */ - if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == 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; -} -#endif + /* Put aside some stack memory and use it for GIDX pointers. */ + char boxmem2[GIDX_MAX_SIZE]; + GIDX *gidx1 = (GIDX *)boxmem2; + POSTGIS_DEBUG(3, "entered function"); -/** -* Calculate the centroid->centroid distance between the boxes. -*/ -#if POSTGIS_PGSQL_VERSION < 95 -static double gidx_distance_leaf_centroid(const GIDX *a, const GIDX *b) -{ - int ndims, i; - double sum = 0; - - /* Base computation on least available dimensions */ - ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a)); - for ( i = 0; i < ndims; ++i ) - { - double ca, cb, d; - double amin = GIDX_GET_MIN(a,i); - double amax = GIDX_GET_MAX(a,i); - double bmin = GIDX_GET_MIN(b,i); - double bmax = GIDX_GET_MAX(b,i); - ca = amin + ( ( amax - amin ) / 2.0 ); - cb = bmin + ( ( bmax - bmin ) / 2.0 ); - d = ca - cb; - if ( ! isfinite(d) ) - { - /* Can happen if a dimension was padded with FLT_MAX, - * effectively meaning "infinite range". In that case - * we take that dimension as adding 0 to the total - * distance. - */ - continue; - } - sum += d * d; -/* - POSTGIS_DEBUGF(3, " centroid of A for dimension %d is %g", i, ca); - POSTGIS_DEBUGF(3, " centroid of B for dimension %d is %g", i, cb); - POSTGIS_DEBUGF(3, " distance on dimension %d is %g, squared as %g, grows sum to %g", i, d, d*d, sum); -*/ - } - return sqrt(sum); + /* Must be able to build box for gs2 arguement (ie, not empty geometry) + and predicate function to return true. */ + if ((gserialized_datum_get_gidx_p(gs1, gidx1) == 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; } -#endif /** -* Calculate the box->box distance. -*/ -static double gidx_distance(const GIDX *a, const GIDX *b, int m_is_time) -{ - int ndims, i; - double sum = 0; - - /* Base computation on least available dimensions */ - ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a)); - for ( i = 0; i < ndims; ++i ) - { - double d; - double amin = GIDX_GET_MIN(a,i); - double amax = GIDX_GET_MAX(a,i); - double bmin = GIDX_GET_MIN(b,i); - double bmax = GIDX_GET_MAX(b,i); - POSTGIS_DEBUGF(3, "A %g - %g", amin, amax); - POSTGIS_DEBUGF(3, "B %g - %g", bmin, bmax); - - if ( ( amin <= bmax && amax >= bmin ) ) - { - /* overlaps */ - d = 0; - } - else if ( i == 4 && m_is_time ) - { - return FLT_MAX; - } - else if ( bmax < amin ) - { - /* is "left" */ - d = amin - bmax; - } - else - { - /* is "right" */ - assert( bmin > amax ); - d = bmin - amax; - } - if ( ! isfinite(d) ) - { - /* Can happen if coordinates are corrupted/NaN */ - continue; - } - sum += d * d; - POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d*d, sum); - } - return sqrt(sum); -} - -#if POSTGIS_PGSQL_VERSION < 95 -static double gidx_distance_node_centroid(const GIDX *node, const GIDX *query) + * Calculate the box->box distance. + */ +static double +gidx_distance(const GIDX *a, const GIDX *b, int m_is_time) { - int i; + int ndims, i; double sum = 0; /* Base computation on least available dimensions */ - int ndims = Min(GIDX_NDIMS(node), GIDX_NDIMS(query)); - - for ( i = 0; i < ndims; ++i ) + ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a)); + for (i = 0; i < ndims; ++i) { double d; - double amin = GIDX_GET_MIN(query,i); - double amax = GIDX_GET_MAX(query,i); - double bmin = GIDX_GET_MIN(node,i); - double bmax = GIDX_GET_MAX(node,i); - double ca = amin + ( ( amax - amin ) / 2.0 ); - - if ( ( ca <= bmax && ca >= bmin ) ) + double amin = GIDX_GET_MIN(a, i); + double amax = GIDX_GET_MAX(a, i); + double bmin = GIDX_GET_MIN(b, i); + double bmax = GIDX_GET_MAX(b, i); + POSTGIS_DEBUGF(3, "A %g - %g", amin, amax); + POSTGIS_DEBUGF(3, "B %g - %g", bmin, bmax); + + if ((amin <= bmax && amax >= bmin)) { /* overlaps */ d = 0; } - else if ( bmax < ca ) + else if (i == 4 && m_is_time) + { + return FLT_MAX; + } + else if (bmax < amin) { /* is "left" */ - d = ca - bmax; + d = amin - bmax; } else { /* is "right" */ - assert( bmin > ca ); - d = bmin - ca; + assert(bmin > amax); + d = bmin - amax; } - if ( ! isfinite(d) ) + if (!isfinite(d)) { /* Can happen if coordinates are corrupted/NaN */ continue; } sum += d * d; - POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d*d, sum); + POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d * d, sum); } return sqrt(sum); } -#else /* POSTGIS_PGSQL_VERSION >= 95 */ -static double gidx_distance_m(const GIDX *a, const GIDX *b) + +static double +gidx_distance_m(const GIDX *a, const GIDX *b) { int mdim_a, mdim_b; double d, amin, amax, bmin, bmax; @@ -746,48 +634,45 @@ static double gidx_distance_m(const GIDX *a, const GIDX *b) mdim_a = GIDX_NDIMS(a) - 1; mdim_b = GIDX_NDIMS(b) - 1; - amin = GIDX_GET_MIN(a,mdim_a); - amax = GIDX_GET_MAX(a,mdim_a); - bmin = GIDX_GET_MIN(b,mdim_b); - bmax = GIDX_GET_MAX(b,mdim_b); - - if ( ( amin <= bmax && amax >= bmin ) ) - { - /* overlaps */ - d = 0; - } - else if ( bmax < amin ) - { - /* is "left" */ - d = amin - bmax; - } - else - { - /* is "right" */ - assert( bmin > amax ); - d = bmin - amax; - } + amin = GIDX_GET_MIN(a, mdim_a); + amax = GIDX_GET_MAX(a, mdim_a); + bmin = GIDX_GET_MIN(b, mdim_b); + bmax = GIDX_GET_MAX(b, mdim_b); + + if ((amin <= bmax && amax >= bmin)) + { + /* overlaps */ + d = 0; + } + else if (bmax < amin) + { + /* is "left" */ + d = amin - bmax; + } + else + { + /* is "right" */ + assert(bmin > amax); + d = bmin - amax; + } return d; } -#endif /* POSTGIS_PGSQL_VERSION >= 96 */ /** -* Return a #GSERIALIZED with an expanded bounding box. -*/ -GSERIALIZED* + * Return a #GSERIALIZED with an expanded bounding box. + */ +GSERIALIZED * gserialized_expand(GSERIALIZED *g, double distance) { char boxmem[GIDX_MAX_SIZE]; - GIDX *gidx = (GIDX*)boxmem; + 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 ) - { + if (gserialized_get_gidx_p(g, gidx) == LW_FAILURE) return g; - } gidx_expand(gidx, fdistance); @@ -795,39 +680,19 @@ gserialized_expand(GSERIALIZED *g, double distance) } /*********************************************************************** -* GiST N-D Index Operator Functions -*/ + * GiST N-D Index Operator Functions + */ /* -* Do centroid to centroid n-d distance if you don't have -* re-check available (PgSQL 9.5+), do "real" n-d distance -* if you do -*/ + * do "real" n-d distance + */ PG_FUNCTION_INFO_V1(gserialized_distance_nd); Datum gserialized_distance_nd(PG_FUNCTION_ARGS) { char b1mem[GIDX_MAX_SIZE]; - GIDX *b1 = (GIDX*)b1mem; + GIDX *b1 = (GIDX *)b1mem; char b2mem[GIDX_MAX_SIZE]; - GIDX *b2 = (GIDX*)b2mem; - -#if POSTGIS_PGSQL_VERSION < 95 - - /* Centroid-to-centroid distance */ - Datum gs1 = PG_GETARG_DATUM(0); - Datum gs2 = PG_GETARG_DATUM(1); - double box_distance = FLT_MAX; - - /* Must be able to build box for each argument (ie, not empty geometry). */ - if ( (gserialized_datum_get_gidx_p(gs1, b1) == LW_SUCCESS) && - (gserialized_datum_get_gidx_p(gs2, b2) == LW_SUCCESS) ) - { - box_distance = gidx_distance_leaf_centroid(b1, b2); - POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(b1), gidx_to_string(b2)); - } - PG_RETURN_FLOAT8(box_distance); - -#else /* POSTGIS_PGSQL_VERSION >= 96 */ + GIDX *b2 = (GIDX *)b2mem; /* Feature-to-feature distance */ GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0); @@ -837,9 +702,8 @@ Datum gserialized_distance_nd(PG_FUNCTION_ARGS) LWGEOM *closest; double distance; - /* Find an exact shortest line w/ the dimensions we support */ - if ( lwgeom_has_z(lw1) && lwgeom_has_z(lw2) ) + if (lwgeom_has_z(lw1) && lwgeom_has_z(lw2)) { closest = lwgeom_closest_line_3d(lw1, lw2); distance = lwgeom_length(closest); @@ -850,235 +714,236 @@ Datum gserialized_distance_nd(PG_FUNCTION_ARGS) distance = lwgeom_length_2d(closest); } - /* Un-sqrt the distance so we can add extra terms */ - distance = distance*distance; - /* Can only add the M term if both objects have M */ - if ( lwgeom_has_m(lw1) && lwgeom_has_m(lw2) ) + if (lwgeom_has_m(lw1) && lwgeom_has_m(lw2)) { double m1, m2; int usebox = false; + /* Un-sqrt the distance so we can add extra terms */ + distance = distance * distance; - if ( lwgeom_get_type(lw1) == POINTTYPE ) + if (lwgeom_get_type(lw1) == POINTTYPE) { POINT4D p; - lwpoint_getPoint4d_p((LWPOINT*)lw1, &p); + lwpoint_getPoint4d_p((LWPOINT *)lw1, &p); m1 = p.m; } - else if ( lwgeom_get_type(lw1) == LINETYPE ) + else if (lwgeom_get_type(lw1) == LINETYPE) { LWPOINT *lwp1 = lwline_get_lwpoint(lwgeom_as_lwline(closest), 0); m1 = lwgeom_interpolate_point(lw1, lwp1); lwpoint_free(lwp1); } else - { usebox = true; - } - if ( lwgeom_get_type(lw2) == POINTTYPE ) + if (lwgeom_get_type(lw2) == POINTTYPE) { POINT4D p; - lwpoint_getPoint4d_p((LWPOINT*)lw2, &p); + lwpoint_getPoint4d_p((LWPOINT *)lw2, &p); m2 = p.m; } - else if ( lwgeom_get_type(lw2) == LINETYPE ) + else if (lwgeom_get_type(lw2) == LINETYPE) { LWPOINT *lwp2 = lwline_get_lwpoint(lwgeom_as_lwline(closest), 1); m2 = lwgeom_interpolate_point(lw2, lwp2); lwpoint_free(lwp2); } else - { usebox = true; - } - if ( usebox ) + if (usebox) { double d; gserialized_get_gidx_p(geom1, b1); gserialized_get_gidx_p(geom2, b2); d = gidx_distance_m(b1, b2); - distance += d*d; + distance += d * d; } else - { - distance += (m2-m1)*(m2-m1); - } + distance += (m2 - m1) * (m2 - m1); + + distance = sqrt(distance); } lwgeom_free(closest); PG_FREE_IF_COPY(geom1, 0); PG_FREE_IF_COPY(geom2, 1); - PG_RETURN_FLOAT8(sqrt(distance)); -#endif /* POSTGIS_PGSQL_VERSION >= 96 */ + PG_RETURN_FLOAT8(distance); } /* -** '~' and operator function. Based on two serialized return true if +** '~~' and operator function. Based on two serialized return true if ** the first is contained by the second. */ PG_FUNCTION_INFO_V1(gserialized_within); Datum gserialized_within(PG_FUNCTION_ARGS) { - if ( gserialized_datum_predicate(PG_GETARG_DATUM(1), PG_GETARG_DATUM(0), gidx_contains) == LW_TRUE ) - { + if (gserialized_datum_predicate(PG_GETARG_DATUM(1), PG_GETARG_DATUM(0), gidx_contains)) PG_RETURN_BOOL(true); - } PG_RETURN_BOOL(false); } #if POSTGIS_PGSQL_VERSION > 94 /* -** '~' and operator function. Based on a GIDX and a serialized return true if +** '~~' and operator function. Based on a GIDX and a serialized return true if ** the first is contained by the second. */ PG_FUNCTION_INFO_V1(gserialized_gidx_geom_within); Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS) { - GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); + GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); - if ( gserialized_datum_predicate_geom_gidx(PG_GETARG_DATUM(1), gidx, gidx_contains) == LW_TRUE ) - PG_RETURN_BOOL(true); + if (gserialized_datum_predicate_geom_gidx(PG_GETARG_DATUM(1), gidx, gidx_contains)) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } /* -** '~' and operator function. Based on two GIDX return true if +** '~~' and operator function. Based on two GIDX return true if ** the first is contained by the second. */ PG_FUNCTION_INFO_V1(gserialized_gidx_gidx_within); Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS) { - if ( gidx_contains((GIDX *)PG_GETARG_POINTER(1), (GIDX *)PG_GETARG_POINTER(0))) - PG_RETURN_BOOL(true); + if (gidx_contains((GIDX *)PG_GETARG_POINTER(1), (GIDX *)PG_GETARG_POINTER(0))) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } #endif /* -** '@' and operator function. Based on two serialized return true if +** '@@' and operator function. Based on two serialized return true if ** the first contains the second. */ PG_FUNCTION_INFO_V1(gserialized_contains); Datum gserialized_contains(PG_FUNCTION_ARGS) { - if ( gserialized_datum_predicate(PG_GETARG_DATUM(0),PG_GETARG_DATUM(1), gidx_contains) == LW_TRUE ) - { + if (gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_contains)) PG_RETURN_BOOL(true); - } PG_RETURN_BOOL(false); } -#if POSTGIS_PGSQL_VERSION > 94 /* -** '@' and operator function. Based on a GIDX and a serialized return true if +** '@@' and operator function. Based on a GIDX and a serialized return true if ** the first contains the second. */ PG_FUNCTION_INFO_V1(gserialized_gidx_geom_contains); Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS) { - GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); + GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); - if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_contains) == LW_TRUE ) - PG_RETURN_BOOL(true); + if (gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_contains)) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } /* -** '@' and operator function. Based on two GIDX return true if +** '@@' and operator function. Based on two GIDX return true if ** the first contains the second. */ PG_FUNCTION_INFO_V1(gserialized_gidx_gidx_contains); Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS) { - if ( gidx_contains((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1))) - PG_RETURN_BOOL(true); + if (gidx_contains((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1))) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); +} + +/* +** '~=' and operator function. Based on two serialized return true if +** the first equals the second. +*/ +PG_FUNCTION_INFO_V1(gserialized_same); +Datum gserialized_same(PG_FUNCTION_ARGS) +{ + if (gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_equals)) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(false); } PG_FUNCTION_INFO_V1(gserialized_gidx_geom_same); Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS) { - GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); + GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); - if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_equals) == LW_TRUE ) - PG_RETURN_BOOL(true); + if (gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_equals)) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } +/* +** '~=' and operator function. Based on two GIDX return true if +** the first equals the second. +*/ PG_FUNCTION_INFO_V1(gserialized_gidx_gidx_same); Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS) { - if ( gidx_equals((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1)) ) - PG_RETURN_BOOL(true); + if (gidx_equals((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1))) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } -#endif /* -** '&&' operator function. Based on two serialized return true if +** '&&&' operator function. Based on two serialized return true if ** they overlap and false otherwise. */ PG_FUNCTION_INFO_V1(gserialized_overlaps); Datum gserialized_overlaps(PG_FUNCTION_ARGS) { - if ( gserialized_datum_predicate(PG_GETARG_DATUM(0),PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE ) - { + if (gserialized_datum_predicate(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), gidx_overlaps)) PG_RETURN_BOOL(true); - } PG_RETURN_BOOL(false); } -#if POSTGIS_PGSQL_VERSION > 94 /* * This is the cross-operator for the geographies */ PG_FUNCTION_INFO_V1(gserialized_gidx_geog_overlaps); Datum gserialized_gidx_geog_overlaps(PG_FUNCTION_ARGS) { - GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); + GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); - if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE ) - PG_RETURN_BOOL(true); + if (gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps)) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } PG_FUNCTION_INFO_V1(gserialized_gidx_geom_overlaps); Datum gserialized_gidx_geom_overlaps(PG_FUNCTION_ARGS) { - GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); + GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0); - if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE ) - PG_RETURN_BOOL(true); + if (gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps)) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } PG_FUNCTION_INFO_V1(gserialized_gidx_gidx_overlaps); Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS) { - if ( gidx_overlaps((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1)) ) - PG_RETURN_BOOL(true); + if (gidx_overlaps((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1))) + PG_RETURN_BOOL(true); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(false); } -#endif /*********************************************************************** -* GiST Index Support Functions -*/ + * GiST Index Support Functions + */ /* ** GiST support function. Given a geography, return a "compressed" @@ -1089,10 +954,10 @@ Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(gserialized_gist_compress); Datum gserialized_gist_compress(PG_FUNCTION_ARGS) { - GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0); + GISTENTRY *entry_in = (GISTENTRY *)PG_GETARG_POINTER(0); GISTENTRY *entry_out = NULL; char gidxmem[GIDX_MAX_SIZE]; - GIDX *bbox_out = (GIDX*)gidxmem; + GIDX *bbox_out = (GIDX *)gidxmem; int result = LW_SUCCESS; uint32_t i; @@ -1102,7 +967,7 @@ Datum gserialized_gist_compress(PG_FUNCTION_ARGS) ** Not a leaf key? There's nothing to do. ** Return the input unchanged. */ - if ( ! entry_in->leafkey ) + if (!entry_in->leafkey) { POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered"); PG_RETURN_POINTER(entry_in); @@ -1115,11 +980,10 @@ Datum gserialized_gist_compress(PG_FUNCTION_ARGS) ** Null key? Make a copy of the input entry and ** return. */ - if ( DatumGetPointer(entry_in->key) == NULL ) + if (!DatumGetPointer(entry_in->key)) { POSTGIS_DEBUG(4, "[GIST] leafkey is null"); - gistentryinit(*entry_out, (Datum) 0, entry_in->rel, - entry_in->page, entry_in->offset, false); + 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); } @@ -1129,40 +993,45 @@ Datum gserialized_gist_compress(PG_FUNCTION_ARGS) /* Is the bounding box valid (non-empty, non-infinite) ? * If not, use the "unknown" GIDX. */ - if ( result == LW_FAILURE ) + if (result == LW_FAILURE) { POSTGIS_DEBUG(4, "[GIST] empty geometry!"); gidx_set_unknown(bbox_out); - gistentryinit(*entry_out, PointerGetDatum(gidx_copy(bbox_out)), - entry_in->rel, entry_in->page, - entry_in->offset, false); + gistentryinit(*entry_out, + PointerGetDatum(gidx_copy(bbox_out)), + entry_in->rel, + entry_in->page, + entry_in->offset, + false); PG_RETURN_POINTER(entry_out); } POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", gidx_to_string(bbox_out)); + /* ORIGINAL VERSION */ /* Check all the dimensions for finite values. * If not, use the "unknown" GIDX as a key */ - for ( i = 0; i < GIDX_NDIMS(bbox_out); i++ ) + for (i = 0; i < GIDX_NDIMS(bbox_out); i++) { - if ( ! isfinite(GIDX_GET_MAX(bbox_out, i)) - || ! isfinite(GIDX_GET_MIN(bbox_out, i)) ) + if (!isfinite(GIDX_GET_MAX(bbox_out, i)) || !isfinite(GIDX_GET_MIN(bbox_out, i))) { gidx_set_unknown(bbox_out); gistentryinit(*entry_out, - PointerGetDatum(gidx_copy(bbox_out)), - entry_in->rel, entry_in->page, - entry_in->offset, false); + PointerGetDatum(gidx_copy(bbox_out)), + entry_in->rel, + entry_in->page, + entry_in->offset, + false); PG_RETURN_POINTER(entry_out); } } - /* Enure bounding box has minimums below maximums. */ + /* Ensure bounding box has minimums below maximums. */ gidx_validate(bbox_out); /* Prepare GISTENTRY for return. */ - gistentryinit(*entry_out, PointerGetDatum(gidx_copy(bbox_out)), - entry_in->rel, entry_in->page, entry_in->offset, false); + gistentryinit( + *entry_out, PointerGetDatum(gidx_copy(bbox_out)), entry_in->rel, entry_in->page, entry_in->offset, false); /* Return GISTENTRY. */ POSTGIS_DEBUG(4, "[GIST] 'compress' function complete"); @@ -1184,28 +1053,28 @@ 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) +static inline bool +gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, StrategyNumber strategy) { bool retval; - POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]", - strategy, geog_counter_leaf++); + POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]", strategy, geog_counter_leaf++); switch (strategy) { case RTOverlapStrategyNumber: - retval = (bool) gidx_overlaps(key, query); + retval = (bool)gidx_overlaps(key, query); break; case RTSameStrategyNumber: - retval = (bool) gidx_equals(key, query); + retval = (bool)gidx_equals(key, query); break; case RTContainsStrategyNumber: case RTOldContainsStrategyNumber: - retval = (bool) gidx_contains(key, query); + retval = (bool)gidx_contains(key, query); break; case RTContainedByStrategyNumber: case RTOldContainedByStrategyNumber: - retval = (bool) gidx_contains(query, key); + retval = (bool)gidx_contains(query, key); break; default: retval = false; @@ -1217,26 +1086,29 @@ static inline bool gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, Stra /* ** GiST support function. Called from gserialized_gist_consistent below. */ -static inline bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy) +static inline bool +gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy) { - bool retval; + 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) ); + 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)); switch (strategy) { case RTOverlapStrategyNumber: - retval = (bool) gidx_overlaps(key, query); + case RTContainedByStrategyNumber: + case RTOldContainedByStrategyNumber: + 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); + retval = (bool)gidx_contains(key, query); break; default: retval = false; @@ -1252,15 +1124,15 @@ static inline bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, PG_FUNCTION_INFO_V1(gserialized_gist_consistent); Datum gserialized_gist_consistent(PG_FUNCTION_ARGS) { - GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); - StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + 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; + GIDX *query_gbox_index = (GIDX *)gidxmem; /* 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); + 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 @@ -1270,21 +1142,21 @@ Datum gserialized_gist_consistent(PG_FUNCTION_ARGS) POSTGIS_DEBUG(4, "[GIST] 'consistent' function called"); /* Quick sanity check on query argument. */ - if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL ) + if (!DatumGetPointer(PG_GETARG_DATUM(1))) { 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 ) + if (!DatumGetPointer(entry->key)) { 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 ) + 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); @@ -1293,15 +1165,13 @@ Datum gserialized_gist_consistent(PG_FUNCTION_ARGS) /* 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); + result = + gserialized_gist_consistent_leaf((GIDX *)DatumGetPointer(entry->key), query_gbox_index, strategy); } else { result = gserialized_gist_consistent_internal( - (GIDX*)DatumGetPointer(entry->key), - query_gbox_index, strategy); + (GIDX *)DatumGetPointer(entry->key), query_gbox_index, strategy); } PG_RETURN_BOOL(result); @@ -1320,19 +1190,26 @@ Datum gserialized_gist_consistent(PG_FUNCTION_ARGS) ** greater than float b, integer A with same bit representation as a is greater ** than integer B with same bits as b. */ -static float pack_float(const float value, const int realm) +static float +pack_float(const float value, const int realm) { - union { - float f; - struct { unsigned value:31, sign:1; } vbits; - struct { unsigned value:29, realm:2, sign:1; } rbits; - } a; + union { + float f; + struct + { + unsigned value : 31, sign : 1; + } vbits; + struct + { + unsigned value : 29, realm : 2, sign : 1; + } rbits; + } a; - a.f = value; - a.rbits.value = a.vbits.value >> 2; - a.rbits.realm = realm; + a.f = value; + a.rbits.value = a.vbits.value >> 2; + a.rbits.realm = realm; - return a.f; + return a.f; } /* @@ -1342,19 +1219,19 @@ static float pack_float(const float value, const int realm) PG_FUNCTION_INFO_V1(gserialized_gist_penalty); Datum gserialized_gist_penalty(PG_FUNCTION_ARGS) { - GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0); - GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1); - float *result = (float*) PG_GETARG_POINTER(2); + GISTENTRY *origentry = (GISTENTRY *)PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *)PG_GETARG_POINTER(1); + float *result = (float *)PG_GETARG_POINTER(2); GIDX *gbox_index_orig, *gbox_index_new; float size_union, size_orig, edge_union, edge_orig; POSTGIS_DEBUG(4, "[GIST] 'penalty' function called"); - gbox_index_orig = (GIDX*)DatumGetPointer(origentry->key); - gbox_index_new = (GIDX*)DatumGetPointer(newentry->key); + gbox_index_orig = (GIDX *)DatumGetPointer(origentry->key); + gbox_index_new = (GIDX *)DatumGetPointer(newentry->key); /* Drop out if we're dealing with null inputs. Shouldn't happen. */ - if ( (gbox_index_orig == NULL) && (gbox_index_new == NULL) ) + if (!gbox_index_orig && !gbox_index_new) { POSTGIS_DEBUG(4, "[GIST] both inputs NULL! returning penalty of zero"); *result = 0.0; @@ -1367,37 +1244,30 @@ Datum gserialized_gist_penalty(PG_FUNCTION_ARGS) *result = size_union - size_orig; /* REALM 0: No extension is required, volume is zero, return edge */ - /* REALM 1: No extension is required, return nonzero area */ - /* REALM 2: Area extension is zero, return nonzero edge extension */ - /* REALM 3: Area extension is nonzero, return it */ + /* REALM 1: No extension is required, return nonzero area */ + /* REALM 2: Area extension is zero, return nonzero edge extension */ + /* REALM 3: Area extension is nonzero, return it */ - if( *result == 0 ) - { + if (*result == 0) + { if (size_orig > 0) - { *result = pack_float(size_orig, 1); /* REALM 1 */ - } else { edge_union = gidx_union_edge(gbox_index_orig, gbox_index_new); edge_orig = gidx_edge(gbox_index_orig); - *result = edge_union - edge_orig; - if( *result == 0 ) - { - *result = pack_float(edge_orig, 0); /* REALM 0 */ - } - else - { - *result = pack_float(*result, 2); /* REALM 2 */ - } + *result = edge_union - edge_orig; + if (*result == 0) + *result = pack_float(edge_orig, 0); /* REALM 0 */ + else + *result = pack_float(*result, 2); /* REALM 2 */ } - } - else - { - *result = pack_float(*result, 3); /* REALM 3 */ - } + } + else + *result = pack_float(*result, 3); /* REALM 3 */ - POSTGIS_DEBUGF(4, "[GIST] union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result); + POSTGIS_DEBUGF( + 4, "[GIST] union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result); PG_RETURN_POINTER(result); } @@ -1408,22 +1278,22 @@ Datum gserialized_gist_penalty(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(gserialized_gist_union); Datum gserialized_gist_union(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; + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); + int *sizep = (int *)PG_GETARG_POINTER(1); /* Size of the return value */ + int numranges, i; GIDX *box_cur, *box_union; POSTGIS_DEBUG(4, "[GIST] 'union' function called"); numranges = entryvec->n; - box_cur = (GIDX*) DatumGetPointer(entryvec->vector[0].key); + box_cur = (GIDX *)DatumGetPointer(entryvec->vector[0].key); box_union = gidx_copy(box_cur); - for ( i = 1; i < numranges; i++ ) + for (i = 1; i < numranges; i++) { - box_cur = (GIDX*) DatumGetPointer(entryvec->vector[i].key); + box_cur = (GIDX *)DatumGetPointer(entryvec->vector[i].key); gidx_merge(&box_union, box_cur); } @@ -1432,7 +1302,6 @@ Datum gserialized_gist_union(PG_FUNCTION_ARGS) POSTGIS_DEBUGF(4, "[GIST] union called, numranges(%i), pageunion %s", numranges, gidx_to_string(box_union)); PG_RETURN_POINTER(box_union); - } /* @@ -1441,9 +1310,9 @@ Datum gserialized_gist_union(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(gserialized_gist_same); Datum gserialized_gist_same(PG_FUNCTION_ARGS) { - GIDX *b1 = (GIDX*)PG_GETARG_POINTER(0); - GIDX *b2 = (GIDX*)PG_GETARG_POINTER(1); - bool *result = (bool*)PG_GETARG_POINTER(2); + GIDX *b1 = (GIDX *)PG_GETARG_POINTER(0); + GIDX *b2 = (GIDX *)PG_GETARG_POINTER(1); + bool *result = (bool *)PG_GETARG_POINTER(2); POSTGIS_DEBUG(4, "[GIST] 'same' function called"); @@ -1452,49 +1321,40 @@ Datum gserialized_gist_same(PG_FUNCTION_ARGS) PG_RETURN_POINTER(result); } - - - PG_FUNCTION_INFO_V1(gserialized_gist_geog_distance); Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS) { - GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); + GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); Datum query_datum = PG_GETARG_DATUM(1); - StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); -#if POSTGIS_PGSQL_VERSION >= 95 - bool *recheck = (bool *) PG_GETARG_POINTER(4); -#endif + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); + bool *recheck = (bool *)PG_GETARG_POINTER(4); char query_box_mem[GIDX_MAX_SIZE]; - GIDX *query_box = (GIDX*)query_box_mem; + GIDX *query_box = (GIDX *)query_box_mem; GIDX *entry_box; double distance; POSTGIS_DEBUGF(3, "[GIST] '%s' function called", __func__); /* We are using '13' as the gist geography distance <-> strategy number */ - if ( strategy != 13 ) + if (strategy != 13) { elog(ERROR, "unrecognized strategy number: %d", strategy); PG_RETURN_FLOAT8(FLT_MAX); } /* Null box should never make this far. */ - if ( gserialized_datum_get_gidx_p(query_datum, query_box) == LW_FAILURE ) + if (gserialized_datum_get_gidx_p(query_datum, query_box) == LW_FAILURE) { POSTGIS_DEBUG(2, "[GIST] null query_gbox_index!"); PG_RETURN_FLOAT8(FLT_MAX); } -#if POSTGIS_PGSQL_VERSION >= 95 /* When we hit leaf nodes, it's time to turn on recheck */ if (GIST_LEAF(entry)) - { *recheck = true; - } -#endif /* Get the entry box */ - entry_box = (GIDX*)DatumGetPointer(entry->key); + entry_box = (GIDX *)DatumGetPointer(entry->key); /* Return distances from key-based tests should always be */ /* the minimum possible distance, box-to-box */ @@ -1507,7 +1367,6 @@ Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(distance); } - /* ** GiST support function. ** Take in a query and an entry and return the "distance" between them. @@ -1527,14 +1386,12 @@ Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(gserialized_gist_distance); Datum gserialized_gist_distance(PG_FUNCTION_ARGS) { - GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); - StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); + StrategyNumber strategy = (StrategyNumber)PG_GETARG_UINT16(2); char query_box_mem[GIDX_MAX_SIZE]; - GIDX *query_box = (GIDX*)query_box_mem; + GIDX *query_box = (GIDX *)query_box_mem; GIDX *entry_box; -#if POSTGIS_PGSQL_VERSION >= 95 - bool *recheck = (bool *) PG_GETARG_POINTER(4); -#endif + bool *recheck = (bool *)PG_GETARG_POINTER(4); double distance; @@ -1542,22 +1399,21 @@ Datum gserialized_gist_distance(PG_FUNCTION_ARGS) /* Strategy 13 is <<->> */ /* Strategy 20 is |=| */ - if ( strategy != 13 && strategy != 20 ) { + if (strategy != 13 && strategy != 20) + { elog(ERROR, "unrecognized strategy number: %d", strategy); PG_RETURN_FLOAT8(FLT_MAX); } /* Null box should never make this far. */ - if ( gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_box) == LW_FAILURE ) + if (gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_box) == LW_FAILURE) { POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!"); PG_RETURN_FLOAT8(FLT_MAX); } /* Get the entry box */ - entry_box = (GIDX*)DatumGetPointer(entry->key); - -#if POSTGIS_PGSQL_VERSION >= 95 + entry_box = (GIDX *)DatumGetPointer(entry->key); /* Strategy 20 is |=| */ distance = gidx_distance(entry_box, query_box, strategy == 20); @@ -1565,40 +1421,21 @@ Datum gserialized_gist_distance(PG_FUNCTION_ARGS) /* Treat leaf node tests different from internal nodes */ if (GIST_LEAF(entry)) *recheck = true; -#else - if ( strategy == 20 ) - { - elog(ERROR, "You need PostgreSQL 9.5.0 or higher in order to use |=| with index"); - PG_RETURN_FLOAT8(FLT_MAX); - } - - /* Treat leaf node tests different from internal nodes */ - if (GIST_LEAF(entry)) - { - /* Calculate distance to leaves */ - distance = (double)gidx_distance_leaf_centroid(entry_box, query_box); - } - else - { - /* Calculate distance for internal nodes */ - distance = (double)gidx_distance_node_centroid(entry_box, query_box); - } -#endif PG_RETURN_FLOAT8(distance); } - /* ** Utility function to add entries to the axis partition lists in the ** picksplit function. */ -static void gserialized_gist_picksplit_addlist(OffsetNumber *list, GIDX **box_union, GIDX *box_current, int *pos, int num) +static void +gserialized_gist_picksplit_addlist(OffsetNumber *list, GIDX **box_union, GIDX *box_current, int *pos, int num) { - if ( *pos ) - gidx_merge(box_union, box_current); + if (*pos) + gidx_merge(box_union, box_current); else - memcpy((void*)(*box_union), (void*)box_current, VARSIZE(box_current)); + memcpy((void *)(*box_union), (void *)box_current, VARSIZE(box_current)); list[*pos] = num; (*pos)++; } @@ -1607,33 +1444,34 @@ static void gserialized_gist_picksplit_addlist(OffsetNumber *list, GIDX **box_un ** Utility function check whether the number of entries two halves of the ** space constitute a "bad ratio" (poor balance). */ -static int gserialized_gist_picksplit_badratio(int x, int y) +static int +gserialized_gist_picksplit_badratio(int x, int y) { POSTGIS_DEBUGF(4, "[GIST] checking split ratio (%d, %d)", x, y); - if ( (y == 0) || (((float)x / (float)y) < LIMIT_RATIO) || - (x == 0) || (((float)y / (float)x) < LIMIT_RATIO) ) + if ((y == 0) || (((float)x / (float)y) < LIMIT_RATIO) || (x == 0) || (((float)y / (float)x) < LIMIT_RATIO)) return true; return false; } -static bool gserialized_gist_picksplit_badratios(int *pos, int dims) +static bool +gserialized_gist_picksplit_badratios(int *pos, int dims) { int i; - for ( i = 0; i < dims; i++ ) + for (i = 0; i < dims; i++) { - if ( gserialized_gist_picksplit_badratio(pos[2*i],pos[2*i+1]) == false ) + if (gserialized_gist_picksplit_badratio(pos[2 * i], pos[2 * i + 1]) == false) return false; } return true; } - /* ** Where the picksplit algorithm cannot find any basis for splitting one way ** or another, we simply split the overflowing node in half. */ -static void gserialized_gist_picksplit_fallback(GistEntryVector *entryvec, GIST_SPLITVEC *v) +static void +gserialized_gist_picksplit_fallback(GistEntryVector *entryvec, GIST_SPLITVEC *v) { OffsetNumber i, maxoff; GIDX *unionL = NULL; @@ -1645,59 +1483,56 @@ static void gserialized_gist_picksplit_fallback(GistEntryVector *entryvec, GIST_ maxoff = entryvec->n - 1; nbytes = (maxoff + 2) * sizeof(OffsetNumber); - v->spl_left = (OffsetNumber*) palloc(nbytes); - v->spl_right = (OffsetNumber*) palloc(nbytes); + v->spl_left = (OffsetNumber *)palloc(nbytes); + v->spl_right = (OffsetNumber *)palloc(nbytes); v->spl_nleft = v->spl_nright = 0; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { - GIDX *cur = (GIDX*)DatumGetPointer(entryvec->vector[i].key); + GIDX *cur = (GIDX *)DatumGetPointer(entryvec->vector[i].key); if (i <= (maxoff - FirstOffsetNumber + 1) / 2) { v->spl_left[v->spl_nleft] = i; - if (unionL == NULL) - { + if (!unionL) unionL = gidx_copy(cur); - } else - { gidx_merge(&unionL, cur); - } v->spl_nleft++; } else { v->spl_right[v->spl_nright] = i; - if (unionR == NULL) - { + if (!unionR) unionR = gidx_copy(cur); - } else - { gidx_merge(&unionR, cur); - } v->spl_nright++; } } if (v->spl_ldatum_exists) - gidx_merge(&unionL, (GIDX*)DatumGetPointer(v->spl_ldatum)); + gidx_merge(&unionL, (GIDX *)DatumGetPointer(v->spl_ldatum)); v->spl_ldatum = PointerGetDatum(unionL); if (v->spl_rdatum_exists) - gidx_merge(&unionR, (GIDX*)DatumGetPointer(v->spl_rdatum)); + gidx_merge(&unionR, (GIDX *)DatumGetPointer(v->spl_rdatum)); v->spl_rdatum = PointerGetDatum(unionR); v->spl_ldatum_exists = v->spl_rdatum_exists = false; } - - -static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNumber *list1, int nlist1, GIDX **union1, OffsetNumber *list2, int nlist2, GIDX **union2) +static void +gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, + OffsetNumber *list1, + int nlist1, + GIDX **union1, + OffsetNumber *list2, + int nlist2, + GIDX **union2) { - bool firstToLeft = true; + bool firstToLeft = true; POSTGIS_DEBUG(4, "[GIST] picksplit in constructsplit function"); @@ -1711,32 +1546,41 @@ static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNu GIDX *RLr = gidx_copy(*union1); double sizeLR, sizeRL; - gidx_merge(&LRl, (GIDX*)DatumGetPointer(v->spl_ldatum)); - gidx_merge(&LRr, (GIDX*)DatumGetPointer(v->spl_rdatum)); - gidx_merge(&RLl, (GIDX*)DatumGetPointer(v->spl_ldatum)); - gidx_merge(&RLr, (GIDX*)DatumGetPointer(v->spl_rdatum)); + gidx_merge(&LRl, (GIDX *)DatumGetPointer(v->spl_ldatum)); + gidx_merge(&LRr, (GIDX *)DatumGetPointer(v->spl_rdatum)); + gidx_merge(&RLl, (GIDX *)DatumGetPointer(v->spl_ldatum)); + gidx_merge(&RLr, (GIDX *)DatumGetPointer(v->spl_rdatum)); - sizeLR = gidx_inter_volume(LRl,LRr); - sizeRL = gidx_inter_volume(RLl,RLr); + sizeLR = gidx_inter_volume(LRl, LRr); + sizeRL = gidx_inter_volume(RLl, RLr); POSTGIS_DEBUGF(4, "[GIST] sizeLR / sizeRL == %.12g / %.12g", sizeLR, sizeRL); if (sizeLR > sizeRL) firstToLeft = false; - } else { float p1, p2; - GISTENTRY oldUnion, addon; + GISTENTRY oldUnion, addon; - gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum, - NULL, NULL, InvalidOffsetNumber, false); + gistentryinit(oldUnion, + (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum, + NULL, + NULL, + InvalidOffsetNumber, + false); gistentryinit(addon, PointerGetDatum(*union1), NULL, NULL, InvalidOffsetNumber, false); - DirectFunctionCall3(gserialized_gist_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p1)); + DirectFunctionCall3(gserialized_gist_penalty, + PointerGetDatum(&oldUnion), + PointerGetDatum(&addon), + PointerGetDatum(&p1)); gistentryinit(addon, PointerGetDatum(*union2), NULL, NULL, InvalidOffsetNumber, false); - DirectFunctionCall3(gserialized_gist_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p2)); + DirectFunctionCall3(gserialized_gist_penalty, + PointerGetDatum(&oldUnion), + PointerGetDatum(&addon), + PointerGetDatum(&p2)); POSTGIS_DEBUGF(4, "[GIST] p1 / p2 == %.12g / %.12g", p1, p2); @@ -1754,10 +1598,10 @@ static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNu v->spl_nleft = nlist1; v->spl_nright = nlist2; if (v->spl_ldatum_exists) - gidx_merge(union1, (GIDX*)DatumGetPointer(v->spl_ldatum)); + gidx_merge(union1, (GIDX *)DatumGetPointer(v->spl_ldatum)); v->spl_ldatum = PointerGetDatum(*union1); if (v->spl_rdatum_exists) - gidx_merge(union2, (GIDX*)DatumGetPointer(v->spl_rdatum)); + gidx_merge(union2, (GIDX *)DatumGetPointer(v->spl_rdatum)); v->spl_rdatum = PointerGetDatum(*union2); } else @@ -1767,19 +1611,18 @@ static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNu v->spl_nleft = nlist2; v->spl_nright = nlist1; if (v->spl_ldatum_exists) - gidx_merge(union2, (GIDX*)DatumGetPointer(v->spl_ldatum)); + gidx_merge(union2, (GIDX *)DatumGetPointer(v->spl_ldatum)); v->spl_ldatum = PointerGetDatum(*union2); if (v->spl_rdatum_exists) - gidx_merge(union1, (GIDX*)DatumGetPointer(v->spl_rdatum)); + gidx_merge(union1, (GIDX *)DatumGetPointer(v->spl_rdatum)); v->spl_rdatum = PointerGetDatum(*union1); } v->spl_ldatum_exists = v->spl_rdatum_exists = false; } - -#define BELOW(d) (2*(d)) -#define ABOVE(d) ((2*(d))+1) +#define BELOW(d) (2 * (d)) +#define ABOVE(d) ((2 * (d)) + 1) /* ** GiST support function. Split an overflowing node into two new nodes. @@ -1792,9 +1635,9 @@ PG_FUNCTION_INFO_V1(gserialized_gist_picksplit); Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) { - GistEntryVector *entryvec = (GistEntryVector*) PG_GETARG_POINTER(0); + GistEntryVector *entryvec = (GistEntryVector *)PG_GETARG_POINTER(0); - GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1); + GIST_SPLITVEC *v = (GIST_SPLITVEC *)PG_GETARG_POINTER(1); OffsetNumber i; /* One union box for each half of the space. */ GIDX **box_union; @@ -1817,24 +1660,24 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) */ max_offset = entryvec->n - 1; - box_current = (GIDX*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key); + box_current = (GIDX *)DatumGetPointer(entryvec->vector[FirstOffsetNumber].key); box_pageunion = gidx_copy(box_current); /* Calculate the containing box (box_pageunion) for the whole page we are going to split. */ - for ( i = OffsetNumberNext(FirstOffsetNumber); i <= max_offset; i = OffsetNumberNext(i) ) + for (i = OffsetNumberNext(FirstOffsetNumber); i <= max_offset; i = OffsetNumberNext(i)) { - box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); + box_current = (GIDX *)DatumGetPointer(entryvec->vector[i].key); - if ( all_entries_equal == true && ! gidx_equals (box_pageunion, box_current) ) + if (all_entries_equal && !gidx_equals(box_pageunion, box_current)) all_entries_equal = false; - gidx_merge( &box_pageunion, box_current ); + gidx_merge(&box_pageunion, box_current); } POSTGIS_DEBUGF(3, "[GIST] box_pageunion: %s", gidx_to_string(box_pageunion)); /* Every box in the page is the same! So, we split and just put half the boxes in each child. */ - if ( all_entries_equal ) + if (all_entries_equal) { POSTGIS_DEBUG(4, "[GIST] picksplit finds all entries equal!"); gserialized_gist_picksplit_fallback(entryvec, v); @@ -1845,13 +1688,13 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) nbytes = (max_offset + 2) * sizeof(OffsetNumber); ndims_pageunion = GIDX_NDIMS(box_pageunion); POSTGIS_DEBUGF(4, "[GIST] ndims_pageunion == %d", ndims_pageunion); - pos = palloc(2*ndims_pageunion * sizeof(int)); - list = palloc(2*ndims_pageunion * sizeof(OffsetNumber*)); - box_union = palloc(2*ndims_pageunion * sizeof(GIDX*)); - for ( d = 0; d < ndims_pageunion; d++ ) + pos = palloc(2 * ndims_pageunion * sizeof(int)); + list = palloc(2 * ndims_pageunion * sizeof(OffsetNumber *)); + box_union = palloc(2 * ndims_pageunion * sizeof(GIDX *)); + for (d = 0; d < ndims_pageunion; d++) { - list[BELOW(d)] = (OffsetNumber*) palloc(nbytes); - list[ABOVE(d)] = (OffsetNumber*) palloc(nbytes); + list[BELOW(d)] = (OffsetNumber *)palloc(nbytes); + list[ABOVE(d)] = (OffsetNumber *)palloc(nbytes); box_union[BELOW(d)] = gidx_new(ndims_pageunion); box_union[ABOVE(d)] = gidx_new(ndims_pageunion); pos[BELOW(d)] = 0; @@ -1864,23 +1707,20 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) ** Each entry thereby ends up in three of the six partitions. */ POSTGIS_DEBUG(4, "[GIST] 'picksplit' calculating best split axis"); - for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) + for (i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i)) { - box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); + box_current = (GIDX *)DatumGetPointer(entryvec->vector[i].key); - for ( d = 0; d < ndims_pageunion; d++ ) + for (d = 0; d < ndims_pageunion; d++) { - if ( GIDX_GET_MIN(box_current,d)-GIDX_GET_MIN(box_pageunion,d) < GIDX_GET_MAX(box_pageunion,d)-GIDX_GET_MAX(box_current,d) ) - { - gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); - } + if (GIDX_GET_MIN(box_current, d) - GIDX_GET_MIN(box_pageunion, d) < + GIDX_GET_MAX(box_pageunion, d) - GIDX_GET_MAX(box_current, d)) + gserialized_gist_picksplit_addlist( + list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); else - { - gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); - } - + gserialized_gist_picksplit_addlist( + list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); } - } /* @@ -1890,7 +1730,7 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) ** sides of the page union box can occasionally all end up in one place, leading ** to this condition. */ - if ( gserialized_gist_picksplit_badratios(pos,ndims_pageunion) == true ) + if (gserialized_gist_picksplit_badratios(pos, ndims_pageunion) == true) { /* ** Instead we split on center points and see if we do better. @@ -1898,22 +1738,18 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) */ double *avgCenter = palloc(ndims_pageunion * sizeof(double)); - for ( d = 0; d < ndims_pageunion; d++ ) - { + for (d = 0; d < ndims_pageunion; d++) avgCenter[d] = 0.0; - } POSTGIS_DEBUG(4, "[GIST] picksplit can't find good split axis, trying center point method"); - for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) + for (i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i)) { - box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); - for ( d = 0; d < ndims_pageunion; d++ ) - { - avgCenter[d] += (GIDX_GET_MAX(box_current,d) + GIDX_GET_MIN(box_current,d)) / 2.0; - } + box_current = (GIDX *)DatumGetPointer(entryvec->vector[i].key); + for (d = 0; d < ndims_pageunion; d++) + avgCenter[d] += (GIDX_GET_MAX(box_current, d) + GIDX_GET_MIN(box_current, d)) / 2.0; } - for ( d = 0; d < ndims_pageunion; d++ ) + for (d = 0; d < ndims_pageunion; d++) { avgCenter[d] /= max_offset; pos[BELOW(d)] = pos[ABOVE(d)] = 0; /* Re-initialize our counters. */ @@ -1921,35 +1757,44 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) } /* For each of our entries... */ - for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) + for (i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i)) { double center; - box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); + box_current = (GIDX *)DatumGetPointer(entryvec->vector[i].key); - for ( d = 0; d < ndims_pageunion; d++ ) + for (d = 0; d < ndims_pageunion; d++) { - center = (GIDX_GET_MIN(box_current,d)+GIDX_GET_MAX(box_current,d))/2.0; - if ( center < avgCenter[d] ) - gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); - else if ( FPeq(center, avgCenter[d]) ) - if ( pos[BELOW(d)] > pos[ABOVE(d)] ) - gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); + center = (GIDX_GET_MIN(box_current, d) + GIDX_GET_MAX(box_current, d)) / 2.0; + if (center < avgCenter[d]) + gserialized_gist_picksplit_addlist( + list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); + else if (FPeq(center, avgCenter[d])) + if (pos[BELOW(d)] > pos[ABOVE(d)]) + gserialized_gist_picksplit_addlist(list[ABOVE(d)], + &(box_union[ABOVE(d)]), + box_current, + &(pos[ABOVE(d)]), + i); else - gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); + gserialized_gist_picksplit_addlist(list[BELOW(d)], + &(box_union[BELOW(d)]), + box_current, + &(pos[BELOW(d)]), + i); else - gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); + gserialized_gist_picksplit_addlist( + list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); } - } /* Do we have a good disposition now? If not, screw it, just cut the node in half. */ - if ( gserialized_gist_picksplit_badratios(pos,ndims_pageunion) == true ) + if (gserialized_gist_picksplit_badratios(pos, ndims_pageunion)) { - POSTGIS_DEBUG(4, "[GIST] picksplit still cannot find a good split! just cutting the node in half"); + POSTGIS_DEBUG(4, + "[GIST] picksplit still cannot find a good split! just cutting the node in half"); gserialized_gist_picksplit_fallback(entryvec, v); PG_RETURN_POINTER(v); } - } /* @@ -1959,41 +1804,43 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) ** number of entries in its regions is the most evenly distributed. ** TODO: what if the distributions are equal in two or more axes? */ - for ( d = 0; d < ndims_pageunion; d++ ) + for (d = 0; d < ndims_pageunion; d++) { - int posd = Max(pos[ABOVE(d)],pos[BELOW(d)]); - if ( posd < posmin ) + int posd = Max(pos[ABOVE(d)], pos[BELOW(d)]); + if (posd < posmin) { direction = d; posmin = posd; } } - if ( direction == -1 || posmin == entryvec->n ) - { - /* ERROR OUT HERE */ + if (direction == -1 || posmin == entryvec->n) elog(ERROR, "Error in building split, unable to determine split direction."); - } POSTGIS_DEBUGF(3, "[GIST] 'picksplit' splitting on axis %d", direction); - gserialized_gist_picksplit_constructsplit(v, list[BELOW(direction)], - pos[BELOW(direction)], - &(box_union[BELOW(direction)]), - list[ABOVE(direction)], - pos[ABOVE(direction)], - &(box_union[ABOVE(direction)]) ); - - POSTGIS_DEBUGF(4, "[GIST] spl_ldatum: %s", gidx_to_string((GIDX*)v->spl_ldatum)); - POSTGIS_DEBUGF(4, "[GIST] spl_rdatum: %s", gidx_to_string((GIDX*)v->spl_rdatum)); - - POSTGIS_DEBUGF(4, "[GIST] axis %d: parent range (%.12g, %.12g) left range (%.12g, %.12g), right range (%.12g, %.12g)", - direction, - GIDX_GET_MIN(box_pageunion, direction), GIDX_GET_MAX(box_pageunion, direction), - GIDX_GET_MIN((GIDX*)v->spl_ldatum, direction), GIDX_GET_MAX((GIDX*)v->spl_ldatum, direction), - GIDX_GET_MIN((GIDX*)v->spl_rdatum, direction), GIDX_GET_MAX((GIDX*)v->spl_rdatum, direction) ); + gserialized_gist_picksplit_constructsplit(v, + list[BELOW(direction)], + pos[BELOW(direction)], + &(box_union[BELOW(direction)]), + list[ABOVE(direction)], + pos[ABOVE(direction)], + &(box_union[ABOVE(direction)])); + + POSTGIS_DEBUGF(4, "[GIST] spl_ldatum: %s", gidx_to_string((GIDX *)v->spl_ldatum)); + POSTGIS_DEBUGF(4, "[GIST] spl_rdatum: %s", gidx_to_string((GIDX *)v->spl_rdatum)); + + POSTGIS_DEBUGF( + 4, + "[GIST] axis %d: parent range (%.12g, %.12g) left range (%.12g, %.12g), right range (%.12g, %.12g)", + direction, + GIDX_GET_MIN(box_pageunion, direction), + GIDX_GET_MAX(box_pageunion, direction), + GIDX_GET_MIN((GIDX *)v->spl_ldatum, direction), + GIDX_GET_MAX((GIDX *)v->spl_ldatum, direction), + GIDX_GET_MIN((GIDX *)v->spl_rdatum, direction), + GIDX_GET_MAX((GIDX *)v->spl_rdatum, direction)); PG_RETURN_POINTER(v); - } /* @@ -2003,15 +1850,14 @@ Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS) PG_FUNCTION_INFO_V1(gidx_in); Datum gidx_in(PG_FUNCTION_ARGS) { - ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function gidx_in not implemented"))); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function gidx_in not implemented"))); PG_RETURN_POINTER(NULL); } PG_FUNCTION_INFO_V1(gidx_out); Datum gidx_out(PG_FUNCTION_ARGS) { - GIDX *box = (GIDX *) PG_GETARG_POINTER(0); - char *result = gidx_to_string(box); - PG_RETURN_CSTRING(result); + GIDX *box = (GIDX *)PG_GETARG_POINTER(0); + char *result = gidx_to_string(box); + PG_RETURN_CSTRING(result); } diff --git a/postgis/lwgeom_box3d.c b/postgis/lwgeom_box3d.c index 236a51e1d..dfd23d051 100644 --- a/postgis/lwgeom_box3d.c +++ b/postgis/lwgeom_box3d.c @@ -28,6 +28,7 @@ #include "fmgr.h" #include "utils/elog.h" #include "utils/geo_decls.h" +#include "gserialized_spgist_3d.h" #include "../postgis_config.h" #include "lwgeom_pg.h" diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index 848a2ae61..285350c30 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -824,6 +824,48 @@ CREATE OPERATOR &&& ( JOIN = gserialized_gist_joinsel_nd ); +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_contains_nd(geometry, geometry) + RETURNS boolean + AS 'MODULE_PATHNAME' ,'gserialized_contains' + LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL; + +-- Availability: 2.5.0 +CREATE OPERATOR ~~ ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_contains_nd, + COMMUTATOR = '@@', + RESTRICT = gserialized_gist_sel_nd, + JOIN = gserialized_gist_joinsel_nd +); + +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_within_nd(geometry, geometry) + RETURNS boolean + AS 'MODULE_PATHNAME' ,'gserialized_within' + LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL; + +-- Availability: 2.5.0 +CREATE OPERATOR @@ ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_within_nd, + COMMUTATOR = '~~', + RESTRICT = gserialized_gist_sel_nd, + JOIN = gserialized_gist_joinsel_nd +); + +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_same_nd(geometry, geometry) + RETURNS boolean + AS 'MODULE_PATHNAME' ,'gserialized_same' + LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL; + +-- Availability: 2.5.0 +CREATE OPERATOR ~~= ( + LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_same_nd, + COMMUTATOR = '~~=', + RESTRICT = gserialized_gist_sel_nd, + JOIN = gserialized_gist_joinsel_nd +); + -- Availability: 2.2.0 CREATE OR REPLACE FUNCTION geometry_distance_centroid_nd(geometry,geometry) RETURNS float8 @@ -866,9 +908,12 @@ CREATE OPERATOR CLASS gist_geometry_ops_nd FOR TYPE geometry USING GIST AS STORAGE gidx, OPERATOR 3 &&& , --- OPERATOR 6 ~= , --- OPERATOR 7 ~ , --- OPERATOR 8 @ , + -- Availability: 2.5.0 + OPERATOR 6 ~~= , + -- Availability: 2.5.0 + OPERATOR 7 ~~ , + -- Availability: 2.5.0 + OPERATOR 8 @@ , -- Availability: 2.2.0 OPERATOR 13 <<->> FOR ORDER BY pg_catalog.float_ops, #if POSTGIS_PGSQL_VERSION >= 95 diff --git a/postgis/postgis_spgist.sql.in b/postgis/postgis_spgist.sql.in index bcde4ea38..3fd2424e4 100644 --- a/postgis/postgis_spgist.sql.in +++ b/postgis/postgis_spgist.sql.in @@ -163,4 +163,107 @@ CREATE OPERATOR CLASS spgist_geometry_ops_3d FUNCTION 4 geometry_spgist_inner_consistent_3d(internal, internal), FUNCTION 5 geometry_spgist_leaf_consistent_3d(internal, internal), FUNCTION 6 geometry_spgist_compress_3d(internal); + +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +-- SP-GiST ND Support Functions +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- + +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +-- Geometry +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- + +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_config_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_config_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_choose_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_choose_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_picksplit_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_picksplit_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_inner_consistent_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_inner_consistent_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_leaf_consistent_nd(internal, internal) + RETURNS bool + AS 'MODULE_PATHNAME' ,'gserialized_spgist_leaf_consistent_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geometry_spgist_compress_nd(internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_spgist_compress_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Availability: 2.5.0 +CREATE OPERATOR CLASS spgist_geometry_ops_nd + FOR TYPE geometry USING SPGIST AS + OPERATOR 3 &&& , + OPERATOR 6 ~~= , + OPERATOR 7 ~~ , + OPERATOR 8 @@ , + FUNCTION 1 geometry_spgist_config_nd(internal, internal), + FUNCTION 2 geometry_spgist_choose_nd(internal, internal), + FUNCTION 3 geometry_spgist_picksplit_nd(internal, internal), + FUNCTION 4 geometry_spgist_inner_consistent_nd(internal, internal), + FUNCTION 5 geometry_spgist_leaf_consistent_nd(internal, internal), + FUNCTION 6 geometry_spgist_compress_nd(internal); + +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +-- Geography +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- + +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_config_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_config_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_choose_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_choose_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_picksplit_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_picksplit_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_inner_consistent_nd(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' ,'gserialized_spgist_inner_consistent_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_leaf_consistent_nd(internal, internal) + RETURNS bool + AS 'MODULE_PATHNAME' ,'gserialized_spgist_leaf_consistent_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- Availability: 2.5.0 +CREATE OR REPLACE FUNCTION geography_spgist_compress_nd(internal) + RETURNS internal + AS 'MODULE_PATHNAME' ,'gserialized_spgist_compress_nd' + LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Availability: 2.5.0 +CREATE OPERATOR CLASS spgist_geography_ops_nd + DEFAULT FOR TYPE geography USING SPGIST AS + OPERATOR 3 && , +-- OPERATOR 6 ~= , +-- OPERATOR 7 ~ , +-- OPERATOR 8 @ , + FUNCTION 1 geography_spgist_config_nd(internal, internal), + FUNCTION 2 geography_spgist_choose_nd(internal, internal), + FUNCTION 3 geography_spgist_picksplit_nd(internal, internal), + FUNCTION 4 geography_spgist_inner_consistent_nd(internal, internal), + FUNCTION 5 geography_spgist_leaf_consistent_nd(internal, internal), + FUNCTION 6 geography_spgist_compress_nd(internal); + #endif diff --git a/regress/core/Makefile.in b/regress/core/Makefile.in index f5eb6bc50..045b7667f 100644 --- a/regress/core/Makefile.in +++ b/regress/core/Makefile.in @@ -233,7 +233,9 @@ endif ifeq ($(HAVE_SPGIST),yes) TESTS += \ regress_spgist_index_2d \ - regress_spgist_index_3d + regress_spgist_index_3d \ + regress_spgist_index_nd #\ +# regress_gist_index_nd endif ifeq ($(HAVE_PROTOBUF),yes) diff --git a/regress/core/operators.sql b/regress/core/operators.sql index 206fd88dd..0b8a44e3b 100644 --- a/regress/core/operators.sql +++ b/regress/core/operators.sql @@ -115,30 +115,121 @@ select 'ndov7', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry &&& -- &&& with mixed dimensions WITH v(i,g) AS ( VALUES - (1,'POINT(0 0)'::geometry), -- true, infinite M range - (2,'POINTZ(0 0 1)'), -- true, infinite M range - (3,'POINTZ(0 0 0)'), -- true, infinite M range - (4,'POINTM(0 0 1)'), -- true, fully defined overlap - (5,'POINTZM(0 0 0 1)'), -- true, fully defined overlap - (6,'POINTZM(0 0 1 0)'), -- false, M out of range - (7,'LINESTRINGM(-1 0 2,1 0 3)'), -- false, M out of range - (8,'LINESTRINGZ(-1 0 2,1 0 3)') -- true, infinite M range + (1,'POINT(0 0)'::geometry), -- true for {1,2,3,4,5,6,7,8,9} + (2,'POINTZ(0 0 1)'), -- true for {1,2,4,6,7} + (3,'POINTZ(0 0 0)'), -- true for {1,3,4,5,7} + (4,'POINTM(0 0 1)'), -- true for {1,2,3,4,5,8} + (5,'POINTZM(0 0 0 1)'), -- true for {1,3,4,5} + (6,'POINTZM(0 0 1 0)'), -- true for {1,2,6} + (7,'LINESTRINGM(-1 0 2,1 0 3)'), -- true for {1,2,3,7,8,9} + (8,'LINESTRINGZ(-1 0 2,1 0 3)'), -- true for {1,4,7,8,9} + (9,'LINESTRINGZM(-1 0 2 2,1 0 3 3)') -- true for {1,7,8,9} ) -SELECT 'ndovm1', array_agg(i) FROM v WHERE g &&& 'POINTM(0 0 1)'::geometry -ORDER BY 1; +SELECT 'ndovm', v1.i, array_agg(v2.i) FROM v v1, v v2 WHERE v1.g &&& v2.g +group by v1.i +ORDER BY 2; + +-- nd contains ~~ + +select 'ndcont1', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(3 3 3 5)'::geometry; -- f +select 'ndcont2', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(3 3 5 3)'::geometry; -- f +select 'ndcont3', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(3 5 3 3)'::geometry; -- f +select 'ndcont4', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(5 3 3 3)'::geometry; -- f +select 'ndcont5', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(3 3 3 3)'::geometry; -- t +select 'ndcont6', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(2 4 2 4)'::geometry; -- t +select 'ndcont7', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~ + 'POINT(4 2 4 2)'::geometry; -- t + +-- ~~ with mixed dimensions + +WITH v(i,g) AS ( VALUES + (1,'POINT(0 0)'::geometry), + (2,'POINTZ(0 0 1)'), + (3,'POINTZ(0 0 0)'), + (4,'POINTM(0 0 1)'), + (5,'POINTZM(0 0 0 1)'), + (6,'POINTZM(0 0 1 0)'), + (7,'LINESTRINGM(-1 0 2,1 0 3)'), + (8,'LINESTRINGZ(-1 0 2,1 0 3)'), + (9,'LINESTRINGZM(-1 0 2 2,1 0 3 3)') + ) +SELECT 'ndcontm', v1.i, array_agg(v2.i) FROM v v1, v v2 WHERE v1.g ~~ v2.g +group by v1.i +ORDER BY 2; + +-- nd within @@ + +select 'ndwithin1', 'POINT(3 3 3 5)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- f +select 'ndwithin2', 'POINT(3 3 5 3)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- f +select 'ndwithin3', 'POINT(3 5 3 3)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- f +select 'ndwithin4', 'POINT(5 3 3 3)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- f +select 'ndwithin5', 'POINT(3 3 3 3)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- t +select 'ndwithin6', 'POINT(2 4 2 4)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- t +select 'ndwithin7', 'POINT(4 2 4 2)'::geometry @@ + 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry; -- t + +-- @@ with mixed dimensions + +WITH v(i,g) AS ( VALUES + (1,'POINT(0 0)'::geometry), + (2,'POINTZ(0 0 1)'), + (3,'POINTZ(0 0 0)'), + (4,'POINTM(0 0 1)'), + (5,'POINTZM(0 0 0 1)'), + (6,'POINTZM(0 0 1 0)'), + (7,'LINESTRINGM(-1 0 2,1 0 3)'), + (8,'LINESTRINGZ(-1 0 2,1 0 3)'), + (9,'LINESTRINGZM(-1 0 2 2,1 0 3 3)') + ) +SELECT 'ndwithinm', v1.i, array_agg(v2.i) FROM v v1, v v2 WHERE v1.g @@ v2.g +group by v1.i +ORDER BY 2; + +-- nd same ~~= + +select 'ndsame1', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'POINT(3 3 3 3)'::geometry; -- f +select 'ndsame2', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(2 2 2 2, 4 4 4 5)'::geometry; -- f +select 'ndsame3', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(2 2 2 2, 4 4 5 4)'::geometry; -- f +select 'ndsame4', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(2 2 2 2, 4 5 4 4)'::geometry; -- f +select 'ndsame5', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(2 2 2 2, 5 4 4 4)'::geometry; -- f +select 'ndsame6', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(4 4 4 4, 2 2 2 2)'::geometry; -- t +select 'ndsame7', 'LINESTRING(2 2 2 2, 4 4 4 4)'::geometry ~~= + 'LINESTRING(2 2 2 4, 2 2 4 2, 2 4 2 2, 4 2 2 2)'::geometry; -- t + +-- ~~= with mixed dimensions WITH v(i,g) AS ( VALUES - (1,'POINT(0 0)'::geometry), -- true, infinite Z range - (2,'POINTZ(0 0 1)'), -- true, fully defined overlap - (3,'POINTZ(0 0 0)'), -- false, Z out of range - (4,'POINTM(0 0 0)'), -- true, infinite Z range - (5,'POINTZM(0 0 0 1)'), -- false, Z out of range - (6,'POINTZM(0 0 1 0)'), -- true, fully defined overlap - (7,'LINESTRINGM(-1 0 2,1 0 3)'), -- true, infinite Z range - (8,'LINESTRINGZ(-1 0 2,1 0 3)') -- false, Z out of range + (1,'POINT(0 0)'::geometry), + (2,'POINTZ(0 0 1)'), + (3,'POINTZ(0 0 0)'), + (4,'POINTM(0 0 1)'), + (5,'POINTZM(0 0 0 1)'), + (6,'POINTZM(0 0 1 0)'), + (7,'LINESTRINGM(-1 0 2,1 0 3)'), + (8,'LINESTRINGZ(-1 0 2,1 0 3)'), + (9,'LINESTRINGZM(-1 0 2 2,1 0 3 3)') ) -SELECT 'ndovm2', array_agg(i) FROM v WHERE g &&& 'POINTZ(0 0 1)'::geometry -ORDER BY 1; +SELECT 'ndsamem', v1.i, array_agg(v2.i) FROM v v1, v v2 WHERE v1.g ~~= v2.g +group by v1.i +ORDER BY 2; -- GROUP BY on empty SELECT '#3777', ST_AsText(geom), count(*) diff --git a/regress/core/operators_expected b/regress/core/operators_expected index b5018c509..801e8bfec 100644 --- a/regress/core/operators_expected +++ b/regress/core/operators_expected @@ -54,8 +54,63 @@ ndov4|f ndov5|t ndov6|t ndov7|t -ndovm1|{1,2,3,4,5,8} -ndovm2|{1,2,4,6,7} +ndovm|1|{1,2,3,4,5,6,7,8,9} +ndovm|2|{1,2,4,6,7} +ndovm|3|{1,3,4,5,7} +ndovm|4|{1,2,3,4,5,8} +ndovm|5|{1,3,4,5} +ndovm|6|{1,2,6} +ndovm|7|{1,2,3,7,8,9} +ndovm|8|{1,4,7,8,9} +ndovm|9|{1,7,8,9} +ndcont1|f +ndcont2|f +ndcont3|f +ndcont4|f +ndcont5|t +ndcont6|t +ndcont7|t +ndcontm|1|{1,2,3,4,5,6} +ndcontm|2|{1,2,4,6} +ndcontm|3|{1,3,4,5} +ndcontm|4|{1,2,3,4,5} +ndcontm|5|{1,3,4,5} +ndcontm|6|{1,2,6} +ndcontm|7|{1,2,3,7,8,9} +ndcontm|8|{1,4,7,8,9} +ndcontm|9|{1,7,8,9} +ndwithin1|f +ndwithin2|f +ndwithin3|f +ndwithin4|f +ndwithin5|t +ndwithin6|t +ndwithin7|t +ndwithinm|1|{1,2,3,4,5,6,7,8,9} +ndwithinm|2|{1,2,4,6,7} +ndwithinm|3|{1,3,4,5,7} +ndwithinm|4|{1,2,3,4,5,8} +ndwithinm|5|{1,3,4,5} +ndwithinm|6|{1,2,6} +ndwithinm|7|{7,8,9} +ndwithinm|8|{7,8,9} +ndwithinm|9|{7,8,9} +ndsame1|f +ndsame2|f +ndsame3|f +ndsame4|f +ndsame5|f +ndsame6|t +ndsame7|t +ndsamem|1|{1,2,3,4,5,6} +ndsamem|2|{1,2,4,6} +ndsamem|3|{1,3,4,5} +ndsamem|4|{1,2,3,4,5} +ndsamem|5|{1,3,4,5} +ndsamem|6|{1,2,6} +ndsamem|7|{7,8,9} +ndsamem|8|{7,8,9} +ndsamem|9|{7,8,9} #3777|GEOMETRYCOLLECTION EMPTY|1 #3777|LINESTRING(0 0,0 1)|1 #3777|POINT EMPTY|1