From 6ebf5c2a9fb761a6657c6ebc6ababfbf088e87e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Broccolo Date: Mon, 15 Jan 2018 11:00:18 +0100 Subject: [PATCH 1/2] Add BRIN support for spoint and sbox Adjust style using pgindent, execute regression test for BRIN code just for PG>9.5 --- Makefile | 6 +- README.pg_sphere | 1 + brin.c | 348 +++++++++++++++++++++++++++++++++ brin.h | 40 ++++ doc/indices.sgm | 45 ++++- expected/spoint_brin.out | 56 ++++++ pgs_brin.sql.in | 411 +++++++++++++++++++++++++++++++++++++++ sql/spoint_brin.sql | 136 +++++++++++++ 8 files changed, 1036 insertions(+), 7 deletions(-) create mode 100644 brin.c create mode 100644 brin.h create mode 100644 expected/spoint_brin.out create mode 100644 pgs_brin.sql.in create mode 100644 sql/spoint_brin.sql diff --git a/Makefile b/Makefile index 062aab6..bb5074f 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ MODULE_big = pg_sphere OBJS = src/sscan.o src/sparse.o src/sbuffer.o src/vector3d.o src/point.o \ src/euler.o src/circle.o src/line.o src/ellipse.o src/polygon.o \ src/path.o src/box.o src/output.o src/gq_cache.o src/gist.o \ - src/key.o src/gnomo.o src/epochprop.o + src/key.o src/gnomo.o src/epochprop.o brin.o ifneq ($(USE_HEALPIX),0) OBJS += src/healpix.o src/moc.o src/process_moc.o \ @@ -33,7 +33,7 @@ DATA_built = $(RELEASE_SQL) \ DOCS = README.pg_sphere COPYRIGHT.pg_sphere REGRESS = init tables points euler circle line ellipse poly path box index \ contains_ops contains_ops_compat bounding_box_gist gnomo epochprop \ - contains overlaps + contains overlaps spoint_brin ifneq ($(USE_HEALPIX),0) REGRESS += healpix moc mocautocast @@ -66,7 +66,7 @@ CRUSH_TESTS = init_extended circle_extended PGS_SQL = pgs_types.sql pgs_point.sql pgs_euler.sql pgs_circle.sql \ pgs_line.sql pgs_ellipse.sql pgs_polygon.sql pgs_path.sql \ pgs_box.sql pgs_contains_ops.sql pgs_contains_ops_compat.sql \ - pgs_gist.sql gnomo.sql + pgs_gist.sql gnomo.sql pgs_brin.sql ifneq ($(USE_HEALPIX),0) PGS_SQL += healpix.sql diff --git a/README.pg_sphere b/README.pg_sphere index 8a85c39..cccc8ca 100644 --- a/README.pg_sphere +++ b/README.pg_sphere @@ -11,6 +11,7 @@ It provides: This is an R-Tree implementation using GiST for spherical objects like spherical points and spherical circles with useful functions and operators. +It also support the Block Range INdexing (BRIN) for large datasets. NOTICE: This version will work only with PostgreSQL version 10 and above. diff --git a/brin.c b/brin.c new file mode 100644 index 0000000..5a5ae03 --- /dev/null +++ b/brin.c @@ -0,0 +1,348 @@ +/* + * BRIN SUPPORT for spheric objects: + * + * The stored type is the spherekey, as for GiST support, + * so include some already defined stuffs. We have to define + * then all the cross-type functions needed by the OpFamilies. + */ + +#include "brin.h" +#include "gist.h" +#include + +/* + * Functions needed to build a BRIN index + */ +PG_FUNCTION_INFO_V1(spoint_brin_inclusion_add_value); +PG_FUNCTION_INFO_V1(sbox_brin_inclusion_add_value); + +/* + * Functions needed to define supported operators + */ +PG_FUNCTION_INFO_V1(spoint_overlaps_spherekey); +PG_FUNCTION_INFO_V1(spoint_contains_spherekey); +PG_FUNCTION_INFO_V1(spoint_iscontained_spherekey); +PG_FUNCTION_INFO_V1(sbox_overlaps_spherekey); +PG_FUNCTION_INFO_V1(sbox_contains_spherekey); +PG_FUNCTION_INFO_V1(sbox_iscontained_spherekey); +PG_FUNCTION_INFO_V1(spherekey_overlaps_spherekey); +PG_FUNCTION_INFO_V1(spherekey_contains_spherekey); +PG_FUNCTION_INFO_V1(spherekey_iscontained_spherekey); +PG_FUNCTION_INFO_V1(spoint_overlaps_sbox); +PG_FUNCTION_INFO_V1(sbox_iscontained_spoint); + +Datum +spoint_brin_inclusion_add_value(PG_FUNCTION_ARGS) +{ + BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); + SPoint *newval = (SPoint *) DatumGetPointer(PG_GETARG_DATUM(2)); + bool isnull = PG_GETARG_BOOL(3); + int32 spointkey[6]; + int32 *skey = (int32 *) column->bv_values[INCLUSION_UNION]; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + spherepoint_gen_key(&spointkey, newval); + + /* + * If spherekey pointer is NULL, we consider the spoint entry as 'empty'. + * + * The OpClass support empty entries: we need to set the "contains empty" + * flag in the element (unless already set). + */ + if (spointkey == NULL) + { + if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) + { + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); + } + + /* if the recorded value is null, we just need to store the spherekey */ + if (column->bv_allnulls) + { + column->bv_values[INCLUSION_UNION] = datumCopy((Datum) spointkey, false, + (sizeof(int32) * 6)); + column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false); + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false); + column->bv_allnulls = false; + PG_RETURN_BOOL(true); + } + + /* + * Check if the stored spherekey already contains the key of the new value + */ + if (spherekey_interleave(skey, spointkey) == SCKEY_IN) + { + PG_RETURN_BOOL(false); + } + + /* + * Otherwise, we need to enlarge it to contains the current spoint + */ + spherekey_union_two(skey, spointkey); + + PG_RETURN_BOOL(true); +} + +Datum +sbox_brin_inclusion_add_value(PG_FUNCTION_ARGS) +{ + BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); + SBOX *newval = (SBOX *) DatumGetPointer(PG_GETARG_DATUM(2)); + bool isnull = PG_GETARG_BOOL(3); + int32 sboxkey[6]; + int32 *skey = (int32 *) column->bv_values[INCLUSION_UNION]; + + /* + * If the new value is null, we record that we saw it if it's the first + * one; otherwise, there's nothing to do. + */ + if (isnull) + { + if (column->bv_hasnulls) + PG_RETURN_BOOL(false); + + column->bv_hasnulls = true; + PG_RETURN_BOOL(true); + } + + spherebox_gen_key(&sboxkey, newval); + + /* + * If spherekey pointer is NULL, we consider the spoint entry as 'empty'. + * + * The OpClass support empty entries: we need to set the "contains empty" + * flag in the element (unless already set). + */ + if (sboxkey == NULL) + { + if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) + { + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); + } + + /* if the recorded value is null, we just need to store the spherekey */ + if (column->bv_allnulls) + { + column->bv_values[INCLUSION_UNION] = datumCopy((Datum) sboxkey, false, + (sizeof(int32) * 6)); + column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false); + column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false); + column->bv_allnulls = false; + PG_RETURN_BOOL(true); + } + + /* + * Check if the stored spherekey already contains the key of the new value + */ + if (spherekey_interleave(skey, sboxkey) == SCKEY_IN) + { + PG_RETURN_BOOL(false); + } + + /* + * Otherwise, we need to enlarge it to contains the current spoint + */ + spherekey_union_two(skey, sboxkey); + + PG_RETURN_BOOL(true); +} + +/* */ +/* Define operators procedures below */ +/* */ + +Datum +spoint_overlaps_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherepoint_gen_key(&k1, p1); + if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spoint_contains_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherepoint_gen_key(&k1, p1); + if (spherekey_interleave(k1, k2) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spoint_iscontained_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherepoint_gen_key(&k1, p1); + if (spherekey_interleave(k2, k1) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +sbox_overlaps_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherebox_gen_key(&k1, p1); + if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +sbox_contains_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherebox_gen_key(&k1, p1); + if (spherekey_interleave(k1, k2) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +sbox_iscontained_spherekey(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + spherebox_gen_key(&k1, p1); + if (spherekey_interleave(k2, k1) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spherekey_overlaps_spherekey(PG_FUNCTION_ARGS) +{ + int32 *k1 = (int32 *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spherekey_contains_spherekey(PG_FUNCTION_ARGS) +{ + int32 *k1 = (int32 *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + if (spherekey_interleave(k1, k2) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spherekey_iscontained_spherekey(PG_FUNCTION_ARGS) +{ + int32 *k1 = (int32 *) PG_GETARG_POINTER(0); + int32 *k2 = (int32 *) PG_GETARG_POINTER(1); + + if (spherekey_interleave(k2, k1) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +spoint_overlaps_sbox(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); + int32 k2[6]; + SBOX *p2 = (SBOX *) PG_GETARG_POINTER(1); + + spherepoint_gen_key(&k1, p1); + spherebox_gen_key(&k2, p2); + + if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} + +Datum +sbox_iscontained_spoint(PG_FUNCTION_ARGS) +{ + int32 k1[6]; + SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); + int32 k2[6]; + SPoint *p2 = (SPoint *) PG_GETARG_POINTER(1); + + spherebox_gen_key(&k1, p1); + spherepoint_gen_key(&k2, p2); + + if (spherekey_interleave(k1, k2) == SCKEY_IN) + { + PG_RETURN_BOOL(true); + } + + PG_RETURN_BOOL(false); +} diff --git a/brin.h b/brin.h new file mode 100644 index 0000000..b1a1c2c --- /dev/null +++ b/brin.h @@ -0,0 +1,40 @@ +#ifndef __PGS_BRIN_H__ +#define __PGS_BRIN_H__ + +/* + * BRIN declarations + */ + +#include "postgres.h" +#include "fmgr.h" + +#include "key.h" + +#include +#include +#include +#include +#include "access/brin_tuple.h" +#include "utils/datum.h" + +#define INCLUSION_UNION 0 +#define INCLUSION_UNMERGEABLE 1 +#define INCLUSION_CONTAINS_EMPTY 2 + +Datum spoint_brin_inclusion_add_value(PG_FUNCTION_ARGS); +Datum sbox_brin_inclusion_add_value(PG_FUNCTION_ARGS); + +Datum spoint_overlaps_spherekey(PG_FUNCTION_ARGS); +Datum spoint_contains_spherekey(PG_FUNCTION_ARGS); +Datum spoint_iscontained_spherekey(PG_FUNCTION_ARGS); +Datum sbox_overlaps_spherekey(PG_FUNCTION_ARGS); +Datum sbox_contains_spherekey(PG_FUNCTION_ARGS); +Datum sbox_iscontained_spherekey(PG_FUNCTION_ARGS); +Datum spherekey_overlaps_spherekey(PG_FUNCTION_ARGS); +Datum spherekey_contains_spherekey(PG_FUNCTION_ARGS); +Datum spherekey_iscontained_spherekey(PG_FUNCTION_ARGS); + +Datum spoint_overlaps_sbox(PG_FUNCTION_ARGS); +Datum sbox_iscontained_spoint(PG_FUNCTION_ARGS); + +#endif diff --git a/doc/indices.sgm b/doc/indices.sgm index 8103072..7918ee0 100644 --- a/doc/indices.sgm +++ b/doc/indices.sgm @@ -8,15 +8,32 @@ pgSphere uses GiST - to create spherical indices. An index speeds up the execution - time of operators BRIN) algorithms to create + spherical indices. + GiST index represents the R-Tree implementation for + spherical objects, while BRINs are based on "summarization" + of data blocks (pages) on physical storage in order to + organize data searches on ranges of summarized data that can be easily skipped + on the base of search filters (see + PostgreSQL documentation for further details on BRINs). + As a consequence, BRINs result to be really small indexes (up to 1000 times + than GiST ones), generally with lower a performance compared with a GiST one, + but up to 100 times faster than a full sequential scan of a table performed + without any index. So BRINs are particularly suitable in a big data context. + An index speeds up the execution time of searches based on operators <@, @, &&, #, =, and !=. You can create - an index with the following spherical data types: + linkend="op.equal">!=. + + + Create a spherical index + + + You can create a GiST index with the following spherical data types: @@ -55,6 +72,10 @@ + + BRIN indexing supports just spherical points (spoint) + and spherical coordinates range (sbox) at the moment. + Simple index of spherical points @@ -65,6 +86,22 @@ + + BRINs can be created through the following syntax: + + + + + + By default, BRINs summarize block of 128 pages. The lower numbers + of pages are specified, the higher granularity is reached during + the searches, and performance's gap between GiST indexes and BRINs + is lower (consider that BRINs size increases as well). Different + summarizations can be used with the following command: + + + + diff --git a/expected/spoint_brin.out b/expected/spoint_brin.out new file mode 100644 index 0000000..4bb88b0 --- /dev/null +++ b/expected/spoint_brin.out @@ -0,0 +1,56 @@ +CREATE TABLE +COPY 77 +CREATE FUNCTION +CREATE INDEX +SET +SET +SET + ?column? | qnodes +----------+---------- + scan_seq | Seq Scan +(1 row) + + p +----------------------------------------- + (0.349065850398866 , 0.174532925199433) +(1 row) + + ?column? | qnodes +----------+---------- + scan_seq | Seq Scan +(1 row) + + p +----------------------------------------- + (0.349065850398866 , 0.174532925199433) +(1 row) + +SET +SET +SET + ?column? | qnodes +----------+------------------------------------ + scan_idx | Bitmap Heap Scan,Bitmap Index Scan +(1 row) + + p +----------------------------------------- + (0.349065850398866 , 0.174532925199433) +(1 row) + + ?column? | qnodes +----------+------------------------------------ + scan_idx | Bitmap Heap Scan,Bitmap Index Scan +(1 row) + + p +----------------------------------------- + (0.349065850398866 , 0.174532925199433) +(1 row) + +DROP INDEX +DROP TABLE +DROP FUNCTION +SET +SET +SET diff --git a/pgs_brin.sql.in b/pgs_brin.sql.in new file mode 100644 index 0000000..93fe8dd --- /dev/null +++ b/pgs_brin.sql.in @@ -0,0 +1,411 @@ + +-------------------------------------------------------------------- +-- BRIN support -- +-------------------------------------------------------------------- + +-------------------------------- +-- the Operators -- +-------------------------------- + +CREATE FUNCTION spoint_overlaps_spherekey(spoint, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spoint_overlaps_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION spoint_contains_spherekey(spoint, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spoint_contains_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION spoint_iscontained_spherekey(spoint, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spoint_iscontained_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION sbox_overlaps_spherekey(sbox, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'sbox_overlaps_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION sbox_contains_spherekey(sbox, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'sbox_contains_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION sbox_iscontained_spherekey(sbox, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'sbox_iscontained_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_overlaps_spherekey(spherekey, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spherekey_overlaps_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_contains_spherekey(spherekey, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spherekey_contains_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_iscontained_spherekey(spherekey, spherekey) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spoint_iscontained_spherekey' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE OPERATOR && ( + LEFTARG = spoint, + RIGHTARG = spherekey, + PROCEDURE = spoint_overlaps_spherekey, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (spoint, spherekey) IS + 'true, if the spherical point overlaps a spherekey'; + +CREATE OPERATOR @> ( + LEFTARG = spoint, + RIGHTARG = spherekey, + PROCEDURE = spoint_contains_spherekey, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR @> (spoint, spherekey) IS + 'true, if the spherical point contains a spherekey - just needed to define the OpFamily'; + +CREATE OPERATOR <@ ( + LEFTARG = spoint, + RIGHTARG = spherekey, + PROCEDURE = spoint_iscontained_spherekey, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR <@ (spoint, spherekey) IS + 'true, if the spherical point is contained in a spherekey'; + +CREATE OPERATOR && ( + LEFTARG = sbox, + RIGHTARG = spherekey, + PROCEDURE = sbox_overlaps_spherekey, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (sbox, spherekey) IS + 'true, if the spherical box overlaps a spherekey'; + +CREATE OPERATOR @> ( + LEFTARG = sbox, + RIGHTARG = spherekey, + PROCEDURE = sbox_contains_spherekey, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR @> (sbox, spherekey) IS + 'true, if the spherical box contains a spherekey'; + +CREATE OPERATOR <@ ( + LEFTARG = sbox, + RIGHTARG = spherekey, + PROCEDURE = sbox_iscontained_spherekey, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR <@ (sbox, spherekey) IS + 'true, if the spherical box is contained in a spherekey'; + +CREATE OPERATOR && ( + LEFTARG = spherekey, + RIGHTARG = spherekey, + PROCEDURE = spherekey_overlaps_spherekey, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (spherekey, spherekey) IS + 'true, if the spherekey overlaps another spherekey'; + +CREATE OPERATOR @> ( + LEFTARG = spherekey, + RIGHTARG = spherekey, + PROCEDURE = spherekey_contains_spherekey, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR @> (spoint, spherekey) IS + 'true, if the spherekey contains another spherekey'; + +CREATE OPERATOR <@ ( + LEFTARG = spherekey, + RIGHTARG = spherekey, + PROCEDURE = spherekey_iscontained_spherekey, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR <@ (spherekey, spherekey) IS + 'true, if the spherical point is contained in another spherekey'; + +--------------------------------------------- +-- create operators with crossed datatypes -- +--------------------------------------------- + +CREATE FUNCTION spherekey_overlaps_spoint(spherekey, spoint) + RETURNS boolean + AS $$ + SELECT $2 && $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_contains_spoint(spherekey, spoint) + RETURNS boolean + AS $$ + SELECT $2 <@ $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_iscontained_spoint(spherekey, spoint) + RETURNS boolean + AS $$ + SELECT $2 @> $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_overlaps_sbox(spherekey, sbox) + RETURNS boolean + AS $$ + SELECT $2 && $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_contains_sbox(spherekey, sbox) + RETURNS boolean + AS $$ + SELECT $2 <@ $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE FUNCTION spherekey_iscontained_sbox(spherekey, sbox) + RETURNS boolean + AS $$ + SELECT $2 @> $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE OPERATOR && ( + LEFTARG = spherekey, + RIGHTARG = spoint, + PROCEDURE = spherekey_overlaps_spoint, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (spherekey, spoint) IS + 'true, if the spherekey overlaps a spoint'; + +CREATE OPERATOR @> ( + LEFTARG = spherekey, + RIGHTARG = spoint, + PROCEDURE = spherekey_contains_spoint, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR @> (spherekey, spoint) IS + 'true, if the spherekey contains a spherical point'; + +CREATE OPERATOR <@ ( + LEFTARG = spherekey, + RIGHTARG = spoint, + PROCEDURE = spherekey_iscontained_spoint, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR <@ (spherekey, spoint) IS + 'true, if the spherekey is contained in a spherical point - just needed to define the OpFamily'; + +CREATE OPERATOR && ( + LEFTARG = spherekey, + RIGHTARG = sbox, + PROCEDURE = spherekey_overlaps_sbox, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (spherekey, sbox) IS + 'true, if the spherekey overlaps a spherical point'; + +CREATE OPERATOR @> ( + LEFTARG = spherekey, + RIGHTARG = sbox, + PROCEDURE = spherekey_contains_sbox, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR @> (spherekey, sbox) IS + 'true, if the spherekey contains a spherical point'; + +CREATE OPERATOR <@ ( + LEFTARG = spherekey, + RIGHTARG = sbox, + PROCEDURE = spherekey_iscontained_sbox, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR <@ (spherekey, sbox) IS + 'true, if the spherekey is contained in a spherical point'; + +------------------------------------------------- +-- create operators that will actually be used -- +------------------------------------------------- + +CREATE FUNCTION spoint_overlaps_sbox(spoint, sbox) + RETURNS boolean + AS 'MODULE_PATHNAME', 'spoint_overlaps_sbox' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE OPERATOR && ( + LEFTARG = spoint, + RIGHTARG = sbox, + PROCEDURE = spoint_overlaps_sbox, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (spoint, sbox) IS + 'true, if the spherical point overlaps a spherical box'; + +------------------------------------------------------------ +-- Complementar operators, needed for OpFamily definition -- +------------------------------------------------------------ + +CREATE FUNCTION sbox_overlaps_spoint(sbox, spoint) + RETURNS boolean + AS $$ + SELECT $2 && $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE OPERATOR && ( + LEFTARG = sbox, + RIGHTARG = spoint, + PROCEDURE = sbox_overlaps_spoint, + COMMUTATOR = '&&', + RESTRICT = contsel, + JOIN = contjoinsel +); + +COMMENT ON OPERATOR && (sbox, spoint) IS + 'true, if the spherical box overlaps a spherical point'; + +CREATE FUNCTION sbox_iscontained_spoint(sbox, spoint) + RETURNS boolean + AS 'MODULE_PATHNAME', 'sbox_iscontained_spoint' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE OPERATOR <@ ( + LEFTARG = sbox, + RIGHTARG = spoint, + PROCEDURE = sbox_iscontained_spoint, + COMMUTATOR = '@>', + RESTRICT = contsel, + JOIN = contjoinsel +); + +CREATE FUNCTION spoint_contains_sbox(spoint, sbox) + RETURNS boolean + AS $$ + SELECT $2 <@ $1; + $$ LANGUAGE SQL IMMUTABLE STRICT; + +CREATE OPERATOR @> ( + LEFTARG = spoint, + RIGHTARG = sbox, + PROCEDURE = spoint_contains_sbox, + COMMUTATOR = '<@', + RESTRICT = contsel, + JOIN = contjoinsel +); + +-------------------------------- +-- the OpFamily -- +-------------------------------- + +CREATE OPERATOR FAMILY brin_inclusion_spheric_ops USING brin; + +CREATE OR REPLACE FUNCTION spoint_brin_inclusion_add_value(internal, internal, internal, internal) + RETURNS boolean + AS 'MODULE_PATHNAME','spoint_brin_inclusion_add_value' + LANGUAGE 'c'; + +CREATE OR REPLACE FUNCTION sbox_brin_inclusion_add_value(internal, internal, internal, internal) + RETURNS boolean + AS 'MODULE_PATHNAME','sbox_brin_inclusion_add_value' + LANGUAGE 'c'; + +CREATE OPERATOR CLASS brin_spoint_inclusion_ops + DEFAULT FOR TYPE spoint + USING brin + FAMILY brin_inclusion_spheric_ops AS + FUNCTION 1 brin_inclusion_opcinfo(internal) , + FUNCTION 2 spoint_brin_inclusion_add_value(internal, internal, internal, internal) , + FUNCTION 3 brin_inclusion_consistent(internal, internal, internal) , + FUNCTION 4 brin_inclusion_union(internal, internal, internal) , + STORAGE spherekey; + +CREATE OPERATOR CLASS brin_sbox_inclusion_ops + DEFAULT FOR TYPE sbox + USING brin + FAMILY brin_inclusion_spheric_ops AS + FUNCTION 1 brin_inclusion_opcinfo(internal) , + FUNCTION 2 sbox_brin_inclusion_add_value(internal, internal, internal, internal) , + FUNCTION 3 brin_inclusion_consistent(internal, internal, internal) , + FUNCTION 4 brin_inclusion_union(internal, internal, internal) , + STORAGE spherekey; + +ALTER OPERATOR FAMILY brin_inclusion_spheric_ops USING brin ADD + OPERATOR 3 &&(spherekey, spoint), + OPERATOR 7 @>(spherekey, spoint), + OPERATOR 8 <@(spherekey, spoint), + + OPERATOR 3 &&(spoint, spherekey), + OPERATOR 7 @>(spoint, spherekey), + OPERATOR 8 <@(spoint, spherekey), + + OPERATOR 3 &&(spherekey, sbox), + OPERATOR 7 @>(spherekey, sbox), + OPERATOR 8 <@(spherekey, sbox), + + OPERATOR 3 &&(sbox, spherekey), + OPERATOR 7 @>(sbox, spherekey), + OPERATOR 8 <@(sbox, spherekey), + + OPERATOR 3 &&(spherekey, spherekey), + OPERATOR 7 @>(spherekey, spherekey), + OPERATOR 8 <@(spherekey, spherekey), + + OPERATOR 3 &&(spoint, sbox), + OPERATOR 3 &&(sbox, spoint), + OPERATOR 7 @>(sbox, spoint), + OPERATOR 7 @>(spoint, sbox), + OPERATOR 8 <@(sbox, spoint), + OPERATOR 8 <@(spoint, sbox); + + diff --git a/sql/spoint_brin.sql b/sql/spoint_brin.sql new file mode 100644 index 0000000..b03508d --- /dev/null +++ b/sql/spoint_brin.sql @@ -0,0 +1,136 @@ +CREATE TABLE test_points ( + p spoint +); + +COPY test_points (p) FROM stdin; +(0.349065850398866 , 0.174532925199433) +(1.59875999207035 , 0.771416330759722) +(1.59876348272885 , 0.77141458543047) +(1.59876697338736 , 0.771412840101218) +(1.59877046404586 , 0.771411094771966) +(1.59877395470437 , 0.771409349442714) +(1.59877744536287 , 0.771407604113461) +(1.59878093602137 , 0.77140585878421) +(1.59878442667988 , 0.771404113454958) +(1.59878791733838 , 0.771402368125706) +(1.59879140799689 , 0.771400622796454) +(1.59879489865539 , 0.771398877467202) +(1.59879838931389 , 0.77139713213795) +(1.5988018799724 , 0.771395386808698) +(1.5988053706309 , 0.771393641479446) +(1.59880886128941 , 0.771391896150194) +(1.59881235194791 , 0.771390150820941) +(1.59881584260641 , 0.77138840549169) +(1.59881933326492 , 0.771386660162438) +(1.59882282392342 , 0.771384914833186) +(1.59882631458193 , 0.771383169503934) +(1.59882980524043 , 0.771381424174682) +(1.59883329589893 , 0.77137967884543) +(1.59883678655744 , 0.771377933516178) +(1.59884027721594 , 0.771376188186926) +(1.59884376787445 , 0.771374442857673) +(1.59884725853295 , 0.771372697528422) +(1.59885074919145 , 0.77137095219917) +(1.59885423984996 , 0.771369206869918) +(1.59885773050846 , 0.771367461540666) +(1.59886122116697 , 0.771365716211414) +(1.59886471182547 , 0.771363970882162) +(1.59886820248397 , 0.77136222555291) +(1.59887169314248 , 0.771360480223658) +(1.59887518380098 , 0.771358734894406) +(1.59887867445948 , 0.771356989565154) +(1.59888216511799 , 0.771355244235902) +(1.59888565577649 , 0.77135349890665) +(1.598889146435 , 0.771351753577398) +(1.5988926370935 , 0.771350008248146) +(1.59889612775201 , 0.771348262918894) +(1.59889961841051 , 0.771346517589642) +(1.59890310906901 , 0.77134477226039) +(1.59890659972752 , 0.771343026931138) +(1.59891009038602 , 0.771341281601886) +(1.59891358104452 , 0.771339536272634) +(1.59891707170303 , 0.771337790943382) +(1.59892056236153 , 0.77133604561413) +(1.59892405302004 , 0.771334300284878) +(1.59892754367854 , 0.771332554955626) +(1.59893103433705 , 0.771330809626374) +(1.59893452499555 , 0.771329064297122) +(1.59893801565405 , 0.77132731896787) +(1.59894150631256 , 0.771325573638618) +(1.59894499697106 , 0.771323828309366) +(1.59894848762957 , 0.771322082980114) +(1.59895197828807 , 0.771320337650862) +(1.59895546894657 , 0.77131859232161) +(3.61121537674092 , -1.33598496521933) +(3.61121886739942 , -1.33598321989008) +(3.61122235805792 , -1.33598147456083) +(3.61122584871643 , -1.33597972923158) +(3.61122933937493 , -1.33597798390233) +(3.61123283003344 , -1.33597623857307) +(3.61123632069194 , -1.33597449324382) +(3.61123981135044 , -1.33597274791457) +(3.61124330200895 , -1.33597100258532) +(3.61124679266745 , -1.33596925725607) +(3.61125028332595 , -1.33596751192682) +(3.61125377398446 , -1.33596576659756) +(3.61125726464296 , -1.33596402126831) +(3.61126075530147 , -1.33596227593906) +(3.61126424595997 , -1.33596053060981) +(3.61126773661847 , -1.33595878528056) +(3.61127122727698 , -1.3359570399513) +(3.61127471793548 , -1.33595529462205) +(3.61127820859399 , -1.3359535492928) +\. + +CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text +LANGUAGE 'plpgsql' AS +$$ +DECLARE + exp TEXT; + mat TEXT[]; + ret TEXT[]; +BEGIN + FOR exp IN EXECUTE 'EXPLAIN ' || q + LOOP + --RAISE NOTICE 'EXP: %', exp; + mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan)'); + --RAISE NOTICE 'MAT: %', mat; + IF mat IS NOT NULL THEN + ret := array_append(ret, mat[1]); + END IF; + --RAISE NOTICE 'RET: %', ret; + END LOOP; + RETURN array_to_string(ret,','); +END; +$$; + +CREATE INDEX brin_spoint ON test_points USING brin (p) WITH (pages_per_range = 16); + +set enable_indexscan = off; +set enable_bitmapscan = off; +set enable_seqscan = on; + +SELECT 'scan_seq', qnodes('SELECT * FROM test_points WHERE p <@ sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_points WHERE p <@ sbox '( (10d,10d), (20d,20d) )'; + +SELECT 'scan_seq', qnodes('SELECT * FROM test_points WHERE p && sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_points WHERE p && sbox '( (10d,10d), (20d,20d) )'; + +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; + +SELECT 'scan_idx', qnodes('SELECT * FROM test_points WHERE p <@ sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_points WHERE p <@ sbox '( (10d,10d), (20d,20d) )'; + +SELECT 'scan_idx', qnodes('SELECT * FROM test_points WHERE p && sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_points WHERE p && sbox '( (10d,10d), (20d,20d) )'; + +-- cleanup +DROP INDEX brin_spoint; +DROP TABLE test_points; +DROP FUNCTION qnodes(text); + +set enable_indexscan = on; +set enable_bitmapscan = on; +set enable_seqscan = on; From fa429a54177282ae1eb94465903154f5f514756a Mon Sep 17 00:00:00 2001 From: Vitaly Davydov Date: Tue, 15 Aug 2023 18:08:28 +0300 Subject: [PATCH 2/2] Prepare the original PR of @gbroccolo for the merge Move brin.c(h) files into src subdirectory Add brin support in upgrade script Fix some code problems Fix test_init Fix Indices section in the doc Add test for sbox BRIN index Fix found problems in the doc after review Fix BRIN support for sbox --- Makefile | 8 +- README.pg_sphere | 4 +- doc/indices.sgm | 30 +++---- expected/init_test_healpix.out.in | 4 +- expected/sbox_brin.out | 91 ++++++++++++++++++++ expected/spoint_brin.out | 99 +++++++++++++-------- pgs_brin.sql.in | 6 +- sql/sbox_brin.sql | 138 ++++++++++++++++++++++++++++++ sql/spoint_brin.sql | 2 +- brin.c => src/brin.c | 28 +++--- brin.h => src/brin.h | 0 11 files changed, 337 insertions(+), 73 deletions(-) create mode 100644 expected/sbox_brin.out create mode 100644 sql/sbox_brin.sql rename brin.c => src/brin.c (95%) rename brin.h => src/brin.h (100%) diff --git a/Makefile b/Makefile index bb5074f..2589494 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ MODULE_big = pg_sphere OBJS = src/sscan.o src/sparse.o src/sbuffer.o src/vector3d.o src/point.o \ src/euler.o src/circle.o src/line.o src/ellipse.o src/polygon.o \ src/path.o src/box.o src/output.o src/gq_cache.o src/gist.o \ - src/key.o src/gnomo.o src/epochprop.o brin.o + src/key.o src/gnomo.o src/epochprop.o src/brin.o ifneq ($(USE_HEALPIX),0) OBJS += src/healpix.o src/moc.o src/process_moc.o \ @@ -33,7 +33,7 @@ DATA_built = $(RELEASE_SQL) \ DOCS = README.pg_sphere COPYRIGHT.pg_sphere REGRESS = init tables points euler circle line ellipse poly path box index \ contains_ops contains_ops_compat bounding_box_gist gnomo epochprop \ - contains overlaps spoint_brin + contains overlaps spoint_brin sbox_brin ifneq ($(USE_HEALPIX),0) REGRESS += healpix moc mocautocast @@ -43,7 +43,7 @@ REGRESS_9_5 = index_9.5 # experimental for spoint3 TESTS = init_test tables points euler circle line ellipse poly path box \ index contains_ops contains_ops_compat bounding_box_gist gnomo \ - epochprop contains overlaps + epochprop contains overlaps spoint_brin sbox_brin ifneq ($(USE_HEALPIX),0) TESTS += healpix moc mocautocast @@ -262,7 +262,7 @@ endif pg_sphere--1.2.2--1.2.3.sql: cat upgrade_scripts/$@.in > $@ -pg_sphere--1.2.3--1.3.0.sql: +pg_sphere--1.2.3--1.3.0.sql: pgs_brin.sql.in cat upgrade_scripts/$@.in > $@ # end of local stuff diff --git a/README.pg_sphere b/README.pg_sphere index cccc8ca..95a2563 100644 --- a/README.pg_sphere +++ b/README.pg_sphere @@ -9,9 +9,9 @@ It provides: * Object rotation by Euler angles * Indexing of spherical data types -This is an R-Tree implementation using GiST for spherical objects like +This is an R-tree implementation using GiST for spherical objects like spherical points and spherical circles with useful functions and operators. -It also support the Block Range INdexing (BRIN) for large datasets. +It also supports the Block Range INdexing (BRIN) for large datasets. NOTICE: This version will work only with PostgreSQL version 10 and above. diff --git a/doc/indices.sgm b/doc/indices.sgm index 7918ee0..11940c2 100644 --- a/doc/indices.sgm +++ b/doc/indices.sgm @@ -10,17 +10,17 @@ pgSphere uses GiST and Block Range INdexing (BRIN) algorithms to create spherical indices. - GiST index represents the R-Tree implementation for - spherical objects, while BRINs are based on "summarization" + GiST indexes utilize an R-tree implementation for + spherical objects, while BRIN indexes are based on the "summarization" of data blocks (pages) on physical storage in order to organize data searches on ranges of summarized data that can be easily skipped on the base of search filters (see - PostgreSQL documentation for further details on BRINs). - As a consequence, BRINs result to be really small indexes (up to 1000 times - than GiST ones), generally with lower a performance compared with a GiST one, + url="https://www.postgresql.org/docs/current/brin-intro.html"> + PostgreSQL documentation for further details on BRIN indexes). + As a consequence, BRIN indexes are very small indexes (up to 1000 times smaller + than GiST ones), generally with lower performance compared with a GiST one, but up to 100 times faster than a full sequential scan of a table performed - without any index. So BRINs are particularly suitable in a big data context. + without any index. So BRIN indexes are particularly suitable in a big data context. An index speeds up the execution time of searches based on operators <@, @, =, and !=. - - Create a spherical index - You can create a GiST index with the following spherical data types: @@ -87,17 +84,18 @@ - BRINs can be created through the following syntax: + BRIN index can be created through the following syntax: - By default, BRINs summarize block of 128 pages. The lower numbers - of pages are specified, the higher granularity is reached during - the searches, and performance's gap between GiST indexes and BRINs - is lower (consider that BRINs size increases as well). Different - summarizations can be used with the following command: + By default, BRIN indexes summarize blocks of 128 pages. The smaller the + number of pages specified, the higher the granularity in searches, + and the gap in performance between GiST indexes and BRIN indexes will be + decreased. Note that the size of the BRIN indexes increases as well. + Different summarizations can be specified with the following + command: diff --git a/expected/init_test_healpix.out.in b/expected/init_test_healpix.out.in index 4195603..b87a70d 100644 --- a/expected/init_test_healpix.out.in +++ b/expected/init_test_healpix.out.in @@ -1,2 +1,2 @@ -psql:pg_sphere.test.sql:9271: NOTICE: return type smoc is only a shell -psql:pg_sphere.test.sql:9277: NOTICE: argument type smoc is only a shell +psql:pg_sphere.test.sql:9684: NOTICE: return type smoc is only a shell +psql:pg_sphere.test.sql:9690: NOTICE: argument type smoc is only a shell diff --git a/expected/sbox_brin.out b/expected/sbox_brin.out new file mode 100644 index 0000000..43fd87c --- /dev/null +++ b/expected/sbox_brin.out @@ -0,0 +1,91 @@ +SELECT set_sphere_output_precision(8); + set_sphere_output_precision +----------------------------- + SET 8 +(1 row) + +CREATE TABLE test_boxes ( + b sbox +); +COPY test_boxes (b) FROM stdin; +CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text +LANGUAGE 'plpgsql' AS +$$ +DECLARE + exp TEXT; + mat TEXT[]; + ret TEXT[]; +BEGIN + FOR exp IN EXECUTE 'EXPLAIN ' || q + LOOP + --RAISE NOTICE 'EXP: %', exp; + mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan on (test_boxes|test_boxes_brin_idx))'); + --RAISE NOTICE 'MAT: %', mat; + IF mat IS NOT NULL THEN + ret := array_append(ret, mat[1]); + END IF; + --RAISE NOTICE 'RET: %', ret; + END LOOP; + RETURN array_to_string(ret,','); +END; +$$; +CREATE INDEX test_boxes_brin_idx ON test_boxes USING brin (b); +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; +SET enable_seqscan = ON; +SELECT 'scan_seq', qnodes('SELECT * FROM test_boxes WHERE b <@ sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------ + scan_seq | Seq Scan on test_boxes +(1 row) + +SELECT * FROM test_boxes WHERE b <@ sbox '( (10d,10d), (20d,20d) )'; + b +--- +(0 rows) + +SELECT 'scan_seq', qnodes('SELECT * FROM test_boxes WHERE b && sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------ + scan_seq | Seq Scan on test_boxes +(1 row) + +SELECT * FROM test_boxes WHERE b && sbox '( (10d,10d), (20d,20d) )'; + b +-------------------------------------------------------- + ((0.34906585 , 0.17453293), (0.35006585 , 0.17463293)) +(1 row) + +SET enable_indexscan = OFF; +SET enable_bitmapscan = ON; +SET enable_seqscan = OFF; +SELECT 'scan_idx', qnodes('SELECT * FROM test_boxes WHERE b <@ sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------------------------------------------------------- + scan_idx | Bitmap Heap Scan on test_boxes,Bitmap Index Scan on test_boxes_brin_idx +(1 row) + +SELECT * FROM test_boxes WHERE b <@ sbox '( (10d,10d), (20d,20d) )'; + b +--- +(0 rows) + +SELECT 'scan_idx', qnodes('SELECT * FROM test_boxes WHERE b && sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------------------------------------------------------- + scan_idx | Bitmap Heap Scan on test_boxes,Bitmap Index Scan on test_boxes_brin_idx +(1 row) + +SELECT * FROM test_boxes WHERE b && sbox '( (10d,10d), (20d,20d) )'; + b +-------------------------------------------------------- + ((0.34906585 , 0.17453293), (0.35006585 , 0.17463293)) +(1 row) + +---- cleanup +DROP INDEX test_boxes_brin_idx; +DROP TABLE test_boxes; +DROP FUNCTION qnodes(text); +SET enable_indexscan = ON; +SET enable_bitmapscan = ON; +SET enable_seqscan = ON; diff --git a/expected/spoint_brin.out b/expected/spoint_brin.out index 4bb88b0..958c709 100644 --- a/expected/spoint_brin.out +++ b/expected/spoint_brin.out @@ -1,56 +1,87 @@ -CREATE TABLE -COPY 77 -CREATE FUNCTION -CREATE INDEX -SET -SET -SET - ?column? | qnodes -----------+---------- - scan_seq | Seq Scan -(1 row) - - p +CREATE TABLE test_points ( + p spoint +); +COPY test_points (p) FROM stdin; +CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text +LANGUAGE 'plpgsql' AS +$$ +DECLARE + exp TEXT; + mat TEXT[]; + ret TEXT[]; +BEGIN + FOR exp IN EXECUTE 'EXPLAIN ' || q + LOOP + --RAISE NOTICE 'EXP: %', exp; + mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan on (test_points|brin_spoint))'); + --RAISE NOTICE 'MAT: %', mat; + IF mat IS NOT NULL THEN + ret := array_append(ret, mat[1]); + END IF; + --RAISE NOTICE 'RET: %', ret; + END LOOP; + RETURN array_to_string(ret,','); +END; +$$; +CREATE INDEX brin_spoint ON test_points USING brin (p) WITH (pages_per_range = 16); +set enable_indexscan = off; +set enable_bitmapscan = off; +set enable_seqscan = on; +SELECT 'scan_seq', qnodes('SELECT * FROM test_points WHERE p <@ sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------- + scan_seq | Seq Scan on test_points +(1 row) + +SELECT * FROM test_points WHERE p <@ sbox '( (10d,10d), (20d,20d) )'; + p ----------------------------------------- (0.349065850398866 , 0.174532925199433) (1 row) - ?column? | qnodes -----------+---------- - scan_seq | Seq Scan +SELECT 'scan_seq', qnodes('SELECT * FROM test_points WHERE p && sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------- + scan_seq | Seq Scan on test_points (1 row) - p +SELECT * FROM test_points WHERE p && sbox '( (10d,10d), (20d,20d) )'; + p ----------------------------------------- (0.349065850398866 , 0.174532925199433) (1 row) -SET -SET -SET - ?column? | qnodes -----------+------------------------------------ - scan_idx | Bitmap Heap Scan,Bitmap Index Scan +set enable_indexscan = off; +set enable_bitmapscan = on; +set enable_seqscan = off; +SELECT 'scan_idx', qnodes('SELECT * FROM test_points WHERE p <@ sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------------------------------------------------ + scan_idx | Bitmap Heap Scan on test_points,Bitmap Index Scan on brin_spoint (1 row) - p +SELECT * FROM test_points WHERE p <@ sbox '( (10d,10d), (20d,20d) )'; + p ----------------------------------------- (0.349065850398866 , 0.174532925199433) (1 row) - ?column? | qnodes -----------+------------------------------------ - scan_idx | Bitmap Heap Scan,Bitmap Index Scan +SELECT 'scan_idx', qnodes('SELECT * FROM test_points WHERE p && sbox ''( (10d,10d), (20d,20d) )'''); + ?column? | qnodes +----------+------------------------------------------------------------------ + scan_idx | Bitmap Heap Scan on test_points,Bitmap Index Scan on brin_spoint (1 row) - p +SELECT * FROM test_points WHERE p && sbox '( (10d,10d), (20d,20d) )'; + p ----------------------------------------- (0.349065850398866 , 0.174532925199433) (1 row) -DROP INDEX -DROP TABLE -DROP FUNCTION -SET -SET -SET +-- cleanup +DROP INDEX brin_spoint; +DROP TABLE test_points; +DROP FUNCTION qnodes(text); +set enable_indexscan = on; +set enable_bitmapscan = on; +set enable_seqscan = on; diff --git a/pgs_brin.sql.in b/pgs_brin.sql.in index 93fe8dd..4b0fba5 100644 --- a/pgs_brin.sql.in +++ b/pgs_brin.sql.in @@ -406,6 +406,8 @@ ALTER OPERATOR FAMILY brin_inclusion_spheric_ops USING brin ADD OPERATOR 7 @>(sbox, spoint), OPERATOR 7 @>(spoint, sbox), OPERATOR 8 <@(sbox, spoint), - OPERATOR 8 <@(spoint, sbox); - + OPERATOR 8 <@(spoint, sbox), + OPERATOR 3 &&(sbox, sbox), + OPERATOR 7 @>(sbox, sbox), + OPERATOR 8 <@(sbox, sbox); diff --git a/sql/sbox_brin.sql b/sql/sbox_brin.sql new file mode 100644 index 0000000..24f30d0 --- /dev/null +++ b/sql/sbox_brin.sql @@ -0,0 +1,138 @@ +SELECT set_sphere_output_precision(8); + +CREATE TABLE test_boxes ( + b sbox +); + +COPY test_boxes (b) FROM stdin; +( (0.349065850398866, 0.174532925199433), (0.350065850398866, 0.174632925199433) ) +( (1.59875999207035, 0.771416330759722), (1.5997599920703498, 0.771516330759722) ) +( (1.59876348272885, 0.77141458543047), (1.5997634827288498, 0.77151458543047) ) +( (1.59876697338736, 0.771412840101218), (1.5997669733873598, 0.771512840101218) ) +( (1.59877046404586, 0.771411094771966), (1.5997704640458599, 0.771511094771966) ) +( (1.59877395470437, 0.771409349442714), (1.59977395470437, 0.771509349442714) ) +( (1.59877744536287, 0.771407604113461), (1.59977744536287, 0.771507604113461) ) +( (1.59878093602137, 0.77140585878421), (1.59978093602137, 0.77150585878421) ) +( (1.59878442667988, 0.771404113454958), (1.59978442667988, 0.771504113454958) ) +( (1.59878791733838, 0.771402368125706), (1.59978791733838, 0.7715023681257059) ) +( (1.59879140799689, 0.771400622796454), (1.5997914079968898, 0.771500622796454) ) +( (1.59879489865539, 0.771398877467202), (1.5997948986553898, 0.771498877467202) ) +( (1.59879838931389, 0.77139713213795), (1.5997983893138898, 0.77149713213795) ) +( (1.5988018799724, 0.771395386808698), (1.5998018799723999, 0.771495386808698) ) +( (1.5988053706309, 0.771393641479446), (1.5998053706309, 0.771493641479446) ) +( (1.59880886128941, 0.771391896150194), (1.59980886128941, 0.771491896150194) ) +( (1.59881235194791, 0.771390150820941), (1.59981235194791, 0.771490150820941) ) +( (1.59881584260641, 0.77138840549169), (1.59981584260641, 0.77148840549169) ) +( (1.59881933326492, 0.771386660162438), (1.59981933326492, 0.7714866601624379) ) +( (1.59882282392342, 0.771384914833186), (1.5998228239234198, 0.771484914833186) ) +( (1.59882631458193, 0.771383169503934), (1.5998263145819298, 0.771483169503934) ) +( (1.59882980524043, 0.771381424174682), (1.5998298052404298, 0.771481424174682) ) +( (1.59883329589893, 0.77137967884543), (1.5998332958989299, 0.77147967884543) ) +( (1.59883678655744, 0.771377933516178), (1.5998367865574399, 0.771477933516178) ) +( (1.59884027721594, 0.771376188186926), (1.59984027721594, 0.771476188186926) ) +( (1.59884376787445, 0.771374442857673), (1.59984376787445, 0.771474442857673) ) +( (1.59884725853295, 0.771372697528422), (1.59984725853295, 0.771472697528422) ) +( (1.59885074919145, 0.77137095219917), (1.59985074919145, 0.77147095219917) ) +( (1.59885423984996, 0.771369206869918), (1.5998542398499598, 0.7714692068699179) ) +( (1.59885773050846, 0.771367461540666), (1.5998577305084598, 0.771467461540666) ) +( (1.59886122116697, 0.771365716211414), (1.5998612211669698, 0.771465716211414) ) +( (1.59886471182547, 0.771363970882162), (1.5998647118254699, 0.771463970882162) ) +( (1.59886820248397, 0.77136222555291), (1.5998682024839699, 0.77146222555291) ) +( (1.59887169314248, 0.771360480223658), (1.59987169314248, 0.771460480223658) ) +( (1.59887518380098, 0.771358734894406), (1.59987518380098, 0.771458734894406) ) +( (1.59887867445948, 0.771356989565154), (1.59987867445948, 0.771456989565154) ) +( (1.59888216511799, 0.771355244235902), (1.59988216511799, 0.771455244235902) ) +( (1.59888565577649, 0.77135349890665), (1.5998856557764898, 0.7714534989066499) ) +( (1.598889146435, 0.771351753577398), (1.5998891464349998, 0.771451753577398) ) +( (1.5988926370935, 0.771350008248146), (1.5998926370934998, 0.771450008248146) ) +( (1.59889612775201, 0.771348262918894), (1.5998961277520098, 0.771448262918894) ) +( (1.59889961841051, 0.771346517589642), (1.5998996184105099, 0.771446517589642) ) +( (1.59890310906901, 0.77134477226039), (1.59990310906901, 0.77144477226039) ) +( (1.59890659972752, 0.771343026931138), (1.59990659972752, 0.771443026931138) ) +( (1.59891009038602, 0.771341281601886), (1.59991009038602, 0.771441281601886) ) +( (1.59891358104452, 0.771339536272634), (1.59991358104452, 0.771439536272634) ) +( (1.59891707170303, 0.771337790943382), (1.59991707170303, 0.7714377909433819) ) +( (1.59892056236153, 0.77133604561413), (1.5999205623615298, 0.77143604561413) ) +( (1.59892405302004, 0.771334300284878), (1.5999240530200398, 0.771434300284878) ) +( (1.59892754367854, 0.771332554955626), (1.5999275436785398, 0.771432554955626) ) +( (1.59893103433705, 0.771330809626374), (1.5999310343370499, 0.771430809626374) ) +( (1.59893452499555, 0.771329064297122), (1.59993452499555, 0.771429064297122) ) +( (1.59893801565405, 0.77132731896787), (1.59993801565405, 0.77142731896787) ) +( (1.59894150631256, 0.771325573638618), (1.59994150631256, 0.771425573638618) ) +( (1.59894499697106, 0.771323828309366), (1.59994499697106, 0.771423828309366) ) +( (1.59894848762957, 0.771322082980114), (1.59994848762957, 0.7714220829801139) ) +( (1.59895197828807, 0.771320337650862), (1.5999519782880698, 0.771420337650862) ) +( (1.59895546894657, 0.77131859232161), (1.5999554689465698, 0.77141859232161) ) +( (3.61121537674092, -1.33598496521933), (3.6122153767409197, -1.33588496521933) ) +( (3.61121886739942, -1.33598321989008), (3.6122188673994198, -1.33588321989008) ) +( (3.61122235805792, -1.33598147456083), (3.61222235805792, -1.33588147456083) ) +( (3.61122584871643, -1.33597972923158), (3.61222584871643, -1.33587972923158) ) +( (3.61122933937493, -1.33597798390233), (3.61222933937493, -1.33587798390233) ) +( (3.61123283003344, -1.33597623857307), (3.61223283003344, -1.33587623857307) ) +( (3.61123632069194, -1.33597449324382), (3.61223632069194, -1.33587449324382) ) +( (3.61123981135044, -1.33597274791457), (3.61223981135044, -1.33587274791457) ) +( (3.61124330200895, -1.33597100258532), (3.6122433020089497, -1.33587100258532) ) +( (3.61124679266745, -1.33596925725607), (3.6122467926674497, -1.33586925725607) ) +( (3.61125028332595, -1.33596751192682), (3.6122502833259498, -1.33586751192682) ) +( (3.61125377398446, -1.33596576659756), (3.61225377398446, -1.33586576659756) ) +( (3.61125726464296, -1.33596402126831), (3.61225726464296, -1.33586402126831) ) +( (3.61126075530147, -1.33596227593906), (3.61226075530147, -1.33586227593906) ) +( (3.61126424595997, -1.33596053060981), (3.61226424595997, -1.33586053060981) ) +( (3.61126773661847, -1.33595878528056), (3.61226773661847, -1.33585878528056) ) +( (3.61127122727698, -1.3359570399513), (3.6122712272769797, -1.3358570399513001) ) +( (3.61127471793548, -1.33595529462205), (3.6122747179354797, -1.33585529462205) ) +( (3.61127820859399, -1.3359535492928), (3.61227820859399, -1.3358535492928) ) +\. + +CREATE OR REPLACE FUNCTION qnodes(q text) RETURNS text +LANGUAGE 'plpgsql' AS +$$ +DECLARE + exp TEXT; + mat TEXT[]; + ret TEXT[]; +BEGIN + FOR exp IN EXECUTE 'EXPLAIN ' || q + LOOP + --RAISE NOTICE 'EXP: %', exp; + mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan on (test_boxes|test_boxes_brin_idx))'); + --RAISE NOTICE 'MAT: %', mat; + IF mat IS NOT NULL THEN + ret := array_append(ret, mat[1]); + END IF; + --RAISE NOTICE 'RET: %', ret; + END LOOP; + RETURN array_to_string(ret,','); +END; +$$; + +CREATE INDEX test_boxes_brin_idx ON test_boxes USING brin (b); + +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; +SET enable_seqscan = ON; + +SELECT 'scan_seq', qnodes('SELECT * FROM test_boxes WHERE b <@ sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_boxes WHERE b <@ sbox '( (10d,10d), (20d,20d) )'; + +SELECT 'scan_seq', qnodes('SELECT * FROM test_boxes WHERE b && sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_boxes WHERE b && sbox '( (10d,10d), (20d,20d) )'; + +SET enable_indexscan = OFF; +SET enable_bitmapscan = ON; +SET enable_seqscan = OFF; + +SELECT 'scan_idx', qnodes('SELECT * FROM test_boxes WHERE b <@ sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_boxes WHERE b <@ sbox '( (10d,10d), (20d,20d) )'; + +SELECT 'scan_idx', qnodes('SELECT * FROM test_boxes WHERE b && sbox ''( (10d,10d), (20d,20d) )'''); +SELECT * FROM test_boxes WHERE b && sbox '( (10d,10d), (20d,20d) )'; + +---- cleanup +DROP INDEX test_boxes_brin_idx; +DROP TABLE test_boxes; +DROP FUNCTION qnodes(text); + +SET enable_indexscan = ON; +SET enable_bitmapscan = ON; +SET enable_seqscan = ON; diff --git a/sql/spoint_brin.sql b/sql/spoint_brin.sql index b03508d..4a37f37 100644 --- a/sql/spoint_brin.sql +++ b/sql/spoint_brin.sql @@ -93,7 +93,7 @@ BEGIN FOR exp IN EXECUTE 'EXPLAIN ' || q LOOP --RAISE NOTICE 'EXP: %', exp; - mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan)'); + mat := regexp_matches(exp, ' *(?:-> *)?(.*Scan on (test_points|brin_spoint))'); --RAISE NOTICE 'MAT: %', mat; IF mat IS NOT NULL THEN ret := array_append(ret, mat[1]); diff --git a/brin.c b/src/brin.c similarity index 95% rename from brin.c rename to src/brin.c index 5a5ae03..1c5ae1b 100644 --- a/brin.c +++ b/src/brin.c @@ -53,7 +53,7 @@ spoint_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } - spherepoint_gen_key(&spointkey, newval); + spherepoint_gen_key(spointkey, newval); /* * If spherekey pointer is NULL, we consider the spoint entry as 'empty'. @@ -61,6 +61,7 @@ spoint_brin_inclusion_add_value(PG_FUNCTION_ARGS) * The OpClass support empty entries: we need to set the "contains empty" * flag in the element (unless already set). */ + /* if (spointkey == NULL) { if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) @@ -71,6 +72,7 @@ spoint_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); } + */ /* if the recorded value is null, we just need to store the spherekey */ if (column->bv_allnulls) @@ -121,7 +123,7 @@ sbox_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } - spherebox_gen_key(&sboxkey, newval); + spherebox_gen_key(sboxkey, newval); /* * If spherekey pointer is NULL, we consider the spoint entry as 'empty'. @@ -129,6 +131,7 @@ sbox_brin_inclusion_add_value(PG_FUNCTION_ARGS) * The OpClass support empty entries: we need to set the "contains empty" * flag in the element (unless already set). */ + /* if (sboxkey == NULL) { if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) @@ -139,6 +142,7 @@ sbox_brin_inclusion_add_value(PG_FUNCTION_ARGS) PG_RETURN_BOOL(false); } + */ /* if the recorded value is null, we just need to store the spherekey */ if (column->bv_allnulls) @@ -178,7 +182,7 @@ spoint_overlaps_spherekey(PG_FUNCTION_ARGS) SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherepoint_gen_key(&k1, p1); + spherepoint_gen_key(k1, p1); if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) { PG_RETURN_BOOL(true); @@ -194,7 +198,7 @@ spoint_contains_spherekey(PG_FUNCTION_ARGS) SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherepoint_gen_key(&k1, p1); + spherepoint_gen_key(k1, p1); if (spherekey_interleave(k1, k2) == SCKEY_IN) { PG_RETURN_BOOL(true); @@ -210,7 +214,7 @@ spoint_iscontained_spherekey(PG_FUNCTION_ARGS) SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherepoint_gen_key(&k1, p1); + spherepoint_gen_key(k1, p1); if (spherekey_interleave(k2, k1) == SCKEY_IN) { PG_RETURN_BOOL(true); @@ -226,7 +230,7 @@ sbox_overlaps_spherekey(PG_FUNCTION_ARGS) SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherebox_gen_key(&k1, p1); + spherebox_gen_key(k1, p1); if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) { PG_RETURN_BOOL(true); @@ -242,7 +246,7 @@ sbox_contains_spherekey(PG_FUNCTION_ARGS) SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherebox_gen_key(&k1, p1); + spherebox_gen_key(k1, p1); if (spherekey_interleave(k1, k2) == SCKEY_IN) { PG_RETURN_BOOL(true); @@ -258,7 +262,7 @@ sbox_iscontained_spherekey(PG_FUNCTION_ARGS) SBOX *p1 = (SBOX *) PG_GETARG_POINTER(0); int32 *k2 = (int32 *) PG_GETARG_POINTER(1); - spherebox_gen_key(&k1, p1); + spherebox_gen_key(k1, p1); if (spherekey_interleave(k2, k1) == SCKEY_IN) { PG_RETURN_BOOL(true); @@ -317,8 +321,8 @@ spoint_overlaps_sbox(PG_FUNCTION_ARGS) int32 k2[6]; SBOX *p2 = (SBOX *) PG_GETARG_POINTER(1); - spherepoint_gen_key(&k1, p1); - spherebox_gen_key(&k2, p2); + spherepoint_gen_key(k1, p1); + spherebox_gen_key(k2, p2); if (spherekey_interleave(k1, k2) == SCKEY_OVERLAP) { @@ -336,8 +340,8 @@ sbox_iscontained_spoint(PG_FUNCTION_ARGS) int32 k2[6]; SPoint *p2 = (SPoint *) PG_GETARG_POINTER(1); - spherebox_gen_key(&k1, p1); - spherepoint_gen_key(&k2, p2); + spherebox_gen_key(k1, p1); + spherepoint_gen_key(k2, p2); if (spherekey_interleave(k1, k2) == SCKEY_IN) { diff --git a/brin.h b/src/brin.h similarity index 100% rename from brin.h rename to src/brin.h