diff --git a/expected/hypo_index_table_10.out b/expected/hypo_index_table_10.out new file mode 100644 index 0000000..745f573 --- /dev/null +++ b/expected/hypo_index_table_10.out @@ -0,0 +1,217 @@ +-- Creating real and hypothetical tables +-- ===================================== +-- Real tables +-- ------------ +-- 1. Range partitioning for hypothetical index +DROP TABLE IF EXISTS part_range; +CREATE TABLE part_range (id integer, val text) PARTITION BY RANGE (id); +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +CREATE TABLE part_range_10000_20000 PARTITION OF part_range FOR VALUES FROM (10000) TO (20000); +CREATE TABLE part_range_20000_30000 PARTITION OF part_range FOR VALUES FROM (20000) TO (30000); +INSERT INTO part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +-- Hypothetical tables +-- ------------------- +-- 0. Dropping any hypothetical object +SELECT * FROM hypopg_reset_index(); + hypopg_reset_index +-------------------- + +(1 row) + +SELECT * FROM hypopg_reset_table(); + hypopg_reset_table +-------------------- + +(1 row) + +-- 1. Range partitioning for hypothetical index +DROP TABLE IF EXISTS hypo_part_range; +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); + tablename +------------------------- + hypo_part_range_1_10000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); + tablename +----------------------------- + hypo_part_range_10000_20000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); + tablename +----------------------------- + hypo_part_range_20000_30000 +(1 row) + +-- Maintenance +-- ----------- +VACUUM ANALYZE; +SELECT * FROM hypopg_analyze('hypo_part_range',100); + hypopg_analyze +---------------- + +(1 row) + +-- Test hypothetical indexes on hypothetical partitioning behavior +-- =============================================================== +-- Indexes on root partitioning tree +-- --------------------------------- +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_range (id)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf partition +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_range(id)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf hypothetical partition +-- Test on real tables +-- ------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_1_10000'; + count +------- + 0 +(1 row) + +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_20000_30000'; + count +------- + 0 +(1 row) + +-- Test on hypothetical tables +-- --------------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_1_10000'; + count +------- + 0 +(1 row) + +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_20000_30000'; + count +------- + 0 +(1 row) + +-- Indexes on specific partitions +-- ------------------------------ +SELECT * FROM hypopg_reset_index(); + hypopg_reset_index +-------------------- + +(1 row) + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_range_1_10000 (id)'); + nb +---- + 1 +(1 row) + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_range_1_10000(id)'); + nb +---- + 1 +(1 row) + +-- Test on real tables +-- ------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_1_10000'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_20000_30000'; + count +------- + 0 +(1 row) + +-- Test on hypothetical tables +-- --------------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_1_10000'; + count +------- + 1 +(1 row) + +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_20000_30000'; + count +------- + 0 +(1 row) + +-- Sanity checks +-- ------------- +SELECT * FROM hypopg_reset_index(); + hypopg_reset_index +-------------------- + +(1 row) + +-- No hypothetical on non-leaf partition +DROP TABLE IF EXISTS part_multi; +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q2 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q2', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); + tablename +---------------------- + hypo_part_multi_1_q2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); + tablename +---------------------- + hypo_part_multi_1_q1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_b +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_a +(1 row) + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_multi_1 (dpt)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf partition +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_multi_1_q1 (dpt)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf partition +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_multi_1 (dpt)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf hypothetical partition +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_multi_1_q1 (dpt)'); +ERROR: hypopg: cannot add hypothetical index on non-leaf hypothetical partition diff --git a/expected/hypo_table_10.out b/expected/hypo_table_10.out new file mode 100644 index 0000000..f1e20cd --- /dev/null +++ b/expected/hypo_table_10.out @@ -0,0 +1,1024 @@ +-- Creating real and hypothetical tables" +-- =====================================" +-- Real tables +-- ----------- +-- 1. Range partition +DROP TABLE IF EXISTS part_range; +NOTICE: table "part_range" does not exist, skipping +CREATE TABLE part_range (id integer, val text) PARTITION BY RANGE (id); +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +CREATE TABLE part_range_10000_20000 PARTITION OF part_range FOR VALUES FROM (10000) TO (20000); +CREATE TABLE part_range_20000_30000 PARTITION OF part_range FOR VALUES FROM (20000) TO (30000); +INSERT INTO part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +-- 2. List partitioning +DROP TABLE IF EXISTS part_list; +NOTICE: table "part_list" does not exist, skipping +CREATE TABLE part_list (id integer, id_key integer, val text) PARTITION BY LIST (id_key); +CREATE TABLE part_list_4_5_6_8_10 PARTITION OF part_list FOR VALUES IN (4, 5, 6, 8, 10); +CREATE TABLE part_list_7_9 PARTITION OF part_list FOR VALUES IN (7, 9); +CREATE TABLE part_list_1_2_3 PARTITION OF part_list FOR VALUES IN (1, 2, 3); +INSERT INTO part_list SELECT i, (i % 9) + 1, 'line ' || i FROM generate_series(1, 50000) i; +-- 3. Multi level range +DROP TABLE IF EXISTS part_multi; +NOTICE: table "part_multi" does not exist, skipping +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_2_q2 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_2_q3 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_2_q4 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q2 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_1_q3 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_1_q4 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +CREATE TABLE part_multi_3 PARTITION OF part_multi FOR VALUES IN (3) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_3_q1 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_3_q2 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_3_q3 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_3_q4 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +INSERT INTO part_multi select (i%3)+1, '2015-01-01'::date + interval '1 day' * (i%365), 'val ' || i FROM generate_series(1,50000) i; +-- Hypothetical tables +-- ------------------- +-- 0. Dropping any hypothetical object +SELECT * FROM hypopg_reset(); + hypopg_reset +-------------- + +(1 row) + +-- 1. Range partition +DROP TABLE IF EXISTS hypo_part_range; +NOTICE: table "hypo_part_range" does not exist, skipping +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); + tablename +------------------------- + hypo_part_range_1_10000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); + tablename +----------------------------- + hypo_part_range_10000_20000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); + tablename +----------------------------- + hypo_part_range_20000_30000 +(1 row) + +-- 2. List partitioning +DROP TABLE IF EXISTS hypo_part_list; +NOTICE: table "hypo_part_list" does not exist, skipping +CREATE TABLE hypo_part_list (id integer, id_key integer, val text); +INSERT INTO hypo_part_list SELECT i, (i % 9) + 1, 'line ' || i FROM generate_series(1, 50000) i; +SELECT * FROM hypopg_partition_table('hypo_part_list', 'PARTITION BY LIST (id_key)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_list_4_5_6_8_10', 'PARTITION OF hypo_part_list FOR VALUES IN (4, 5, 6, 8, 10)'); + tablename +--------------------------- + hypo_part_list_4_5_6_8_10 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_list_7_9', 'PARTITION OF hypo_part_list FOR VALUES IN (7, 9)'); + tablename +-------------------- + hypo_part_list_7_9 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_list_1_2_3', 'PARTITION OF hypo_part_list FOR VALUES IN (1, 2, 3)'); + tablename +---------------------- + hypo_part_list_1_2_3 +(1 row) + +-- 3. Multi level range +DROP TABLE IF EXISTS hypo_part_multi; +NOTICE: table "hypo_part_multi" does not exist, skipping +CREATE TABLE hypo_part_multi(dpt smallint, dt date, val text); +INSERT INTO hypo_part_multi select (i%3)+1, '2015-01-01'::date + interval '1 day' * (i%365), 'val ' || i FROM generate_series(1,50000) i; +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2', 'PARTITION OF hypo_part_multi FOR VALUES IN (2)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q1', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); + tablename +---------------------- + hypo_part_multi_2_q1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q2', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); + tablename +---------------------- + hypo_part_multi_2_q2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q3', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); + tablename +---------------------- + hypo_part_multi_2_q3 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q4', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); + tablename +---------------------- + hypo_part_multi_2_q4 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q2', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); + tablename +---------------------- + hypo_part_multi_1_q2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q3', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); + tablename +---------------------- + hypo_part_multi_1_q3 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q4', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); + tablename +---------------------- + hypo_part_multi_1_q4 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); + tablename +---------------------- + hypo_part_multi_1_q1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_b +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_a +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3', 'PARTITION OF hypo_part_multi FOR VALUES IN (3)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_3 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q1', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); + tablename +---------------------- + hypo_part_multi_3_q1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q2', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); + tablename +---------------------- + hypo_part_multi_3_q2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q3', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); + tablename +---------------------- + hypo_part_multi_3_q3 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q4', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); + tablename +---------------------- + hypo_part_multi_3_q4 +(1 row) + +-- Maintenance +-- ----------- +VACUUM ANALYZE; +SELECT * FROM hypopg_analyze('hypo_part_range',100); + hypopg_analyze +---------------- + +(1 row) + +SELECT * FROM hypopg_analyze('hypo_part_list',100); + hypopg_analyze +---------------- + +(1 row) + +SELECT * FROM hypopg_analyze('hypo_part_multi',100); + hypopg_analyze +---------------- + +(1 row) + +-- Test deparsing +-- ============== +SELECT relid = rootid AS is_root, tablename, parentid IS NULL parentid_is_null, + parentid IS NOT NULL AS parentid_is_not_null, + partition_by_clause, partition_bounds +FROM hypopg_table() +ORDER BY tablename COLLATE "C"; + is_root | tablename | parentid_is_null | parentid_is_not_null | partition_by_clause | partition_bounds +---------+-----------------------------+------------------+----------------------+----------------------------+-------------------------------------------------- + t | hypo_part_list | t | f | PARTITION BY LIST (id_key) | + f | hypo_part_list_1_2_3 | f | t | | FOR VALUES IN (1, 2, 3) + f | hypo_part_list_4_5_6_8_10 | f | t | | FOR VALUES IN (4, 5, 6, 8, 10) + f | hypo_part_list_7_9 | f | t | | FOR VALUES IN (7, 9) + t | hypo_part_multi | t | f | PARTITION BY LIST (dpt) | + f | hypo_part_multi_1 | f | t | PARTITION BY RANGE (dt) | FOR VALUES IN ('1') + f | hypo_part_multi_1_q1 | f | t | PARTITION BY RANGE (dt) | FOR VALUES FROM ('01-01-2015') TO ('04-01-2015') + f | hypo_part_multi_1_q1_a | f | t | | FOR VALUES FROM ('01-01-2015') TO ('02-01-2015') + f | hypo_part_multi_1_q1_b | f | t | | FOR VALUES FROM ('02-01-2015') TO ('04-01-2015') + f | hypo_part_multi_1_q2 | f | t | | FOR VALUES FROM ('04-01-2015') TO ('07-01-2015') + f | hypo_part_multi_1_q3 | f | t | | FOR VALUES FROM ('07-01-2015') TO ('10-01-2015') + f | hypo_part_multi_1_q4 | f | t | | FOR VALUES FROM ('10-01-2015') TO ('01-01-2016') + f | hypo_part_multi_2 | f | t | PARTITION BY RANGE (dt) | FOR VALUES IN ('2') + f | hypo_part_multi_2_q1 | f | t | | FOR VALUES FROM ('01-01-2015') TO ('04-01-2015') + f | hypo_part_multi_2_q2 | f | t | | FOR VALUES FROM ('04-01-2015') TO ('07-01-2015') + f | hypo_part_multi_2_q3 | f | t | | FOR VALUES FROM ('07-01-2015') TO ('10-01-2015') + f | hypo_part_multi_2_q4 | f | t | | FOR VALUES FROM ('10-01-2015') TO ('01-01-2016') + f | hypo_part_multi_3 | f | t | PARTITION BY RANGE (dt) | FOR VALUES IN ('3') + f | hypo_part_multi_3_q1 | f | t | | FOR VALUES FROM ('01-01-2015') TO ('04-01-2015') + f | hypo_part_multi_3_q2 | f | t | | FOR VALUES FROM ('04-01-2015') TO ('07-01-2015') + f | hypo_part_multi_3_q3 | f | t | | FOR VALUES FROM ('07-01-2015') TO ('10-01-2015') + f | hypo_part_multi_3_q4 | f | t | | FOR VALUES FROM ('10-01-2015') TO ('01-01-2016') + t | hypo_part_range | t | f | PARTITION BY RANGE (id) | + f | hypo_part_range_10000_20000 | f | t | | FOR VALUES FROM (10000) TO (20000) + f | hypo_part_range_1_10000 | f | t | | FOR VALUES FROM (1) TO (10000) + f | hypo_part_range_20000_30000 | f | t | | FOR VALUES FROM (20000) TO (30000) +(26 rows) + +-- Test hypothetical partitioning behavior +-- ======================================= +-- Simple queries +-- -------------- +-- Real tables +-- ----------- +-- 1. Range partitioning +EXPLAIN (COSTS OFF) SELECT * FROM part_range; + QUERY PLAN +------------------------------------------ + Append + -> Seq Scan on part_range_1_10000 + -> Seq Scan on part_range_10000_20000 + -> Seq Scan on part_range_20000_30000 +(4 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_range WHERE id = 42; + QUERY PLAN +-------------------------------------- + Append + -> Seq Scan on part_range_1_10000 + Filter: (id = 42) +(3 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_range WHERE id < 15000; + QUERY PLAN +------------------------------------------ + Append + -> Seq Scan on part_range_1_10000 + Filter: (id < 15000) + -> Seq Scan on part_range_10000_20000 + Filter: (id < 15000) +(5 rows) + +-- 2. List partitioning +EXPLAIN (COSTS OFF) SELECT * FROM part_list; + QUERY PLAN +---------------------------------------- + Append + -> Seq Scan on part_list_4_5_6_8_10 + -> Seq Scan on part_list_7_9 + -> Seq Scan on part_list_1_2_3 +(4 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id < 42; + QUERY PLAN +---------------------------------------- + Append + -> Seq Scan on part_list_4_5_6_8_10 + Filter: (id < 42) + -> Seq Scan on part_list_7_9 + Filter: (id < 42) + -> Seq Scan on part_list_1_2_3 + Filter: (id < 42) +(7 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id < 15000; + QUERY PLAN +---------------------------------------- + Append + -> Seq Scan on part_list_4_5_6_8_10 + Filter: (id < 15000) + -> Seq Scan on part_list_7_9 + Filter: (id < 15000) + -> Seq Scan on part_list_1_2_3 + Filter: (id < 15000) +(7 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id_key < 5; + QUERY PLAN +---------------------------------------- + Append + -> Seq Scan on part_list_4_5_6_8_10 + Filter: (id_key < 5) + -> Seq Scan on part_list_1_2_3 + Filter: (id_key < 5) +(5 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id_key = 7; + QUERY PLAN +--------------------------------- + Append + -> Seq Scan on part_list_7_9 + Filter: (id_key = 7) +(3 rows) + +-- 3. Multi level range +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; + QUERY PLAN +------------------------------------- + Append + -> Seq Scan on part_multi_2_q1 + -> Seq Scan on part_multi_2_q2 + -> Seq Scan on part_multi_2_q3 + -> Seq Scan on part_multi_2_q4 + -> Seq Scan on part_multi_1_q2 + -> Seq Scan on part_multi_1_q3 + -> Seq Scan on part_multi_1_q4 + -> Seq Scan on part_multi_3_q1 + -> Seq Scan on part_multi_3_q2 + -> Seq Scan on part_multi_3_q3 + -> Seq Scan on part_multi_3_q4 + -> Seq Scan on part_multi_1_q1_b + -> Seq Scan on part_multi_1_q1_a +(14 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi WHERE dpt = 2; + QUERY PLAN +----------------------------------- + Append + -> Seq Scan on part_multi_2_q1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q2 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q3 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q4 + Filter: (dpt = 2) +(9 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi WHERE dt >= '2015-01-05' AND dt < '2015-01-10'; + QUERY PLAN +---------------------------------------------------------------------------- + Append + -> Seq Scan on part_multi_2_q1 + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) + -> Seq Scan on part_multi_3_q1 + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) + -> Seq Scan on part_multi_1_q1_a + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) +(7 rows) + +-- Hypothetical tables +-- ------------------- +-- 1. Range partitioning +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range; + QUERY PLAN +--------------------------------------------------------------- + Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + -> Seq Scan on hypo_part_range hypo_part_range_10000_20000 + -> Seq Scan on hypo_part_range hypo_part_range_20000_30000 +(4 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = 42; + QUERY PLAN +----------------------------------------------------------- + Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + Filter: (id = 42) +(3 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id < 15000; + QUERY PLAN +--------------------------------------------------------------- + Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + Filter: (id < 15000) + -> Seq Scan on hypo_part_range hypo_part_range_10000_20000 + Filter: (id < 15000) +(5 rows) + +-- 2. List partitioning +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list; + QUERY PLAN +------------------------------------------------------------ + Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 +(4 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id < 42; + QUERY PLAN +------------------------------------------------------------ + Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + Filter: (id < 42) + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + Filter: (id < 42) + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 + Filter: (id < 42) +(7 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id < 15000; + QUERY PLAN +------------------------------------------------------------ + Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + Filter: (id < 15000) + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + Filter: (id < 15000) + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 + Filter: (id < 15000) +(7 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id_key < 5; + QUERY PLAN +------------------------------------------------------------ + Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + Filter: (id_key < 5) + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 + Filter: (id_key < 5) +(5 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id_key = 7; + QUERY PLAN +----------------------------------------------------- + Append + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + Filter: (id_key = 7) +(3 rows) + +-- 3. Multi level range +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +---------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q2 + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q3 + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q4 + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q2 + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q3 + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q4 + -> Seq Scan on hypo_part_multi hypo_part_multi_3_q1 + -> Seq Scan on hypo_part_multi hypo_part_multi_3_q2 + -> Seq Scan on hypo_part_multi hypo_part_multi_3_q3 + -> Seq Scan on hypo_part_multi hypo_part_multi_3_q4 + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q1_b + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q1_a +(14 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi WHERE dpt = 2; + QUERY PLAN +-------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q2 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q3 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q4 + Filter: (dpt = 2) +(9 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi WHERE dt >= '2015-01-05' AND dt < '2015-01-10'; + QUERY PLAN +---------------------------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) + -> Seq Scan on hypo_part_multi hypo_part_multi_3_q1 + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q1_a + Filter: ((dt >= '01-05-2015'::date) AND (dt < '01-10-2015'::date)) +(7 rows) + +-- Join queries +-- ------------ +-- Simple joins +-- ------------ +-- 1. Real tables +EXPLAIN (COSTS OFF) SELECT * FROM part_range t1, part_range t2 WHERE t1.id = t2.id and t1.id < 15000; + QUERY PLAN +----------------------------------------------------------- + Hash Join + Hash Cond: (t2.id = t1.id) + -> Append + -> Seq Scan on part_range_1_10000 t2 + -> Seq Scan on part_range_10000_20000 t2_1 + -> Seq Scan on part_range_20000_30000 t2_2 + -> Hash + -> Append + -> Seq Scan on part_range_1_10000 t1 + Filter: (id < 15000) + -> Seq Scan on part_range_10000_20000 t1_1 + Filter: (id < 15000) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list t1, part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; + QUERY PLAN +------------------------------------------------------- + Hash Join + Hash Cond: (t2.id_key = t1.id_key) + -> Append + -> Seq Scan on part_list_4_5_6_8_10 t2 + -> Seq Scan on part_list_7_9 t2_1 + -> Seq Scan on part_list_1_2_3 t2_2 + -> Hash + -> Append + -> Seq Scan on part_list_4_5_6_8_10 t1 + Filter: (id_key < 5) + -> Seq Scan on part_list_1_2_3 t1_1 + Filter: (id_key < 5) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi t1, part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; + QUERY PLAN +---------------------------------------------------- + Nested Loop + -> Append + -> Seq Scan on part_multi_2_q1 t1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q2 t1_1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q3 t1_2 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q4 t1_3 + Filter: (dpt = 2) + -> Materialize + -> Append + -> Seq Scan on part_multi_2_q1 t2 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q2 t2_1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q3 t2_2 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q4 t2_3 + Filter: (dpt = 2) +(20 rows) + +-- 2. Hypothetical tables +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range t1, hypo_part_range t2 WHERE t1.id = t2.id and t1.id < 15000; + QUERY PLAN +--------------------------------------------------------------------------- + Hash Join + Hash Cond: (hypo_part_range_1_10000_1.id = hypo_part_range_1_10000.id) + -> Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000_1 + -> Seq Scan on hypo_part_range hypo_part_range_10000_20000_1 + -> Seq Scan on hypo_part_range hypo_part_range_20000_30000 + -> Hash + -> Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + Filter: (id < 15000) + -> Seq Scan on hypo_part_range hypo_part_range_10000_20000 + Filter: (id < 15000) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list t1, hypo_part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; + QUERY PLAN +-------------------------------------------------------------------------------------- + Hash Join + Hash Cond: (hypo_part_list_4_5_6_8_10_1.id_key = hypo_part_list_4_5_6_8_10.id_key) + -> Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10_1 + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3_1 + -> Hash + -> Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + Filter: (id_key < 5) + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 + Filter: (id_key < 5) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi t1, hypo_part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; + QUERY PLAN +---------------------------------------------------------------------- + Nested Loop + -> Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q2 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q3 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q4 + Filter: (dpt = 2) + -> Materialize + -> Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1_1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q2_1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q3_1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q4_1 + Filter: (dpt = 2) +(20 rows) + +-- 3. Real tables and hypothetical tables +EXPLAIN (COSTS OFF) SELECT * FROM part_range t1, hypo_part_range t2 WHERE t1.id = t2.id and t1.id < 15000; + QUERY PLAN +--------------------------------------------------------------------- + Hash Join + Hash Cond: (hypo_part_range_1_10000.id = t1.id) + -> Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + -> Seq Scan on hypo_part_range hypo_part_range_10000_20000 + -> Seq Scan on hypo_part_range hypo_part_range_20000_30000 + -> Hash + -> Append + -> Seq Scan on part_range_1_10000 t1 + Filter: (id < 15000) + -> Seq Scan on part_range_10000_20000 t1_1 + Filter: (id < 15000) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_list t1, hypo_part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; + QUERY PLAN +------------------------------------------------------------------ + Hash Join + Hash Cond: (hypo_part_list_4_5_6_8_10.id_key = t1.id_key) + -> Append + -> Seq Scan on hypo_part_list hypo_part_list_4_5_6_8_10 + -> Seq Scan on hypo_part_list hypo_part_list_7_9 + -> Seq Scan on hypo_part_list hypo_part_list_1_2_3 + -> Hash + -> Append + -> Seq Scan on part_list_4_5_6_8_10 t1 + Filter: (id_key < 5) + -> Seq Scan on part_list_1_2_3 t1_1 + Filter: (id_key < 5) +(12 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi t1, hypo_part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; + QUERY PLAN +-------------------------------------------------------------------- + Nested Loop + -> Append + -> Seq Scan on part_multi_2_q1 t1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q2 t1_1 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q3 t1_2 + Filter: (dpt = 2) + -> Seq Scan on part_multi_2_q4 t1_3 + Filter: (dpt = 2) + -> Materialize + -> Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q2 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q3 + Filter: (dpt = 2) + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q4 + Filter: (dpt = 2) +(20 rows) + +-- Tests for sanity checks +-- ======================= +-- Duplicate name +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +ERROR: relation "part_range_1_10000" already exists +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +ERROR: hypopg: hypothetical table hypo_part_range_1_10000 already exists +-- Overlapping range bounds +CREATE TABLE part_range_1_10000_dup PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +ERROR: partition "part_range_1_10000_dup" would overlap partition "part_range_1_10000" +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000_dup', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +ERROR: hypopg: partition "hypo_part_range_1_10000_dup" would overlap partition "hypo_part_range_1_10000" +-- Overlapping list bounds +CREATE TABLE part_list_1_2_3_dup PARTITION OF part_list FOR VALUES IN (1, 2, 3); +ERROR: partition "part_list_1_2_3_dup" would overlap partition "part_list_1_2_3" +SELECT tablename FROM hypopg_add_partition('hypo_part_list_1_2_3_dup', 'PARTITION OF hypo_part_list FOR VALUES IN (1, 2, 3)'); +ERROR: hypopg: partition "hypo_part_list_1_2_3_dup" would overlap partition "hypo_part_list_1_2_3" +-- Overlapping range bounds, subpartition +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +ERROR: relation "part_multi_1_q1_a" already exists +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a_dup', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); +ERROR: hypopg: partition "hypo_part_multi_1_q1_a_dup" would overlap partition "hypo_part_multi_1_q1_a" +-- relcache callback test +-- ====================== +SELECT tablename FROM hypopg_table() WHERE tablename LIKE 'hypo_part_range%' ORDER BY tablename COLLATE "C"; + tablename +----------------------------- + hypo_part_range + hypo_part_range_10000_20000 + hypo_part_range_1_10000 + hypo_part_range_20000_30000 +(4 rows) + +DROP TABLE hypo_part_range; +SELECT tablename FROM hypopg_table() WHERE tablename LIKE 'hypo_part_range%' ORDER BY tablename COLLATE "C"; + tablename +----------- +(0 rows) + +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); + hypopg_partition_table +------------------------ + t +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); + tablename +----------------------------- + hypo_part_range_10000_20000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); + tablename +----------------------------- + hypo_part_range_20000_30000 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); + tablename +------------------------- + hypo_part_range_1_10000 +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = 42; + QUERY PLAN +----------------------------------------------------------- + Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + Filter: (id = 42) +(3 rows) + +-- no UPDATE/DELETE test +-- ===================== +-- simple UPDATE and DELETE on hypothetically partitioned table +EXPLAIN (COSTS OFF) UPDATE hypo_part_range set id = id; +ERROR: hypopg: UPDATE and DELETE on hypothetically partitioned tables are not supported +EXPLAIN DELETE FROM hypo_part_range WHERE id = 42; +ERROR: hypopg: UPDATE and DELETE on hypothetically partitioned tables are not supported +-- UPDATE and DELETE on hypothetically partitioned table inside CTE +EXPLAIN (COSTS OFF) WITH s AS (UPDATE hypo_part_range set id = id returning *) SELECT 1; +ERROR: hypopg: UPDATE and DELETE on hypothetically partitioned tables are not supported +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM hypo_part_range WHERE id = 42 returning *) SELECT 1; +ERROR: hypopg: UPDATE and DELETE on hypothetically partitioned tables are not supported +-- UPDATE and DELETE involving hypothetically partitioned table, but on regular +-- tables +CREATE TABLE foo(id integer); +-- UPDATE on non hypothetically partitioned table but having a hypothetically +-- partitioned table joined +EXPLAIN (COSTS OFF) WITH s AS (UPDATE foo SET id = 0 from hypo_part_range WHERE foo.id = hypo_part_range.id AND hypo_part_range.id > 25000 RETURNING *) SELECT 1; + QUERY PLAN +----------------------------------------------------------------------------------------- + Result + CTE s + -> Update on foo + -> Hash Join + Hash Cond: (foo.id = hypo_part_range_20000_30000.id) + -> Seq Scan on foo + -> Hash + -> Append + -> Seq Scan on hypo_part_range hypo_part_range_20000_30000 + Filter: (id > 25000) +(10 rows) + +-- same but with real table +EXPLAIN (COSTS OFF) WITH s AS (UPDATE foo SET id = 0 from part_range WHERE foo.id = part_range.id AND part_range.id > 25000 RETURNING *) SELECT 1; + QUERY PLAN +-------------------------------------------------------------------- + Result + CTE s + -> Update on foo + -> Hash Join + Hash Cond: (foo.id = part_range_20000_30000.id) + -> Seq Scan on foo + -> Hash + -> Append + -> Seq Scan on part_range_20000_30000 + Filter: (id > 25000) +(10 rows) + +-- DELETE on non hypothetically partitioned table but having a hypothetically +-- partitioned table joined +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM foo USING hypo_part_range WHERE foo.id = hypo_part_range.id AND hypo_part_range.id = 42 RETURNING *) SELECT 1; + QUERY PLAN +------------------------------------------------------------------------------- + Result + CTE s + -> Delete on foo + -> Nested Loop + -> Append + -> Seq Scan on hypo_part_range hypo_part_range_1_10000 + Filter: (id = 42) + -> Seq Scan on foo + Filter: (id = 42) +(9 rows) + +-- same but with real table +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM foo USING part_range WHERE foo.id = part_range.id AND part_range.id = 42 RETURNING *) SELECT 1; + QUERY PLAN +---------------------------------------------------------- + Result + CTE s + -> Delete on foo + -> Nested Loop + -> Append + -> Seq Scan on part_range_1_10000 + Filter: (id = 42) + -> Seq Scan on foo + Filter: (id = 42) +(9 rows) + +-- childless partitioning +-- ====================== +SELECT * FROM hypopg_reset(); + hypopg_reset +-------------- + +(1 row) + +DROP TABLE part_multi; +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); + hypopg_partition_table +------------------------ + t +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2', 'PARTITION OF hypo_part_multi FOR VALUES IN (2)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_2 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q1', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); + tablename +---------------------- + hypo_part_multi_2_q1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); + tablename +------------------- + hypo_part_multi_1 +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); + tablename +---------------------- + hypo_part_multi_1_q1 +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; + QUERY PLAN +----------------------------------- + Append + -> Seq Scan on part_multi_2_q1 +(2 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +-------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 +(2 rows) + +-- removing hypothetical partitions +-- ================================ +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_b +(1 row) + +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + tablename +------------------------ + hypo_part_multi_1_q1_a +(1 row) + +DROP TABLE part_multi_1_q1_a; +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi_1_q1_a'; + hypopg_drop_table +------------------- + +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; + QUERY PLAN +------------------------------------- + Append + -> Seq Scan on part_multi_2_q1 + -> Seq Scan on part_multi_1_q1_b +(3 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +---------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 + -> Seq Scan on hypo_part_multi hypo_part_multi_1_q1_b +(3 rows) + +DROP TABLE part_multi_1; +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi_1'; + hypopg_drop_table +------------------- + +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; + QUERY PLAN +----------------------------------- + Append + -> Seq Scan on part_multi_2_q1 +(2 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +-------------------------------------------------------- + Append + -> Seq Scan on hypo_part_multi hypo_part_multi_2_q1 +(2 rows) + +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi'; + hypopg_drop_table +------------------- + +(1 row) + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + QUERY PLAN +----------------------------- + Seq Scan on hypo_part_multi +(1 row) + +SELECT hypopg_drop_table(oid) FROM pg_class WHERE relname = 'pg_class'; +ERROR: hypopg: Oid 1259 is not a hypothetically partitioned table +SELECT hypopg_drop_table(1); +ERROR: hypopg: Oid 1 is not a hypothetically partitioned table diff --git a/test/sql/hypo_index_table_10.sql b/test/sql/hypo_index_table_10.sql new file mode 100644 index 0000000..16d9785 --- /dev/null +++ b/test/sql/hypo_index_table_10.sql @@ -0,0 +1,102 @@ +-- Creating real and hypothetical tables +-- ===================================== +-- Real tables +-- ------------ +-- 1. Range partitioning for hypothetical index +DROP TABLE IF EXISTS part_range; +CREATE TABLE part_range (id integer, val text) PARTITION BY RANGE (id); +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +CREATE TABLE part_range_10000_20000 PARTITION OF part_range FOR VALUES FROM (10000) TO (20000); +CREATE TABLE part_range_20000_30000 PARTITION OF part_range FOR VALUES FROM (20000) TO (30000); +INSERT INTO part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; + +-- Hypothetical tables +-- ------------------- +-- 0. Dropping any hypothetical object +SELECT * FROM hypopg_reset_index(); +SELECT * FROM hypopg_reset_table(); +-- 1. Range partitioning for hypothetical index +DROP TABLE IF EXISTS hypo_part_range; +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); + +-- Maintenance +-- ----------- +VACUUM ANALYZE; +SELECT * FROM hypopg_analyze('hypo_part_range',100); + + +-- Test hypothetical indexes on hypothetical partitioning behavior +-- =============================================================== + +-- Indexes on root partitioning tree +-- --------------------------------- +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_range (id)'); +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_range(id)'); + +-- Test on real tables +-- ------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_1_10000'; +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_20000_30000'; + +-- Test on hypothetical tables +-- --------------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_1_10000'; +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_20000_30000'; + +-- Indexes on specific partitions +-- ------------------------------ +SELECT * FROM hypopg_reset_index(); + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_range_1_10000 (id)'); +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_range_1_10000(id)'); + +-- Test on real tables +-- ------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_1_10000'; +SELECT COUNT(*) FROM do_explain ('SELECT * FROM part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*part_range_20000_30000'; + +-- Test on hypothetical tables +-- --------------------------- +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id = 42') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_1_10000'; +SELECT COUNT(*) FROM do_explain ('SELECT * FROM hypo_part_range WHERE id > 28000') e +WHERE e ~ 'Index.*<\d+>btree.*hypo_part_range_20000_30000'; + +-- Sanity checks +-- ------------- +SELECT * FROM hypopg_reset_index(); + +-- No hypothetical on non-leaf partition +DROP TABLE IF EXISTS part_multi; +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q2 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); + +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q2', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_multi_1 (dpt)'); +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON part_multi_1_q1 (dpt)'); + +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_multi_1 (dpt)'); +SELECT COUNT(*) AS nb FROM hypopg_create_index('CREATE INDEX ON hypo_part_multi_1_q1 (dpt)'); diff --git a/test/sql/hypo_table_10.sql b/test/sql/hypo_table_10.sql new file mode 100644 index 0000000..42404de --- /dev/null +++ b/test/sql/hypo_table_10.sql @@ -0,0 +1,259 @@ +-- Creating real and hypothetical tables" +-- =====================================" + +-- Real tables +-- ----------- +-- 1. Range partition +DROP TABLE IF EXISTS part_range; +CREATE TABLE part_range (id integer, val text) PARTITION BY RANGE (id); +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +CREATE TABLE part_range_10000_20000 PARTITION OF part_range FOR VALUES FROM (10000) TO (20000); +CREATE TABLE part_range_20000_30000 PARTITION OF part_range FOR VALUES FROM (20000) TO (30000); +INSERT INTO part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +-- 2. List partitioning +DROP TABLE IF EXISTS part_list; +CREATE TABLE part_list (id integer, id_key integer, val text) PARTITION BY LIST (id_key); +CREATE TABLE part_list_4_5_6_8_10 PARTITION OF part_list FOR VALUES IN (4, 5, 6, 8, 10); +CREATE TABLE part_list_7_9 PARTITION OF part_list FOR VALUES IN (7, 9); +CREATE TABLE part_list_1_2_3 PARTITION OF part_list FOR VALUES IN (1, 2, 3); +INSERT INTO part_list SELECT i, (i % 9) + 1, 'line ' || i FROM generate_series(1, 50000) i; +-- 3. Multi level range +DROP TABLE IF EXISTS part_multi; +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_2_q2 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_2_q3 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_2_q4 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q2 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_1_q3 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_1_q4 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +CREATE TABLE part_multi_3 PARTITION OF part_multi FOR VALUES IN (3) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_3_q1 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_3_q2 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$); +CREATE TABLE part_multi_3_q3 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$); +CREATE TABLE part_multi_3_q4 PARTITION OF part_multi_3 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$); +INSERT INTO part_multi select (i%3)+1, '2015-01-01'::date + interval '1 day' * (i%365), 'val ' || i FROM generate_series(1,50000) i; + +-- Hypothetical tables +-- ------------------- +-- 0. Dropping any hypothetical object +SELECT * FROM hypopg_reset(); +-- 1. Range partition +DROP TABLE IF EXISTS hypo_part_range; +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); +-- 2. List partitioning +DROP TABLE IF EXISTS hypo_part_list; +CREATE TABLE hypo_part_list (id integer, id_key integer, val text); +INSERT INTO hypo_part_list SELECT i, (i % 9) + 1, 'line ' || i FROM generate_series(1, 50000) i; +SELECT * FROM hypopg_partition_table('hypo_part_list', 'PARTITION BY LIST (id_key)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_list_4_5_6_8_10', 'PARTITION OF hypo_part_list FOR VALUES IN (4, 5, 6, 8, 10)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_list_7_9', 'PARTITION OF hypo_part_list FOR VALUES IN (7, 9)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_list_1_2_3', 'PARTITION OF hypo_part_list FOR VALUES IN (1, 2, 3)'); +-- 3. Multi level range +DROP TABLE IF EXISTS hypo_part_multi; +CREATE TABLE hypo_part_multi(dpt smallint, dt date, val text); +INSERT INTO hypo_part_multi select (i%3)+1, '2015-01-01'::date + interval '1 day' * (i%365), 'val ' || i FROM generate_series(1,50000) i; +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2', 'PARTITION OF hypo_part_multi FOR VALUES IN (2)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q1', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q2', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q3', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q4', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q2', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q3', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q4', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3', 'PARTITION OF hypo_part_multi FOR VALUES IN (3)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q1', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q2', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-04-01$$) TO ($$2015-07-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q3', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-07-01$$) TO ($$2015-10-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_3_q4', 'PARTITION OF hypo_part_multi_3 FOR VALUES FROM ($$2015-10-01$$) TO ($$2016-01-01$$)'); + +-- Maintenance +-- ----------- +VACUUM ANALYZE; +SELECT * FROM hypopg_analyze('hypo_part_range',100); +SELECT * FROM hypopg_analyze('hypo_part_list',100); +SELECT * FROM hypopg_analyze('hypo_part_multi',100); + + +-- Test deparsing +-- ============== +SELECT relid = rootid AS is_root, tablename, parentid IS NULL parentid_is_null, + parentid IS NOT NULL AS parentid_is_not_null, + partition_by_clause, partition_bounds +FROM hypopg_table() +ORDER BY tablename COLLATE "C"; + +-- Test hypothetical partitioning behavior +-- ======================================= + +-- Simple queries +-- -------------- + +-- Real tables +-- ----------- +-- 1. Range partitioning +EXPLAIN (COSTS OFF) SELECT * FROM part_range; +EXPLAIN (COSTS OFF) SELECT * FROM part_range WHERE id = 42; +EXPLAIN (COSTS OFF) SELECT * FROM part_range WHERE id < 15000; +-- 2. List partitioning +EXPLAIN (COSTS OFF) SELECT * FROM part_list; +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id < 42; +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id < 15000; +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id_key < 5; +EXPLAIN (COSTS OFF) SELECT * FROM part_list WHERE id_key = 7; +-- 3. Multi level range +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM part_multi WHERE dpt = 2; +EXPLAIN (COSTS OFF) SELECT * FROM part_multi WHERE dt >= '2015-01-05' AND dt < '2015-01-10'; + +-- Hypothetical tables +-- ------------------- +-- 1. Range partitioning +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = 42; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id < 15000; +-- 2. List partitioning +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id < 42; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id < 15000; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id_key < 5; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list WHERE id_key = 7; +-- 3. Multi level range +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi WHERE dpt = 2; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi WHERE dt >= '2015-01-05' AND dt < '2015-01-10'; + + +-- Join queries +-- ------------ + +-- Simple joins +-- ------------ +-- 1. Real tables +EXPLAIN (COSTS OFF) SELECT * FROM part_range t1, part_range t2 WHERE t1.id = t2.id and t1.id < 15000; +EXPLAIN (COSTS OFF) SELECT * FROM part_list t1, part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; +EXPLAIN (COSTS OFF) SELECT * FROM part_multi t1, part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; +-- 2. Hypothetical tables +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range t1, hypo_part_range t2 WHERE t1.id = t2.id and t1.id < 15000; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_list t1, hypo_part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi t1, hypo_part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; +-- 3. Real tables and hypothetical tables +EXPLAIN (COSTS OFF) SELECT * FROM part_range t1, hypo_part_range t2 WHERE t1.id = t2.id and t1.id < 15000; +EXPLAIN (COSTS OFF) SELECT * FROM part_list t1, hypo_part_list t2 WHERE t1.id_key = t2.id_key and t1.id_key < 5; +EXPLAIN (COSTS OFF) SELECT * FROM part_multi t1, hypo_part_multi t2 WHERE t1.dpt = t2.dpt and t1.dpt = 2; + +-- Tests for sanity checks +-- ======================= + +-- Duplicate name +CREATE TABLE part_range_1_10000 PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +-- Overlapping range bounds +CREATE TABLE part_range_1_10000_dup PARTITION OF part_range FOR VALUES FROM (1) TO (10000); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000_dup', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +-- Overlapping list bounds +CREATE TABLE part_list_1_2_3_dup PARTITION OF part_list FOR VALUES IN (1, 2, 3); +SELECT tablename FROM hypopg_add_partition('hypo_part_list_1_2_3_dup', 'PARTITION OF hypo_part_list FOR VALUES IN (1, 2, 3)'); +-- Overlapping range bounds, subpartition +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a_dup', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + +-- relcache callback test +-- ====================== + +SELECT tablename FROM hypopg_table() WHERE tablename LIKE 'hypo_part_range%' ORDER BY tablename COLLATE "C"; +DROP TABLE hypo_part_range; +SELECT tablename FROM hypopg_table() WHERE tablename LIKE 'hypo_part_range%' ORDER BY tablename COLLATE "C"; +CREATE TABLE hypo_part_range (id integer, val text); +INSERT INTO hypo_part_range SELECT i, 'line ' || i FROM generate_series(1, 29999) i; +SELECT * FROM hypopg_partition_table('hypo_part_range', 'PARTITION BY RANGE (id)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_10000_20000', 'PARTITION OF hypo_part_range FOR VALUES FROM (10000) TO (20000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_20000_30000', 'PARTITION OF hypo_part_range FOR VALUES FROM (20000) TO (30000)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_range_1_10000', 'PARTITION OF hypo_part_range FOR VALUES FROM (1) TO (10000)'); +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = 42; + +-- no UPDATE/DELETE test +-- ===================== +-- simple UPDATE and DELETE on hypothetically partitioned table +EXPLAIN (COSTS OFF) UPDATE hypo_part_range set id = id; +EXPLAIN DELETE FROM hypo_part_range WHERE id = 42; +-- UPDATE and DELETE on hypothetically partitioned table inside CTE +EXPLAIN (COSTS OFF) WITH s AS (UPDATE hypo_part_range set id = id returning *) SELECT 1; +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM hypo_part_range WHERE id = 42 returning *) SELECT 1; +-- UPDATE and DELETE involving hypothetically partitioned table, but on regular +-- tables +CREATE TABLE foo(id integer); +-- UPDATE on non hypothetically partitioned table but having a hypothetically +-- partitioned table joined +EXPLAIN (COSTS OFF) WITH s AS (UPDATE foo SET id = 0 from hypo_part_range WHERE foo.id = hypo_part_range.id AND hypo_part_range.id > 25000 RETURNING *) SELECT 1; +-- same but with real table +EXPLAIN (COSTS OFF) WITH s AS (UPDATE foo SET id = 0 from part_range WHERE foo.id = part_range.id AND part_range.id > 25000 RETURNING *) SELECT 1; +-- DELETE on non hypothetically partitioned table but having a hypothetically +-- partitioned table joined +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM foo USING hypo_part_range WHERE foo.id = hypo_part_range.id AND hypo_part_range.id = 42 RETURNING *) SELECT 1; +-- same but with real table +EXPLAIN (COSTS OFF) WITH s AS (DELETE FROM foo USING part_range WHERE foo.id = part_range.id AND part_range.id = 42 RETURNING *) SELECT 1; + +-- childless partitioning +-- ====================== +SELECT * FROM hypopg_reset(); +DROP TABLE part_multi; + +CREATE TABLE part_multi(dpt smallint, dt date, val text) PARTITION BY LIST (dpt); +SELECT * FROM hypopg_partition_table('hypo_part_multi', 'PARTITION BY LIST (dpt)'); + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + +CREATE TABLE part_multi_2 PARTITION OF part_multi FOR VALUES IN (2) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_2_q1 PARTITION OF part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1 PARTITION OF part_multi FOR VALUES IN (1) PARTITION BY RANGE(dt); +CREATE TABLE part_multi_1_q1 PARTITION OF part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$) PARTITION BY RANGE (dt); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2', 'PARTITION OF hypo_part_multi FOR VALUES IN (2)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_2_q1', 'PARTITION OF hypo_part_multi_2 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1', 'PARTITION OF hypo_part_multi FOR VALUES IN (1)', 'PARTITION BY RANGE(dt)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1', 'PARTITION OF hypo_part_multi_1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-04-01$$)','PARTITION BY RANGE (dt)'); + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + +-- removing hypothetical partitions +-- ================================ +CREATE TABLE part_multi_1_q1_b PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$); +CREATE TABLE part_multi_1_q1_a PARTITION OF part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_b', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-02-01$$) TO ($$2015-04-01$$)'); +SELECT tablename FROM hypopg_add_partition('hypo_part_multi_1_q1_a', 'PARTITION OF hypo_part_multi_1_q1 FOR VALUES FROM ($$2015-01-01$$) TO ($$2015-02-01$$)'); + +DROP TABLE part_multi_1_q1_a; +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi_1_q1_a'; + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + +DROP TABLE part_multi_1; +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi_1'; + +EXPLAIN (COSTS OFF) SELECT * FROM part_multi; +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + +SELECT hypopg_drop_table(relid) FROM hypopg_table() WHERE tablename = 'hypo_part_multi'; + +EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_multi; + +SELECT hypopg_drop_table(oid) FROM pg_class WHERE relname = 'pg_class'; +SELECT hypopg_drop_table(1);