From c72377a15c34605f76855691dc2d113e475ccc3e Mon Sep 17 00:00:00 2001 From: Christoph Berg Date: Tue, 24 Oct 2023 22:35:21 +0200 Subject: [PATCH] Configurable smoc_gin_ops index resolution Back when smoc GIN indexes were implemented, indexes did not support opclass options yet; that was added only later in PG 13. Add an "order" parameter on the smoc_gin_ops opclass to allow picking the smoc index granularity between level 0 and 12. (Larger levels do not fit into the internal int32 datatype anymore.) Example: `create index on sky using gin (coverage smoc_gin_ops (order = 8))` --- Makefile | 13 +++++++++++++ doc/indices.sgm | 19 ++++++++++++++----- expected/moc_options.out | 40 ++++++++++++++++++++++++++++++++++++++++ pgs_moc_ops.sql.in | 1 + pgs_moc_options.sql.in | 12 ++++++++++++ sql/moc_options.sql | 14 ++++++++++++++ src/moc.c | 29 +++++++++++++++++++++++++---- src/pgs_moc.h | 19 ++++++++++++++++++- 8 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 expected/moc_options.out create mode 100644 pgs_moc_options.sql.in create mode 100644 sql/moc_options.sql 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