From: Regina Obe Date: Fri, 11 Nov 2016 19:42:31 +0000 (+0000) Subject: index corruption and memory leak in BRIN support X-Git-Tag: 2.3.1~12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7d5e3d7770e1809355f8fe22e009d29b45061b48;p=postgis index corruption and memory leak in BRIN support Patch from Julien Rouhaud (Dalibo) closes #3665 for PostGIS 2.3.1 git-svn-id: http://svn.osgeo.org/postgis/branches/2.3@15227 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index 11cf5cc01..9d4992d50 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ PostGIS 2.3.1 - #3644, Deadlock on interrupt - #3652, Crash on Collection(MultiCurve()) - #3656, Fix upgrade of aggregates from 2.2 or lower version + - #3665, Index corruption and memory leak in BRIN indexes + patch from Julien Rouhaud (Dalibo) - #3667, geography ST_Segmentize bug patch from Hugo Mercier (Oslandia) diff --git a/doc/using_postgis_dataman.xml b/doc/using_postgis_dataman.xml index ee91e6740..c9081630b 100644 --- a/doc/using_postgis_dataman.xml +++ b/doc/using_postgis_dataman.xml @@ -2184,7 +2184,7 @@ SELECT UPDATE_GEOMETRY_STATS([table_name], [column_name]);BRIN stands for "Block Range Index" and is a generic form of indexing that has been introduced in PostgreSQL 9.5. BRIN is a lossy kind of index, and its main usage is to provide a compromise for both read and - write performance. It's primary goal is to handle very large tables for + write performance. Its primary goal is to handle very large tables for which some of the columns have some natural correlation with their physical location within the table. In addition to GIS indexing, BRIN is used to speed up searches on various kinds of regular or irregular data @@ -2221,13 +2221,22 @@ SELECT UPDATE_GEOMETRY_STATS([table_name], [column_name]); CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geometryfield] ); - The above syntax will always build a 2D-index. To get the 3d-dimensional index supported in PostGIS 2.0+ for the geometry type, you can create one using this syntax + The above syntax will always build a 2D-index. To get a 3d-dimensional index, you can create one using this syntax CREATE INDEX [indexname] ON [tablename] USING BRIN ([geometryfield] brin_geometry_inclusion_ops_3d); - These above syntaxes will use the default number or block in a range, which is 128. To specify the number of blocks you want to summarise in a range, you can create one using this syntax - CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geometryfield] ) WITH (pages_per_range = [number]); + You can also get a 4d-dimensional index using the 4d operator class + CREATE INDEX [indexname] ON [tablename] USING BRIN ([geometryfield] brin_geometry_inclusion_ops_4d); + These above syntaxes will use the default number or block in a range, which is 128. To specify the number of blocks you want to summarise in a range, you can create one using this syntax + CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geometryfield] ) WITH (pages_per_range = [number]); + Also, keep in mind that a BRIN index will only store one index + value for a large number of rows. If your table stores geometries with + a mixed number of dimensions, it's likely that the resulting index will + have poor performance. You can avoid this drop of performance by + choosing the operator class whith the least number of dimensions of the + stored geometries + Also the "geography" datatype is supported for BRIN indexing. The - syntax for building a BRIN index on a "geometry" column is as follows: + syntax for building a BRIN index on a "geography" column is as follows: CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geographyfield] ); The above syntax will always build a 2D-index for geospatial objetcs on the spheroid. diff --git a/libpgcommon/gserialized_gist.c b/libpgcommon/gserialized_gist.c index 4a8d8c977..d379fb26a 100644 --- a/libpgcommon/gserialized_gist.c +++ b/libpgcommon/gserialized_gist.c @@ -290,12 +290,17 @@ gserialized_datum_get_gidx_p(Datum gsdatum, GIDX *gidx) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); lwgeom_free(lwgeom); + POSTGIS_FREE_IF_COPY_P(gpart, gsdatum); + POSTGIS_FREE_IF_COPY_P(g, gsdatum); return LW_FAILURE; } lwgeom_free(lwgeom); + POSTGIS_FREE_IF_COPY_P(g, gsdatum); result = gidx_from_gbox_p(gbox, gidx); } - + + POSTGIS_FREE_IF_COPY_P(gpart, gsdatum); + if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); diff --git a/libpgcommon/gserialized_gist.h b/libpgcommon/gserialized_gist.h index b6a3c49ab..292bb1e01 100644 --- a/libpgcommon/gserialized_gist.h +++ b/libpgcommon/gserialized_gist.h @@ -27,6 +27,17 @@ typedef struct #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)) \ + pfree(ptrsrc); \ + } while (0) + /********************************************************************** ** BOX2DF structure. ** @@ -95,6 +106,8 @@ GSERIALIZED* gserialized_set_gidx(GSERIALIZED *g, GIDX *gidx); /* Remove the box from a disk serialization */ GSERIALIZED* gserialized_drop_gidx(GSERIALIZED *g); +bool box2df_contains(const BOX2DF *a, const BOX2DF *b); +bool gidx_contains(GIDX *a, GIDX *b); int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df); diff --git a/postgis/brin_2d.c b/postgis/brin_2d.c index f5fc2f53c..148951db2 100644 --- a/postgis/brin_2d.c +++ b/postgis/brin_2d.c @@ -72,17 +72,20 @@ geom2d_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } + box_key = (BOX2DF *) column->bv_values[INCLUSION_UNION]; + + /* Check if the stored bouding box already contains the geometry's one */ + if (box2df_contains(box_key, &box_geom)) + PG_RETURN_BOOL(false); + /* * Otherwise, we need to enlarge the stored box2df to make it contains the * current geometry */ - box_key = (BOX2DF *) column->bv_values[INCLUSION_UNION]; - - /* enlarge box2df */ box_key->xmin = Min(box_key->xmin, box_geom.xmin); box_key->xmax = Max(box_key->xmax, box_geom.xmax); box_key->ymin = Min(box_key->ymin, box_geom.ymin); box_key->ymax = Max(box_key->ymax, box_geom.ymax); - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(true); } diff --git a/postgis/brin_nd.c b/postgis/brin_nd.c index db8a87149..ad1470d93 100644 --- a/postgis/brin_nd.c +++ b/postgis/brin_nd.c @@ -12,7 +12,7 @@ */ Datum gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum - newval, bool isnull, int dims_wanted); + newval, bool isnull, int max_dims); /* * As for the GiST case, geographies are converted into GIDX before @@ -60,13 +60,13 @@ geom4d_brin_inclusion_add_value(PG_FUNCTION_ARGS) Datum gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum newval, - bool isnull, int dims_wanted) + bool isnull, int max_dims) { char gboxmem[GIDX_MAX_SIZE]; GIDX *gidx_geom, *gidx_key; - int dims_geom, i; + int dims_geom, dims_key, i; - Assert(dims_wanted <= GIDX_MAX_DIM); + Assert(max_dims <= GIDX_MAX_DIM); /* * If the new value is null, we record that we saw it if it's the first @@ -81,6 +81,14 @@ gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum newval, PG_RETURN_BOOL(true); } + /* + * No need for further processing if the block range is already initialized + * and is marked as containing unmergeable values. + */ + if (!column->bv_allnulls && + DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) + PG_RETURN_BOOL(false); + /* create a new GIDX in stack memory, maximum dimensions */ gidx_geom = (GIDX *) gboxmem; @@ -120,28 +128,24 @@ gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum newval, if (column->bv_allnulls) { /* - * We have to make sure we store a GIDX of wanted dimension. If the - * original geometry has less dimensions, we zero them in the GIDX. If - * the original geometry has more, we ignore them. + * It's not safe to summarize geometries of different number of + * dimensions in the same range. We therefore fix the number of + * dimension for this range by storing the bounding box of the first + * geometry found as is, being careful not to store more dimension than + * defined in the opclass. */ - if (dims_geom != dims_wanted) + if (dims_geom > max_dims) { /* - * This is safe to either enlarge or diminush the varsize because - * the GIDX was created with the maximum number of dimension a GIDX - * can contain + * Diminush the varsize to only store the maximum number of + * dimensions allowed by the opclass */ - SET_VARSIZE(gidx_geom, VARHDRSZ + dims_wanted * 2 * sizeof(float)); - } - /* zero the extra dimensions if we enlarged the GIDX */ - for (i = dims_geom; i < dims_wanted; i++) - { - GIDX_SET_MIN(gidx_geom, i, 0); - GIDX_SET_MAX(gidx_geom, i, 0); + SET_VARSIZE(gidx_geom, VARHDRSZ + max_dims * 2 * sizeof(float)); + dims_geom = max_dims; } column->bv_values[INCLUSION_UNION] = datumCopy((Datum) gidx_geom, false, - GIDX_SIZE(dims_wanted)); + GIDX_SIZE(dims_geom)); column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false); column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false); column->bv_allnulls = false; @@ -149,12 +153,28 @@ gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum newval, } gidx_key = (GIDX *) column->bv_values[INCLUSION_UNION]; + dims_key = GIDX_NDIMS(gidx_key); + + /* + * Mark the datum as unmergeable if its number of dimension is not the same + * as the one stored in the key of the current range + */ + if (dims_key != dims_geom) + { + column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + /* Check if the stored bounding box already contains the geometry's one */ + if (gidx_contains(gidx_key, gidx_geom)) + PG_RETURN_BOOL(false); /* - * As we always store a GIDX of the wanted number of dimensions, we just - * need adjust min and max + * Otherwise, we need to enlarge the stored GIDX to make it contains the + * current geometry. As we store a GIDX with a fixed number of dimensions, + * we just need adjust min and max */ - for ( i = 0; i < dims_wanted; i++ ) + for ( i = 0; i < dims_key; i++ ) { /* Adjust minimums */ GIDX_SET_MIN(gidx_key, i, @@ -164,5 +184,5 @@ gidx_brin_inclusion_add_value(BrinDesc *bdesc, BrinValues *column, Datum newval, Max(GIDX_GET_MAX(gidx_key,i),GIDX_GET_MAX(gidx_geom,i))); } - PG_RETURN_BOOL(false); + PG_RETURN_BOOL(true); } diff --git a/postgis/gserialized_gist_2d.c b/postgis/gserialized_gist_2d.c index 70ba6d0dc..6c3824958 100644 --- a/postgis/gserialized_gist_2d.c +++ b/postgis/gserialized_gist_2d.c @@ -291,7 +291,7 @@ static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b) return TRUE; } -static bool box2df_contains(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 */ @@ -595,11 +595,16 @@ gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df) if (gserialized_get_gbox_p(g, &gbox) == LW_FAILURE) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); + POSTGIS_FREE_IF_COPY_P(gpart, gsdatum); + POSTGIS_FREE_IF_COPY_P(g, gsdatum); return LW_FAILURE; } + POSTGIS_FREE_IF_COPY_P(g, gsdatum); result = box2df_from_gbox_p(&gbox, box2df); } + POSTGIS_FREE_IF_COPY_P(gpart, gsdatum); + if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got box2df %s", box2df_to_string(box2df)); diff --git a/postgis/gserialized_gist_nd.c b/postgis/gserialized_gist_nd.c index 9be47be17..4fc5fa476 100644 --- a/postgis/gserialized_gist_nd.c +++ b/postgis/gserialized_gist_nd.c @@ -378,7 +378,7 @@ 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) */ -static bool gidx_contains(GIDX *a, GIDX *b) +bool gidx_contains(GIDX *a, GIDX *b) { int i, dims_a, dims_b; diff --git a/regress/regress_brin_index.sql b/regress/regress_brin_index.sql index 089523cbe..e0aaf5633 100644 --- a/regress/regress_brin_index.sql +++ b/regress/regress_brin_index.sql @@ -1,7 +1,7 @@ --- build a larger database \i regress_lots_of_points.sql ---- test some of the searching capabilities +--- Test the various BRIN opclass with dataset containing 2D geometries CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text LANGUAGE 'plpgsql' AS @@ -96,6 +96,81 @@ SELECT 'scan_idx', qnodes('select * from test where the_geom &&& ST_MakePoint(0, DROP INDEX brin_4d; +-- test adding rows and unsummarized ranges +-- + +-- 2D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1); +CREATE INDEX brin_2d on test using brin (the_geom) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(2.1 2.1, 3.1 3.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(2.1 2.1, 3.1 3.1)'::box2d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(900.1 900.1, 920.1 920.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(900.1 900.1, 920.1 920.1)'::box2d; + +SELECT 'summarize 2d', brin_summarize_new_values('brin_2d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(900.1 900.1, 920.1 920.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(900.1 900.1, 920.1 920.1)'::box2d; + +DROP INDEX brin_2d; + +-- 3D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1); +CREATE INDEX brin_3d on test using brin (the_geom brin_geometry_inclusion_ops_3d) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)'::box3d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +SELECT 'summarize 3d', brin_summarize_new_values('brin_3d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +DROP INDEX brin_3d; + +-- 4D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1); +CREATE INDEX brin_4d on test using brin (the_geom brin_geometry_inclusion_ops_4d) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)'::box3d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +SELECT 'summarize 4d', brin_summarize_new_values('brin_4d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +DROP INDEX brin_4d; + -- cleanup DROP TABLE test; DROP FUNCTION qnodes(text); diff --git a/regress/regress_brin_index_3d.sql b/regress/regress_brin_index_3d.sql index 07c50da45..4fef2a07c 100644 --- a/regress/regress_brin_index_3d.sql +++ b/regress/regress_brin_index_3d.sql @@ -1,7 +1,8 @@ --- build a larger database \i regress_lots_of_3dpoints.sql ---- test some of the searching capabilities +--- Test the various BRIN opclass with dataset containing 3D geometries, or +-- geometries of different number of dimensions CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text LANGUAGE 'plpgsql' AS @@ -114,6 +115,137 @@ SELECT 'scan_idx', qnodes('SELECT * FROM test WHERE the_geom IS NULL'); DROP INDEX brin_4d; +-- test adding rows and unsummarized ranges +-- + +-- 2D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1, 1); +CREATE INDEX brin_2d on test using brin (the_geom) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(2.1 2.1, 3.1 3.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(2.1 2.1, 3.1 3.1)'::box2d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(900.1 900.1, 920.1 920.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(900.1 900.1, 920.1 920.1)'::box2d; + +SELECT 'summarize 2d', brin_summarize_new_values('brin_2d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom && ''BOX(900.1 900.1, 920.1 920.1)''::box2d'); + select '2d', count(*) from test where the_geom && 'BOX(900.1 900.1, 920.1 920.1)'::box2d; + +DROP INDEX brin_2d; + +-- 3D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1, 1); +CREATE INDEX brin_3d on test using brin (the_geom brin_geometry_inclusion_ops_3d) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)'::box3d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +SELECT 'summarize 3d', brin_summarize_new_values('brin_3d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '3d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +DROP INDEX brin_3d; + +-- 4D +TRUNCATE TABLE test; +INSERT INTO test select 1, st_makepoint(1, 1, 1); +CREATE INDEX brin_4d on test using brin (the_geom brin_geometry_inclusion_ops_4d) WITH (pages_per_range = 1); +INSERT INTO test select i, st_makepoint(i, i, i) FROM generate_series(2, 3) i; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(2.1 2.1 2.1, 3.1 3.1 3.1)'::box3d; + +INSERT INTO test select i, st_makepoint(i, i) FROM generate_series(4, 1000) i; +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +SELECT 'summarize 4d', brin_summarize_new_values('brin_4d'); + +SELECT 'scan_idx', qnodes('select count(*) from test where the_geom &&& ''BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)''::box3d'); + select '4d', count(*) from test where the_geom &&& 'BOX3D(900.1 900.1 900.1, 920.1 920.1 920.1)'::box3d; + +DROP INDEX brin_4d; + +-- test mix of dimensions, NULL and empty geomertries +TRUNCATE TABLE test; +INSERT INTO test SELECT i, + CASE i%5 + WHEN 0 THEN ST_MakePoint(i, i) + WHEN 1 THEN ST_MakePoint(i, i, 2) + WHEN 2 THEN ST_MakePoint(i, i, 2, 3) + WHEN 3 THEN NULL + ELSE 'POINTZ EMPTY'::geometry + END + FROM generate_series(1, 5) i; + +-- seq scan +set enable_indexscan = off; +set enable_bitmapscan = off; +set enable_seqscan = on; +SELECT 'scan_seq', qnodes('select * from test where the_geom && ''BOX(1 1, 5 5)''::box2d'); + select 'mix_seq_box2d', num,ST_astext(the_geom) from test where the_geom && 'BOX(1 1, 5 5)'::box2d order by num; + select 'mix_seq_box3d', num,ST_astext(the_geom) from test where the_geom &&& 'BOX3D(1 1 0, 5 5 0)'::box3d order by num; + +-- 2D +CREATE INDEX brin_2d on test using brin (the_geom) WITH (pages_per_range = 1); +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select * from test where the_geom && ''BOX(1 1, 5 5)''::box2d'); + select 'mix_2d_box2d', num,ST_astext(the_geom) from test where the_geom && 'BOX(1 1, 5 5)'::box2d order by num; + select 'mix_2d_box3d', num,ST_astext(the_geom) from test where the_geom &&& 'BOX3D(1 1 0, 5 5 0)'::box3d order by num; + +DROP INDEX brin_2d; + +-- 3D +CREATE INDEX brin_3d on test using brin (the_geom brin_geometry_inclusion_ops_3d); +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select * from test where the_geom && ''BOX(1 1, 5 5)''::box2d'); + select 'mix_3d_box2d', num,ST_astext(the_geom) from test where the_geom && 'BOX(1 1, 5 5)'::box2d order by num; + select 'mix_3d_box3d', num,ST_astext(the_geom) from test where the_geom &&& 'BOX3D(1 1 0, 5 5 0)'::box3d order by num; + +DROP INDEX brin_3d; + +-- 4D +CREATE INDEX brin_4d on test using brin (the_geom brin_geometry_inclusion_ops_4d); +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('select * from test where the_geom && ''BOX(1 1, 5 5)''::box2d'); + select 'mix_4d_box2d', num,ST_astext(the_geom) from test where the_geom && 'BOX(1 1, 5 5)'::box2d order by num; + select 'mix_4d_box3d', num,ST_astext(the_geom) from test where the_geom &&& 'BOX3D(1 1 0, 5 5 0)'::box3d order by num; + +DROP INDEX brin_4d; + -- cleanup DROP TABLE test; DROP FUNCTION qnodes(text); diff --git a/regress/regress_brin_index_3d_expected b/regress/regress_brin_index_3d_expected index 0c05fc689..6cbaf0894 100644 --- a/regress/regress_brin_index_3d_expected +++ b/regress/regress_brin_index_3d_expected @@ -130,3 +130,44 @@ scan_idx|Bitmap Heap Scan,Bitmap Index Scan 1260|POINT Z (126 126 126) scan_idx|Bitmap Heap Scan,Bitmap Index Scan 20 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|20 +summarize 2d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|20 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|20 +summarize 3d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|20 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|20 +summarize 4d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|20 +scan_seq|Seq Scan +mix_seq_box2d|1|POINT Z (1 1 2) +mix_seq_box2d|2|POINT ZM (2 2 2 3) +mix_seq_box2d|5|POINT(5 5) +mix_seq_box3d|5|POINT(5 5) +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +mix_2d_box2d|1|POINT Z (1 1 2) +mix_2d_box2d|2|POINT ZM (2 2 2 3) +mix_2d_box2d|5|POINT(5 5) +mix_2d_box3d|5|POINT(5 5) +scan_idx|Seq Scan +mix_3d_box2d|1|POINT Z (1 1 2) +mix_3d_box2d|2|POINT ZM (2 2 2 3) +mix_3d_box2d|5|POINT(5 5) +mix_3d_box3d|5|POINT(5 5) +scan_idx|Seq Scan +mix_4d_box2d|1|POINT Z (1 1 2) +mix_4d_box2d|2|POINT ZM (2 2 2 3) +mix_4d_box2d|5|POINT(5 5) +mix_4d_box3d|5|POINT(5 5) diff --git a/regress/regress_brin_index_expected b/regress/regress_brin_index_expected index 009a0ae7d..7322b2930 100644 --- a/regress/regress_brin_index_expected +++ b/regress/regress_brin_index_expected @@ -38,3 +38,24 @@ scan_idx|Bitmap Heap Scan,Bitmap Index Scan 2594|POINT(130.504303 126.53112) 3618|POINT(130.447205 131.655289) 7245|POINT(128.10466 130.94133) +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|20 +summarize 2d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +2d|20 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|20 +summarize 3d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +3d|20 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|1 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|20 +summarize 4d|8 +scan_idx|Bitmap Heap Scan,Bitmap Index Scan +4d|20