From 205482a733c98e1ad4759bcbd34d4d464d2b77c8 Mon Sep 17 00:00:00 2001 From: Julien Rouhaud Date: Sat, 13 Jun 2020 09:42:37 +0200 Subject: [PATCH] Add support for hypothetical index on partitioned tables. --- Makefile | 8 ++++ expected/hypo_index_part.out | 65 +++++++++++++++++++++++++++++++++ expected/hypo_index_part_10.out | 32 ++++++++++++++++ hypopg.c | 39 +++++++++++++++++++- hypopg_index.c | 9 +++-- test/sql/hypo_index_part.sql | 30 +++++++++++++++ test/sql/hypo_index_part_10.sql | 26 +++++++++++++ 7 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 expected/hypo_index_part.out create mode 100644 expected/hypo_index_part_10.out create mode 100644 test/sql/hypo_index_part.sql create mode 100644 test/sql/hypo_index_part_10.sql diff --git a/Makefile b/Makefile index 4fbe617..ecae2e1 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,14 @@ ifneq ($(MAJORVERSION),$(filter $(MAJORVERSION), 9.2 9.3 9.4)) REGRESS += hypo_brin endif +ifeq ($(MAJORVERSION),10) + REGRESS += hypo_index_part_10 +endif + +ifneq ($(MAJORVERSION),$(filter $(MAJORVERSION), 9.2 9.3 9.4 9.5 9.6 10)) + REGRESS += hypo_index_part +endif + DEBUILD_ROOT = /tmp/$(EXTENSION) deb: release-zip diff --git a/expected/hypo_index_part.out b/expected/hypo_index_part.out new file mode 100644 index 0000000..fbd1fc9 --- /dev/null +++ b/expected/hypo_index_part.out @@ -0,0 +1,65 @@ +-- Hypothetical on partitioned tabled +CREATE TABLE hypo_part(id1 integer, id2 integer, id3 integer) + PARTITION BY LIST (id1); +CREATE TABLE hypo_part_1 + PARTITION OF hypo_part FOR VALUES IN (1) + PARTITION BY LIST (id2); +CREATE TABLE hypo_part_1_1 + PARTITION OF hypo_part_1 FOR VALUES IN (1); +INSERT INTO hypo_part SELECT 1, 1, generate_series(1, 10000); +ANALYZE hypo_part; +SET enable_seqscan = 0; +-- hypothetical index on root partitioned table should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part (id3)'); + nb +---- + 1 +(1 row) + +SELECT 1, COUNT(*) FROM do_explain('SELECT * FROM hypo_part WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; + ?column? | count +----------+------- + 1 | 1 +(1 row) + +SELECT hypopg_reset(); + hypopg_reset +-------------- + +(1 row) + +-- hypothetical index on non-root partitioned table should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1 (id3)'); + nb +---- + 1 +(1 row) + +SELECT 2, COUNT(*) FROM do_explain('SELECT * FROM hypo_part_1 WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; + ?column? | count +----------+------- + 2 | 1 +(1 row) + +SELECT hypopg_reset(); + hypopg_reset +-------------- + +(1 row) + +-- hypothetical index on partition should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1_1 (id3)'); + nb +---- + 1 +(1 row) + +SELECT 3, COUNT(*) FROM do_explain('SELECT * FROM hypo_part_1_1 WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; + ?column? | count +----------+------- + 3 | 1 +(1 row) + diff --git a/expected/hypo_index_part_10.out b/expected/hypo_index_part_10.out new file mode 100644 index 0000000..a0d4040 --- /dev/null +++ b/expected/hypo_index_part_10.out @@ -0,0 +1,32 @@ +-- Hypothetical on partitioned tabled +CREATE TABLE hypo_part(id1 integer, id2 integer, id3 integer) + PARTITION BY LIST (id1); +CREATE TABLE hypo_part_1 + PARTITION OF hypo_part FOR VALUES IN (1) + PARTITION BY LIST (id2); +CREATE TABLE hypo_part_1_1 + PARTITION OF hypo_part_1 FOR VALUES IN (1); +INSERT INTO hypo_part SELECT 1, 1, generate_series(1, 10000); +ANALYZE hypo_part; +-- hypothetical index on root partitioned table should not work +SELECT hypopg_create_index('CREATE INDEX ON hypo_part (id1)'); +ERROR: hypopg: cannot create hypothetical index on partitioned table "hypo_part" +-- hypothetical index on non-root partitioned table should not work +SELECT hypopg_create_index('CREATE INDEX ON hypo_part_1 (id1)'); +ERROR: hypopg: cannot create hypothetical index on partitioned table "hypo_part_1" +-- hypothetical index on partition should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1_1 (id3)'); + nb +---- + 1 +(1 row) + +-- Should use hypothetical index +SET enable_seqscan = 0; +SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo_part WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part_1_1.*'; + count +------- + 1 +(1 row) + diff --git a/hypopg.c b/hypopg.c index 9fe409b..24c2ab6 100644 --- a/hypopg.c +++ b/hypopg.c @@ -17,6 +17,12 @@ #include "postgres.h" #include "fmgr.h" +#if PG_VERSION_NUM >= 110000 +#include "catalog/partition.h" +#include "nodes/pg_list.h" +#include "utils/lsyscache.h" +#endif + #include "include/hypopg.h" #include "include/hypopg_import.h" #include "include/hypopg_index.h" @@ -75,6 +81,7 @@ static void hypo_get_relation_info_hook(PlannerInfo *root, RelOptInfo *rel); static get_relation_info_hook_type prev_get_relation_info_hook = NULL; +static bool hypo_index_match_table(hypoIndex *entry, Oid relid); static bool hypo_query_walker(Node *node); void @@ -256,6 +263,35 @@ hypo_utility_hook( } +static bool +hypo_index_match_table(hypoIndex *entry, Oid relid) +{ + /* Hypothetical index on the exact same relation, use it. */ + if (entry->relid == relid) + return true; +#if PG_VERSION_NUM >= 110000 + /* + * If the table is a partition, see if the hypothetical index belongs to + * one of the partition parent. + */ + if (get_rel_relispartition(relid)) + { + List *parents = get_partition_ancestors(relid); + ListCell *lc; + + foreach(lc, parents) + { + Oid oid = lfirst_oid(lc); + + if (oid == entry->relid) + return true; + } + } +#endif + + return false; +} + /* Detect if the current utility command is compatible with hypothetical indexes * i.e. an EXPLAIN, no ANALYZE */ @@ -292,6 +328,7 @@ hypo_query_walker(Node *parsetree) return false; } + /* Reset the isExplain flag after each query */ static void hypo_executorEnd_hook(QueryDesc *queryDesc) @@ -333,7 +370,7 @@ hypo_get_relation_info_hook(PlannerInfo *root, { hypoIndex *entry = (hypoIndex *) lfirst(lc); - if (entry->relid == relationObjectId) + if (hypo_index_match_table(entry, RelationGetRelid(relation))) { /* * hypothetical index found, add it to the relation's diff --git a/hypopg_index.c b/hypopg_index.c index 9e0fbf7..6c3fe69 100644 --- a/hypopg_index.c +++ b/hypopg_index.c @@ -366,18 +366,19 @@ hypo_index_store_parsetree(IndexStmt *node, const char *queryString) { #if PG_VERSION_NUM >= 90300 case RELKIND_MATVIEW: +#endif +#if PG_VERSION_NUM >= 110000 + case RELKIND_PARTITIONED_TABLE: #endif case RELKIND_RELATION: /* this is supported */ break; -#if PG_VERSION_NUM >= 100000 -#if PG_VERSION_NUM < 110000 +#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000 case RELKIND_PARTITIONED_TABLE: elog(ERROR, "hypopg: cannot create hypothetical index on" " partitioned table \"%s\"", node->relation->relname); -#endif /* pg11- */ break; -#endif /* pg10- */ +#endif default: elog(ERROR, "hypopg: \"%s\" is not a table" #if PG_VERSION_NUM >= 90300 diff --git a/test/sql/hypo_index_part.sql b/test/sql/hypo_index_part.sql new file mode 100644 index 0000000..36d5c9a --- /dev/null +++ b/test/sql/hypo_index_part.sql @@ -0,0 +1,30 @@ +-- Hypothetical on partitioned tabled + +CREATE TABLE hypo_part(id1 integer, id2 integer, id3 integer) + PARTITION BY LIST (id1); +CREATE TABLE hypo_part_1 + PARTITION OF hypo_part FOR VALUES IN (1) + PARTITION BY LIST (id2); +CREATE TABLE hypo_part_1_1 + PARTITION OF hypo_part_1 FOR VALUES IN (1); + +INSERT INTO hypo_part SELECT 1, 1, generate_series(1, 10000); +ANALYZE hypo_part; +SET enable_seqscan = 0; + +-- hypothetical index on root partitioned table should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part (id3)'); +SELECT 1, COUNT(*) FROM do_explain('SELECT * FROM hypo_part WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; +SELECT hypopg_reset(); + +-- hypothetical index on non-root partitioned table should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1 (id3)'); +SELECT 2, COUNT(*) FROM do_explain('SELECT * FROM hypo_part_1 WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; +SELECT hypopg_reset(); + +-- hypothetical index on partition should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1_1 (id3)'); +SELECT 3, COUNT(*) FROM do_explain('SELECT * FROM hypo_part_1_1 WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part.*'; diff --git a/test/sql/hypo_index_part_10.sql b/test/sql/hypo_index_part_10.sql new file mode 100644 index 0000000..ddc38cd --- /dev/null +++ b/test/sql/hypo_index_part_10.sql @@ -0,0 +1,26 @@ +-- Hypothetical on partitioned tabled + +CREATE TABLE hypo_part(id1 integer, id2 integer, id3 integer) + PARTITION BY LIST (id1); +CREATE TABLE hypo_part_1 + PARTITION OF hypo_part FOR VALUES IN (1) + PARTITION BY LIST (id2); +CREATE TABLE hypo_part_1_1 + PARTITION OF hypo_part_1 FOR VALUES IN (1); + +INSERT INTO hypo_part SELECT 1, 1, generate_series(1, 10000); +ANALYZE hypo_part; + +-- hypothetical index on root partitioned table should not work +SELECT hypopg_create_index('CREATE INDEX ON hypo_part (id1)'); + +-- hypothetical index on non-root partitioned table should not work +SELECT hypopg_create_index('CREATE INDEX ON hypo_part_1 (id1)'); + +-- hypothetical index on partition should work +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_1_1 (id3)'); + +-- Should use hypothetical index +SET enable_seqscan = 0; +SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo_part WHERE id3 = 1') e +WHERE e ~ 'Index.*<\d+>btree_hypo_part_1_1.*';