]> granicus.if.org Git - postgis/commitdiff
_postgis_index_extent() for extent from index
authorPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 13 Dec 2017 20:51:36 +0000 (20:51 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 13 Dec 2017 20:51:36 +0000 (20:51 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@16151 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
libpgcommon/gserialized_gist.h
postgis/gserialized_estimate.c
postgis/gserialized_gist_2d.c
postgis/gserialized_gist_nd.c
regress/estimatedextent.sql
regress/estimatedextent_expected

diff --git a/NEWS b/NEWS
index ecac7ba041d60d63014ba65ea5b23c501796c294..65a298a0263fad712580a1c7bf658b24cdeffbcf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,7 @@ PostGIS 2.5.0
   - #3564, ST_LineInterpolatePoints (Dan Baston)
   - #3896, PostGIS_Extensions_Upgrade()
   - #3913, Upgrade when creating extension from unpackaged (Sandro Santilli)
+  - #2256, _postgis_index_extent() for extent from index (Paul Ramsey)
 
 * Breaking Changes *
   - #3885, version number removed from address_standardize lib file
index 67d3f2e018f9643f75379345036c5bb4ed4bcdc4..8ebf6bfe3a942a072fc9213b4b618b9ee080ab0d 100644 (file)
@@ -66,6 +66,8 @@ GIDX* gidx_new(int ndims) ;
 /* Increase the size of a GIDX */
 void gidx_expand(GIDX *a, float d);
 
+/* Note empty GIDX */
+bool gidx_is_unknown(const GIDX *a);
 
 /* Generate human readable form for GIDX. */
 char* gidx_to_string(GIDX *a) ;
@@ -99,6 +101,9 @@ GIDX* gidx_copy(GIDX *b);
 /* Grow the first argument to contain the second */
 void gidx_merge(GIDX **b_union, GIDX *b_new);
 
+/* Note empty BOX2DF */
+bool box2df_is_empty(const BOX2DF *a);
+
 /* Fill in a gbox from a GIDX */
 void gbox_from_gidx(GIDX *a, GBOX *gbox, int flags);
 
index b475a8b01e5c77c1aef771ca0abeb2a9b9eb13ca..ad9a42ac1b6134d29fd5bed427a5275cc1c2a38d 100644 (file)
@@ -2327,7 +2327,7 @@ Datum gserialized_estimated_extent(PG_FUNCTION_ARGS)
                PG_RETURN_NULL();
        }
 
-#if 0
+#if 1
        /* Read the extent from the head of the spatial index, if there is one */
        idx_oid = table_get_spatial_index(tbl_oid, col, &key_type);
        if (!idx_oid)
@@ -2525,11 +2525,15 @@ spatial_index_read_extent(Oid idx_oid, int key_type)
 
        if (key_type == STATISTIC_SLOT_2D && bounds_2df)
        {
+               if (box2df_is_empty(bounds_2df))
+                       return NULL;
                gbox = gbox_new(0);
                box2df_to_gbox_p(bounds_2df, gbox);
        }
        else if (key_type == STATISTIC_SLOT_ND && bounds_gidx)
        {
+               if (gidx_is_unknown(bounds_gidx))
+                       return NULL;
                gbox = gbox_new(0);
                gbox_from_gidx(bounds_gidx, gbox, 0);
        }
index 634c959825335b66c47d99266a81d8d68b003a94..103d95e74bd6bbb5d8f71a00d2b87620d88f0950 100644 (file)
@@ -153,7 +153,32 @@ BOX2DF* box2df_copy(BOX2DF *b)
        return c;
 }
 
+inline bool box2df_is_empty(const BOX2DF *a)
+{
+       if (isnan(a->xmin))
+               return true;
+       else
+               return false;
+}
+
+static inline void box2df_set_empty(BOX2DF *a)
+{
+       a->xmin = a->xmax = a->ymin = a->ymax = NAN;
+       return;
+}
 
+static inline void box2df_set_finite(BOX2DF *a)
+{
+       if ( ! isfinite(a->xmax) )
+               a->xmax = FLT_MAX;
+       if ( ! isfinite(a->ymax) )
+               a->ymax = FLT_MAX;
+       if ( ! isfinite(a->ymin) )
+               a->ymin = -1*FLT_MAX;
+       if ( ! isfinite(a->xmin) )
+               a->xmin = -1*FLT_MAX;
+       return;
+}
 
 /* Enlarge b_union to contain b_new. If b_new contains more
    dimensions than b_union, expand b_union to contain those dimensions. */
