[postgis-tickets] r15680 - #3841, geography btree handling of empty
Paul Ramsey
pramsey at cleverelephant.ca
Mon Sep 11 07:30:27 PDT 2017
Author: pramsey
Date: 2017-09-11 07:30:27 -0700 (Mon, 11 Sep 2017)
New Revision: 15680
Modified:
trunk/libpgcommon/gserialized_gist.c
trunk/libpgcommon/gserialized_gist.h
trunk/postgis/geography_btree.c
Log:
#3841, geography btree handling of empty
Modified: trunk/libpgcommon/gserialized_gist.c
===================================================================
--- trunk/libpgcommon/gserialized_gist.c 2017-09-11 13:54:32 UTC (rev 15679)
+++ trunk/libpgcommon/gserialized_gist.c 2017-09-11 14:30:27 UTC (rev 15680)
@@ -316,7 +316,7 @@
** full geography and return the box based on that. If no box is available,
** return LW_FAILURE, otherwise LW_SUCCESS.
*/
-int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx)
+int gserialized_get_gidx_p(const GSERIALIZED *g, GIDX *gidx)
{
int result = LW_SUCCESS;
Modified: trunk/libpgcommon/gserialized_gist.h
===================================================================
--- trunk/libpgcommon/gserialized_gist.h 2017-09-11 13:54:32 UTC (rev 15679)
+++ trunk/libpgcommon/gserialized_gist.h 2017-09-11 14:30:27 UTC (rev 15680)
@@ -97,7 +97,7 @@
int gserialized_datum_get_gidx_p(Datum gserialized_datum, GIDX *gidx);
/* Pull out the gidx bounding box from an already de-toasted geography */
-int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx);
+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);
Modified: trunk/postgis/geography_btree.c
===================================================================
--- trunk/postgis/geography_btree.c 2017-09-11 13:54:32 UTC (rev 15679)
+++ trunk/postgis/geography_btree.c 2017-09-11 14:30:27 UTC (rev 15680)
@@ -47,20 +47,22 @@
** geocentric bounding box. We don't divide by two
** because we're only using the values for comparison.
*/
-static void geography_gidx_center(GIDX *gidx, POINT3D *p)
+static void geography_gidx_center(const GIDX *gidx, POINT3D *p)
{
p->x = GIDX_GET_MIN(gidx, 0) + GIDX_GET_MAX(gidx, 0);
p->y = GIDX_GET_MIN(gidx, 1) + GIDX_GET_MAX(gidx, 1);
p->z = GIDX_GET_MIN(gidx, 2) + GIDX_GET_MAX(gidx, 2);
}
-/*
-** BTree support function. Based on two geographies return true if
-** they are "less than" and false otherwise.
-*/
-PG_FUNCTION_INFO_V1(geography_lt);
-Datum geography_lt(PG_FUNCTION_ARGS)
+static int geography_cmp_internal(const GSERIALIZED *g1, const GSERIALIZED *g2)
{
+ int cmp, have_box1, have_box2;
+ int g1_is_empty = gserialized_is_empty(g1);
+ int g2_is_empty = gserialized_is_empty(g2);
+ size_t sz1 = SIZE_GET(g1->size);
+ size_t sz2 = SIZE_GET(g2->size);
+ size_t sz = sz1 < sz2 ? sz1 : sz2;
+
/* Put aside some stack memory and use it for GIDX pointers. */
char gboxmem1[GIDX_MAX_SIZE];
char gboxmem2[GIDX_MAX_SIZE];
@@ -68,50 +70,86 @@
GIDX *gbox2 = (GIDX*)gboxmem2;
POINT3D p1, p2;
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
+ /* Empty == Empty */
+ if (g1_is_empty && g2_is_empty)
+ return 0;
+
+ /* Empty < Non-empty */
+ if (g1_is_empty)
+ return -1;
+
+ /* Non-empty > Empty */
+ if (g2_is_empty)
+ return 1;
+
+ /* Return equality for perfect equality only */
+ cmp = memcmp(g1, g2, sz);
+ if ( sz1 == sz2 && cmp == 0 )
+ return 0;
+
+ /* Must be able to build box for each argument */
+ /* (ie, not empty geometry) */
+ have_box1 = gserialized_get_gidx_p(g1, gbox1);
+ have_box2 = gserialized_get_gidx_p(g2, gbox2);
+ if ( ! (have_box1 && have_box2) )
{
- PG_RETURN_BOOL(FALSE);
+ lwerror("%s [%d] unable to calculate boxes of geographies", __FILE__, __LINE__);
}
+ /* Order geographies more or less left-to-right */
+ /* using the centroids, and preferring the X axis */
geography_gidx_center(gbox1, &p1);
geography_gidx_center(gbox2, &p2);
- if ( p1.x < p2.x || p1.y < p2.y || p1.z < p2.z )
- PG_RETURN_BOOL(TRUE);
+ if ( ! FP_EQUALS(p1.x, p2.x) )
+ return p1.x < p2.x ? -1 : 1;
- PG_RETURN_BOOL(FALSE);
+ if ( ! FP_EQUALS(p1.y, p2.y) )
+ return p1.y < p2.y ? -1 : 1;
+
+ if ( ! FP_EQUALS(p1.z, p2.z) )
+ return p1.z < p2.z ? -1 : 1;
+
+ /* Geographies that are the "same in centroid" */
+ /* but didn't pass the exact equality test */
+ /* First order smallest-first */
+ if (sz1 != sz2)
+ return sz1 < sz2 ? -1 : 1;
+ /* Then just order on the memcmp return */
+ else
+ return cmp;
}
/*
** BTree support function. Based on two geographies return true if
+** they are "less than" and false otherwise.
+*/
+PG_FUNCTION_INFO_V1(geography_lt);
+Datum geography_lt(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ int cmp = geography_cmp_internal(g1, g2);
+ if (cmp < 0)
+ PG_RETURN_BOOL(TRUE);
+ else
+ PG_RETURN_BOOL(FALSE);
+}
+
+/*
+** BTree support function. Based on two geographies return true if
** they are "less than or equal" and false otherwise.
*/
PG_FUNCTION_INFO_V1(geography_le);
Datum geography_le(PG_FUNCTION_ARGS)
{
- /* Put aside some stack memory and use it for GIDX pointers. */
- char gboxmem1[GIDX_MAX_SIZE];
- char gboxmem2[GIDX_MAX_SIZE];
- GIDX *gbox1 = (GIDX*)gboxmem1;
- GIDX *gbox2 = (GIDX*)gboxmem2;
- POINT3D p1, p2;
-
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
- {
- PG_RETURN_BOOL(FALSE);
- }
-
- geography_gidx_center(gbox1, &p1);
- geography_gidx_center(gbox2, &p2);
-
- if ( p1.x <= p2.x || p1.y <= p2.y || p1.z <= p2.z )
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ int cmp = geography_cmp_internal(g1, g2);
+ if (cmp <= 0)
PG_RETURN_BOOL(TRUE);
-
- PG_RETURN_BOOL(FALSE);
+ else
+ PG_RETURN_BOOL(FALSE);
}
/*
@@ -121,27 +159,13 @@
PG_FUNCTION_INFO_V1(geography_gt);
Datum geography_gt(PG_FUNCTION_ARGS)
{
- /* Put aside some stack memory and use it for GIDX pointers. */
- char gboxmem1[GIDX_MAX_SIZE];
- char gboxmem2[GIDX_MAX_SIZE];
- GIDX *gbox1 = (GIDX*)gboxmem1;
- GIDX *gbox2 = (GIDX*)gboxmem2;
- POINT3D p1, p2;
-
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
- {
- PG_RETURN_BOOL(FALSE);
- }
-
- geography_gidx_center(gbox1, &p1);
- geography_gidx_center(gbox2, &p2);
-
- if ( p1.x > p2.x && p1.y > p2.y && p1.z > p2.z )
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ int cmp = geography_cmp_internal(g1, g2);
+ if (cmp > 0)
PG_RETURN_BOOL(TRUE);
-
- PG_RETURN_BOOL(FALSE);
+ else
+ PG_RETURN_BOOL(FALSE);
}
/*
@@ -151,27 +175,41 @@
PG_FUNCTION_INFO_V1(geography_ge);
Datum geography_ge(PG_FUNCTION_ARGS)
{
- /* Put aside some stack memory and use it for GIDX pointers. */
- char gboxmem1[GIDX_MAX_SIZE];
- char gboxmem2[GIDX_MAX_SIZE];
- GIDX *gbox1 = (GIDX*)gboxmem1;
- GIDX *gbox2 = (GIDX*)gboxmem2;
- POINT3D p1, p2;
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ int cmp = geography_cmp_internal(g1, g2);
+ if (cmp >= 0)
+ PG_RETURN_BOOL(TRUE);
+ else
+ PG_RETURN_BOOL(FALSE);
+}
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
- {
- PG_RETURN_BOOL(FALSE);
- }
-
- geography_gidx_center(gbox1, &p1);
- geography_gidx_center(gbox2, &p2);
-
- if ( p1.x >= p2.x && p1.y >= p2.y && p1.z >= p2.z )
+/*
+** BTree support function. Based on two geographies return true if
+** they are "equal" and false otherwise.
+*/
+PG_FUNCTION_INFO_V1(geography_eq);
+Datum geography_eq(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ int cmp = geography_cmp_internal(g1, g2);
+ if (cmp == 0)
PG_RETURN_BOOL(TRUE);
+ else
+ PG_RETURN_BOOL(FALSE);
+}
- PG_RETURN_BOOL(FALSE);
+/*
+** BTree support function. Based on two geographies return true if
+** they are "equal" and false otherwise.
+*/
+PG_FUNCTION_INFO_V1(geography_cmp);
+Datum geography_cmp(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
+ GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
+ PG_RETURN_INT32(geography_cmp_internal(g1, g2));
}
@@ -208,88 +246,4 @@
}
#endif
-/*
-** BTree support function. Based on two geographies return true if
-** they are "equal" and false otherwise.
-*/
-PG_FUNCTION_INFO_V1(geography_eq);
-Datum geography_eq(PG_FUNCTION_ARGS)
-{
- /* Put aside some stack memory and use it for GIDX pointers. */
- char gboxmem1[GIDX_MAX_SIZE];
- char gboxmem2[GIDX_MAX_SIZE];
- GIDX *gbox1 = (GIDX*)gboxmem1;
- GIDX *gbox2 = (GIDX*)gboxmem2;
- POINT3D p1, p2;
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
- {
- PG_RETURN_BOOL(FALSE);
- }
-
- geography_gidx_center(gbox1, &p1);
- geography_gidx_center(gbox2, &p2);
-
- if ( FP_EQUALS(p1.x, p2.x) && FP_EQUALS(p1.y, p2.y) && FP_EQUALS(p1.z, p2.z) )
- PG_RETURN_BOOL(TRUE);
-
- PG_RETURN_BOOL(FALSE);
-
-}
-
-/*
-** BTree support function. Based on two geographies return true if
-** they are "equal" and false otherwise.
-*/
-PG_FUNCTION_INFO_V1(geography_cmp);
-Datum geography_cmp(PG_FUNCTION_ARGS)
-{
- /* Put aside some stack memory and use it for GIDX pointers. */
- char gboxmem1[GIDX_MAX_SIZE];
- char gboxmem2[GIDX_MAX_SIZE];
- GIDX *gbox1 = (GIDX*)gboxmem1;
- GIDX *gbox2 = (GIDX*)gboxmem2;
- POINT3D p1, p2;
-
- /* Must be able to build box for each argument (ie, not empty geometry) */
- if ( ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(0), gbox1) ||
- ! gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), gbox2) )
- {
- PG_RETURN_BOOL(FALSE);
- }
-
- geography_gidx_center(gbox1, &p1);
- geography_gidx_center(gbox2, &p2);
-
- if ( ! FP_EQUALS(p1.x, p2.x) )
- {
- if (p1.x < p2.x)
- {
- PG_RETURN_INT32(-1);
- }
- PG_RETURN_INT32(1);
- }
-
- if ( ! FP_EQUALS(p1.y, p2.y) )
- {
- if (p1.y < p2.y)
- {
- PG_RETURN_INT32(-1);
- }
- PG_RETURN_INT32(1);
- }
-
- if ( ! FP_EQUALS(p1.z, p2.z) )
- {
- if (p1.z < p2.z)
- {
- PG_RETURN_INT32(-1);
- }
- PG_RETURN_INT32(1);
- }
-
- PG_RETURN_INT32(0);
-}
-
More information about the postgis-tickets
mailing list