diff --git a/Makefile b/Makefile
index 062aab6..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
+ 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
+ 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
@@ -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
@@ -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 8a85c39..95a2563 100644
--- a/README.pg_sphere
+++ b/README.pg_sphere
@@ -9,8 +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 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 8103072..11940c2 100644
--- a/doc/indices.sgm
+++ b/doc/indices.sgm
@@ -8,15 +8,29 @@
pgSphere uses GiST
- to create spherical indices. An index speeds up the execution
- time of operators BRIN) algorithms to create
+ spherical indices.
+ 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 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 BRIN indexes 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">!=.
+
+
+ You can create a GiST index with the following spherical data types:
@@ -55,6 +69,10 @@
+
+ BRIN indexing supports just spherical points (spoint)
+ and spherical coordinates range (sbox) at the moment.
+
Simple index of spherical points
@@ -65,6 +83,23 @@
+
+ BRIN index can be created through the following syntax:
+
+
+
+
+
+ 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
new file mode 100644
index 0000000..958c709
--- /dev/null
+++ b/expected/spoint_brin.out
@@ -0,0 +1,87 @@
+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)
+
+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)
+
+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)
+
+SELECT * FROM test_points WHERE p <@ sbox '( (10d,10d), (20d,20d) )';
+ p
+-----------------------------------------
+ (0.349065850398866 , 0.174532925199433)
+(1 row)
+
+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)
+
+SELECT * FROM test_points WHERE p && sbox '( (10d,10d), (20d,20d) )';
+ p
+-----------------------------------------
+ (0.349065850398866 , 0.174532925199433)
+(1 row)
+
+-- 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
new file mode 100644
index 0000000..4b0fba5
--- /dev/null
+++ b/pgs_brin.sql.in
@@ -0,0 +1,413 @@
+
+--------------------------------------------------------------------
+-- 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),
+
+ 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
new file mode 100644
index 0000000..4a37f37
--- /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 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) )''');
+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;
diff --git a/src/brin.c b/src/brin.c
new file mode 100644
index 0000000..1c5ae1b
--- /dev/null
+++ b/src/brin.c
@@ -0,0 +1,352 @@
+/*
+ * 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/src/brin.h b/src/brin.h
new file mode 100644
index 0000000..b1a1c2c
--- /dev/null
+++ b/src/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