@@ -202,7 +227,7 @@ static float box2df_size(const BOX2DF *a)
 {
        float result;
 
-       if ( a == NULL )
+       if ( a == NULL || box2df_is_empty(a) )
                return (float)0.0;
 
        if ( (a->xmax <= a->xmin) || (a->ymax <= a->ymin) )
@@ -219,7 +244,7 @@ static float box2df_size(const BOX2DF *a)
 
 static float box2df_edge(const BOX2DF *a)
 {
-       if ( a == NULL )
+       if ( a == NULL || box2df_is_empty(a) )
                return (float)0.0;
 
        return ((a->xmax) - (a->xmin)) + ((a->ymax) - (a->ymin));
@@ -237,10 +262,10 @@ static float box2df_union_size(const BOX2DF *a, const BOX2DF *b)
                return 0.0;
        }
 
-       if ( a == NULL )
+       if ( a == NULL || box2df_is_empty(a) )
                return box2df_size(b);
 
-       if ( b == NULL )
+       if ( b == NULL || box2df_is_empty(b) )
                return box2df_size(a);
 
        result = ((double)Max(a->xmax,b->xmax) - (double)Min(a->xmin,b->xmin)) *
@@ -264,10 +289,10 @@ static float box2df_union_edge(const BOX2DF *a, const BOX2DF *b)
                return 0.0;
        }
 
-       if ( a == NULL )
+       if ( a == NULL || box2df_is_empty(a) )
                return box2df_edge(b);
 
-       if ( b == NULL )
+       if ( b == NULL || box2df_is_empty(b) )
                return box2df_edge(a);
 
        result = (Max(a->xmax,b->xmax) - Min(a->xmin,b->xmin)) +
@@ -308,6 +333,10 @@ static inline void box2df_validate(BOX2DF *b)
 {
        float tmp;
        POSTGIS_DEBUGF(5,"validating box2df (%s)", box2df_to_string(b));
+
+       if ( box2df_is_empty(b) )
+               return;
+
        if ( b->xmax < b->xmin )
        {
                tmp = b->xmin;
@@ -325,7 +354,8 @@ static inline void box2df_validate(BOX2DF *b)
 
 static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
             (a->ymin > b->ymax) || (b->ymin > a->ymax) )
@@ -338,7 +368,12 @@ static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
 
 bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b )
+               return false;
+
+       /* All things can contain EMPTY (except EMPTY) */
+       if ( box2df_is_empty(b) && ! box2df_is_empty(a) )
+               return true;
 
        if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
             (a->ymin > b->ymin) || (a->ymax < b->ymax) )
@@ -351,7 +386,12 @@ bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b )
+               return false;
+
+       /* EMPTY is within all other things (except EMPTY) */
+       if ( box2df_is_empty(a) && ! box2df_is_empty(b) )
+               return true;
 
        POSTGIS_DEBUG(5, "entered function");
        return box2df_contains(b,a);
@@ -359,25 +399,24 @@ static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( a &&  b ) {
-               if ( (a->xmin != b->xmin) || (a->xmax != b->xmax) ||
-                    (a->ymin != b->ymin) || (a->ymax != b->ymax) )
-               {
-                       return false;
-               }
+       if ( !a && !b )
                return true;
-       } else if ( a || b ) {
-               /* one empty, one not */
+       else if ( !a || !b )
                return false;
-       } else {
-               /* both empty */
+       else if ( box2df_is_empty(a) && box2df_is_empty(b) )
                return true;
-       }
+       else if ( box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
+       else if ((a->xmin == b->xmin) && (a->xmax == b->xmax) && (a->ymin == b->ymin) && (a->ymax == b->ymax))
+               return true;
+       else
+               return false;
 }
 
 static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.xmax <= b.xmax */
        return a->xmax <= b->xmax;
@@ -385,7 +424,8 @@ static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.xmax < b.xmin */
        return a->xmax < b->xmin;
@@ -393,7 +433,8 @@ static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.xmin > b.xmax */
        return a->xmin > b->xmax;
@@ -401,7 +442,8 @@ static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.xmin >= b.xmin */
        return a->xmin >= b->xmin;
@@ -409,7 +451,8 @@ static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.ymax <= b.ymax */
        return a->ymax <= b->ymax;
@@ -417,7 +460,8 @@ static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.ymax < b.ymin */
        return a->ymax < b->ymin;
