diff --git a/Makefile b/Makefile
index 6c648d9..5782814 100644
--- a/Makefile
+++ b/Makefile
@@ -99,6 +99,7 @@ healpix_bare/healpix_bare.o : healpix_bare/healpix_bare.c
pg_version := $(word 2,$(shell $(PG_CONFIG) --version))
has_support_functions = $(if $(filter-out 9.% 10.% 11.%,$(pg_version)),y,n)
+has_index_options = $(if $(filter-out 9.% 10.% 11.% 12.%,$(pg_version)),y,n)
crushtest: TESTS += $(CRUSH_TESTS)
crushtest: installcheck
@@ -108,6 +109,13 @@ PGS_SQL += pgs_gist_support.sql
TESTS += gist_support
endif
+ifneq ($(USE_HEALPIX),0)
+ifeq ($(has_index_options),y)
+PGS_SQL += pgs_moc_options.sql
+TESTS += moc_options
+endif
+endif
+
# "make test" uses a special initialization file that doesn't rely on "create extension"
test: pg_sphere.test.sql
$(pg_regress_installcheck) --temp-instance=tmp_check $(REGRESS_OPTS) init_test $(TESTS)
@@ -186,6 +194,11 @@ pg_sphere--1.3.0--1.3.1.sql:
ifeq ($(has_support_functions),y)
pg_sphere--1.3.1--1.3.2.sql: pgs_gist_support.sql.in
endif
+ifneq ($(USE_HEALPIX),0)
+ifeq ($(has_index_options),y)
+pg_sphere--1.3.1--1.3.2.sql: pgs_moc_options.sql.in
+endif
+endif
pg_sphere--1.3.1--1.3.2.sql: pgs_circle_sel.sql.in
cat upgrade_scripts/$@.in $^ > $@
diff --git a/doc/indices.sgm b/doc/indices.sgm
index 0ef179f..c2e5b6f 100644
--- a/doc/indices.sgm
+++ b/doc/indices.sgm
@@ -121,14 +121,20 @@
The index works by casting all contained smocs to a fixed level, and
for each pixel at that level, storing which smocs overlap with that
pixel. This is especially beneficial for "overlaps" queries using
- the && operator. Two levels of granularity
- are provided: the default opclass smoc_gin_ops
- works on level 5 with a resolution of 12288 pixels, while the
- opclass smoc_gin_ops_fine works on level 8 with
- 786432 pixels. The downside of that approach is that storing large
+ the && operator.
+ The downside of that approach is that storing large
smocs like "all sky" (0/0-11) produces a large
number of index entries.
+
+ The default opclass smoc_gin_ops defaults to
+ working on level 5 with a resolution of 12288 pixels (12 * 4^5).
+ An alternative granularity can be selected by setting the
+ order parameter on the opclass (integer value
+ between 0 and 12; option only available on PG 13 and later).
+ The alternative smoc_gin_ops_fine opclass works
+ on level 8 with 786432 pixels.
+
Index of smoc coverage objects
@@ -136,8 +142,11 @@
+
+
+
diff --git a/expected/moc_options.out b/expected/moc_options.out
new file mode 100644
index 0000000..2342b79
--- /dev/null
+++ b/expected/moc_options.out
@@ -0,0 +1,40 @@
+create table moc_opt (m smoc);
+insert into moc_opt select format('9/%s', i)::smoc from generate_series(1, 1000) g(i);
+analyze moc_opt;
+create index moc_opt5 on moc_opt using gin (m);
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
+ QUERY PLAN
+---------------------------------------------------------------
+ Bitmap Heap Scan on moc_opt (actual rows=1 loops=1)
+ Recheck Cond: (m && '9/1'::smoc)
+ Rows Removed by Index Recheck: 254
+ Heap Blocks: exact=4
+ -> Bitmap Index Scan on moc_opt5 (actual rows=255 loops=1)
+ Index Cond: (m && '9/1'::smoc)
+(6 rows)
+
+drop index moc_opt5;
+create index moc_opt8 on moc_opt using gin (m smoc_gin_ops_fine);
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
+ QUERY PLAN
+-------------------------------------------------------------
+ Bitmap Heap Scan on moc_opt (actual rows=1 loops=1)
+ Recheck Cond: (m && '9/1'::smoc)
+ Rows Removed by Index Recheck: 2
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on moc_opt8 (actual rows=3 loops=1)
+ Index Cond: (m && '9/1'::smoc)
+(6 rows)
+
+drop index moc_opt8;
+create index moc_opt9 on moc_opt using gin (m smoc_gin_ops (order = 9));
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
+ QUERY PLAN
+-------------------------------------------------------------
+ Bitmap Heap Scan on moc_opt (actual rows=1 loops=1)
+ Recheck Cond: (m && '9/1'::smoc)
+ Heap Blocks: exact=1
+ -> Bitmap Index Scan on moc_opt9 (actual rows=1 loops=1)
+ Index Cond: (m && '9/1'::smoc)
+(5 rows)
+
diff --git a/pgs_moc_ops.sql.in b/pgs_moc_ops.sql.in
index 1319f5b..4e71af3 100644
--- a/pgs_moc_ops.sql.in
+++ b/pgs_moc_ops.sql.in
@@ -354,6 +354,7 @@ CREATE OPERATOR CLASS smoc_gin_ops
FUNCTION 4 smoc_gin_consistent (internal, int2, smoc, int4, internal, internal, internal, internal),
--FUNCTION 5 smoc_gin_compare_partial (),
--FUNCTION 6 smoc_gin_tri_consistent (),
+ --FUNCTION 7 (smoc) smoc_gin_options (internal), -- needs PG13
STORAGE int4;
CREATE OPERATOR CLASS smoc_gin_ops_fine
diff --git a/pgs_moc_options.sql.in b/pgs_moc_options.sql.in
new file mode 100644
index 0000000..e2b8e84
--- /dev/null
+++ b/pgs_moc_options.sql.in
@@ -0,0 +1,12 @@
+-- GIN opclass options
+
+CREATE FUNCTION smoc_gin_options (internal)
+ RETURNS void
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C
+ PARALLEL SAFE
+ IMMUTABLE
+ STRICT;
+
+ALTER OPERATOR FAMILY smoc_gin_ops USING gin
+ ADD FUNCTION 7 (smoc) smoc_gin_options (internal);
diff --git a/sql/moc_options.sql b/sql/moc_options.sql
new file mode 100644
index 0000000..5041f65
--- /dev/null
+++ b/sql/moc_options.sql
@@ -0,0 +1,14 @@
+create table moc_opt (m smoc);
+insert into moc_opt select format('9/%s', i)::smoc from generate_series(1, 1000) g(i);
+analyze moc_opt;
+
+create index moc_opt5 on moc_opt using gin (m);
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
+drop index moc_opt5;
+
+create index moc_opt8 on moc_opt using gin (m smoc_gin_ops_fine);
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
+drop index moc_opt8;
+
+create index moc_opt9 on moc_opt using gin (m smoc_gin_ops (order = 9));
+explain (analyze, costs off, timing off, summary off) select * from moc_opt where m && '9/1';
diff --git a/src/moc.c b/src/moc.c
index 6f1cbaf..4457aa6 100644
--- a/src/moc.c
+++ b/src/moc.c
@@ -3,7 +3,8 @@
#include
#include
-#include
+#include "access/gin.h"
+#include "access/reloptions.h"
#include "circle.h"
#include "polygon.h"
@@ -45,6 +46,7 @@ PG_FUNCTION_INFO_V1(smoc_gin_extract_value_fine);
PG_FUNCTION_INFO_V1(smoc_gin_extract_query);
PG_FUNCTION_INFO_V1(smoc_gin_extract_query_fine);
PG_FUNCTION_INFO_V1(smoc_gin_consistent);
+PG_FUNCTION_INFO_V1(smoc_gin_options);
int32 smoc_output_type = 0;
@@ -1079,7 +1081,6 @@ smoc_gin_extract_internal(Smoc *moc_a, int32 *nkeys, int gin_order)
if (*nkeys >= nalloc)
{
nalloc *= 2;
- Assert(nalloc < 2000000);
keys = repalloc(keys, nalloc * sizeof(Datum));
}
keys[(*nkeys)++] = Int32GetDatum(p);
@@ -1094,8 +1095,9 @@ smoc_gin_extract_value(PG_FUNCTION_ARGS)
{
Smoc* moc_a = (Smoc *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
int32* nkeys = (int32 *) PG_GETARG_POINTER(1);
+ int order = SMOC_GIN_GET_ORDER();
- PG_RETURN_DATUM(smoc_gin_extract_internal(moc_a, nkeys, MOC_GIN_ORDER));
+ PG_RETURN_DATUM(smoc_gin_extract_internal(moc_a, nkeys, order));
}
Datum
@@ -1114,13 +1116,14 @@ smoc_gin_extract_query(PG_FUNCTION_ARGS)
int32* nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber st = PG_GETARG_UINT16(2);
int32* searchmode = (int32 *) PG_GETARG_POINTER(6);
+ int order = SMOC_GIN_GET_ORDER();
if (st == MOC_GIN_STRATEGY_SUBSET || (st == MOC_GIN_STRATEGY_EQUAL && moc_a->area == 0))
*searchmode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
else if (st == MOC_GIN_STRATEGY_UNEQUAL)
*searchmode = GIN_SEARCH_MODE_ALL;
- PG_RETURN_DATUM(smoc_gin_extract_internal(moc_a, nkeys, MOC_GIN_ORDER));
+ PG_RETURN_DATUM(smoc_gin_extract_internal(moc_a, nkeys, order));
}
Datum
@@ -1202,3 +1205,21 @@ smoc_gin_consistent(PG_FUNCTION_ARGS)
/* not reached */
PG_RETURN_NULL();
}
+
+#if PG_VERSION_NUM >= 130000
+Datum
+smoc_gin_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(SMocGinOptions));
+ add_local_int_reloption(relopts, "order",
+ "smoc order to store in index",
+ MOC_GIN_ORDER_DEFAULT,
+ 0,
+ 12, /* maximum order fitting into 32bit */
+ offsetof(SMocGinOptions, order));
+
+ PG_RETURN_VOID();
+}
+#endif
diff --git a/src/pgs_moc.h b/src/pgs_moc.h
index 6bad1ae..68116d0 100644
--- a/src/pgs_moc.h
+++ b/src/pgs_moc.h
@@ -122,7 +122,7 @@ next_interval(int32 a)
#define MOC_AREA_ALL_SKY 3458764513820540928
-#define MOC_GIN_ORDER 5 /* order 5 has 12 * 4^5 = 12288 pixels */
+#define MOC_GIN_ORDER_DEFAULT 5 /* order 5 has 12 * 4^5 = 12288 pixels */
#define MOC_GIN_ORDER_FINE 8 /* order 8 has 12 * 4^8 = 786432 pixels */
#define MOC_GIN_STRATEGY_INTERSECTS 1
#define MOC_GIN_STRATEGY_SUBSET 2
@@ -130,4 +130,21 @@ next_interval(int32 a)
#define MOC_GIN_STRATEGY_EQUAL 4
#define MOC_GIN_STRATEGY_UNEQUAL 5
+/* smoc_gin_ops opclass options */
+#if PG_VERSION_NUM >= 130000
+Datum smoc_gin_options(PG_FUNCTION_ARGS);
+
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int order; /* smoc order to store in index (default 5) */
+} SMocGinOptions;
+
+#define SMOC_GIN_GET_ORDER() (PG_HAS_OPCLASS_OPTIONS() ? \
+ ((SMocGinOptions *) PG_GET_OPCLASS_OPTIONS())->order : \
+ MOC_GIN_ORDER_DEFAULT)
+#else
+#define SMOC_GIN_GET_ORDER() MOC_GIN_ORDER_DEFAULT
+#endif
+
#endif