@@ -425,7 +469,8 @@ static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.ymin > b.ymax */
        return a->ymin > b->ymax;
@@ -433,7 +478,8 @@ static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
 
 static bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
 {
-       if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+       if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+               return false;
 
        /* a.ymin >= b.ymin */
        return a->ymin >= b->ymin;
@@ -955,8 +1001,12 @@ Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
        /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
        if ( result == LW_FAILURE )
        {
+               box2df_set_empty(&bbox_out);
+               gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
+                             entry_in->rel, entry_in->page, entry_in->offset, false);
+
                POSTGIS_DEBUG(4, "[GIST] empty geometry!");
-               PG_RETURN_POINTER(entry_in);
+               PG_RETURN_POINTER(entry_out);
        }
 
        POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
@@ -965,8 +1015,12 @@ Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
        if ( ! isfinite(bbox_out.xmax) || ! isfinite(bbox_out.xmin) ||
             ! isfinite(bbox_out.ymax) || ! isfinite(bbox_out.ymin) )
        {
+               box2df_set_finite(&bbox_out);
+               gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
+                             entry_in->rel, entry_in->page, entry_in->offset, false);
+
                POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
-               PG_RETURN_POINTER(entry_in);
+               PG_RETURN_POINTER(entry_out);
        }
 
        /* Enure bounding box has minimums below maximums. */
index 3f988f5e18cd9cb89a48fac5b0fc62956cd4ff21..039854d9af3bdaeae177b37882cfa6c77032e7b3 100644 (file)
@@ -154,7 +154,7 @@ 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) */
-static 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 */
@@ -168,6 +168,11 @@ static inline void gidx_set_unknown(GIDX *a)
        SET_VARSIZE(a, VARHDRSZ);
 }
 
+static inline void gidx_set_finite(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)
index d237b55c3ac72e55ebd632de5abe7eaee7badd8d..93c549a838c936293466720347420b13204672ba 100644 (file)
@@ -187,18 +187,29 @@ round(st_ymin(e.e)::numeric, 2), round(st_ymax(e.e)::numeric, 2) from e;
 
 drop table p cascade;
 
-
--- create table test (id serial primary key, geom1 geometry, geom2 geometry);
--- create index test_x1 on test using gist (geom1);
--- create index test_x2 on test using gist (geom2);
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
--- insert into test (geom1, geom2) select NULL, NULL;
--- insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
--- insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
--- insert into test (geom1, geom2) select st_makepoint(s, s), st_makepoint(2*s, 2*s) from generate_series(-100,100) s;
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
+--
+-- Index assisted extent generation
+--
+create table test (id serial primary key, geom1 geometry, geom2 geometry);
+create index test_x1 on test using gist (geom1);
+create index test_x2 on test using gist (geom2);
+select '1.a null', _postgis_index_extent('test', 'geom1');
+select '1.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select NULL, NULL;
+insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
+select '2.a null', _postgis_index_extent('test', 'geom1');
+select '2.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY' from generate_series(0,1024);
+select '3.a null', _postgis_index_extent('test', 'geom1');
+select '3.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select st_makepoint(s, s), st_makepoint(2*s, 2*s) from generate_series(-100,100) s;
+select '4.a box',_postgis_index_extent('test', 'geom1');
+select '4.b box',_postgis_index_extent('test', 'geom2');
+delete from test;
+select '5.a bad-box',_postgis_index_extent('test', 'geom1');
+select '5.b bad-box',_postgis_index_extent('test', 'geom2');
+vacuum full test;
+select '6.a null', _postgis_index_extent('test', 'geom1');
+select '6.b null', _postgis_index_extent('test', 'geom2');
+drop table test cascade;
 
index f51bedb61e22971ad56e98577cf5fb18daa0be9f..5eb2f15166eea0104da731453490ad562f645101 100644 (file)
@@ -39,3 +39,15 @@ WARNING:  stats for "p.g" do not exist
 #3391.19|0.00|1.00|0.00|1.00
 #3391.20|0.00|1.00|0.00|1.00
 NOTICE:  drop cascades to 2 other objects
+1.a null|
+1.b null|
+2.a null|
+2.b null|
+3.a null|
+3.b null|
+4.a box|BOX(-100 -100,100 100)
+4.b box|BOX(-200 -200,200 200)
+5.a bad-box|BOX(-100 -100,100 100)
+5.b bad-box|BOX(-200 -200,200 200)
+6.a null|
+6.b null|