SELECT pg_sleep(random()/10); -- -- Test merge-append plans for inheritance trees -- create table matest0 (id serial primary key, name text); create table matest1 (id integer primary key) inherits (matest0); create table matest2 (id integer primary key) inherits (matest0); create table matest3 (id integer primary key) inherits (matest0); create index matest0i on matest0 ((1-id)); create index matest1i on matest1 ((1-id)); -- create index matest2i on matest2 ((1-id)); -- intentionally missing create index matest3i on matest3 ((1-id)); insert into matest1 (name) values ('Test 1'); insert into matest1 (name) values ('Test 2'); insert into matest2 (name) values ('Test 3'); insert into matest2 (name) values ('Test 4'); insert into matest3 (name) values ('Test 5'); insert into matest3 (name) values ('Test 6'); set enable_indexscan = off; -- force use of seqscan/sort, so no merge explain (verbose, costs off) select * from matest0 order by 1-id; select * from matest0 order by 1-id; explain (verbose, costs off) select min(1-id) from matest0; select min(1-id) from matest0; reset enable_indexscan; set enable_seqscan = off; -- plan with fewest seqscans should be merge set enable_parallel_append = off; -- Don't let parallel-append interfere explain (verbose, costs off) select * from matest0 order by 1-id; select * from matest0 order by 1-id; explain (verbose, costs off) select min(1-id) from matest0; select min(1-id) from matest0; reset enable_seqscan; reset enable_parallel_append; drop table matest0 cascade; -- -- Check that use of an index with an extraneous column doesn't produce -- a plan with extraneous sorting -- create table matest0 (a int, b int, c int, d int); create table matest1 () inherits(matest0); create index matest0i on matest0 (b, c); create index matest1i on matest1 (b, c); set enable_nestloop = off; -- we want a plan with two MergeAppends explain (costs off) select t1.* from matest0 t1, matest0 t2 where t1.b = t2.b and t2.c = t2.d order by t1.b limit 10; reset enable_nestloop; drop table matest0 cascade; -- -- Test merge-append for UNION ALL append relations -- set enable_seqscan = off; set enable_indexscan = on; set enable_bitmapscan = off; -- exercise rescan code path via a repeatedly-evaluated subquery explain (costs off) SELECT ARRAY(SELECT f.i FROM ( (SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL (SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1) ) f(i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g(i); SELECT ARRAY(SELECT f.i FROM ( (SELECT d + g.i FROM generate_series(4, 30, 3) d ORDER BY 1) UNION ALL (SELECT d + g.i FROM generate_series(0, 30, 5) d ORDER BY 1) ) f(i) ORDER BY f.i LIMIT 10) FROM generate_series(1, 3) g(i); reset enable_seqscan; reset enable_indexscan; reset enable_bitmapscan; -- -- Check handling of a constant-null CHECK constraint -- create table cnullparent (f1 int); create table cnullchild (check (f1 = 1 or f1 = null)) inherits(cnullparent); insert into cnullchild values(1); insert into cnullchild values(2); insert into cnullchild values(null); select * from cnullparent; select * from cnullparent where f1 = 2; drop table cnullparent cascade; -- -- Check use of temporary tables with inheritance trees -- create table inh_perm_parent (a1 int); create temp table inh_temp_parent (a1 int); create temp table inh_temp_child () inherits (inh_perm_parent); -- ok create temp table inh_temp_child_2 () inherits (inh_temp_parent); -- ok insert into inh_perm_parent values (1); insert into inh_temp_parent values (2); insert into inh_temp_child values (3); insert into inh_temp_child_2 values (4); select tableoid::regclass, a1 from inh_perm_parent; select tableoid::regclass, a1 from inh_temp_parent; drop table inh_perm_parent cascade; drop table inh_temp_parent cascade; -- -- Check that constraint exclusion works correctly with partitions using -- implicit constraints generated from the partition bound information. -- create table list_parted ( a varchar ) partition by list (a); create table part_ab_cd partition of list_parted for values in ('ab', 'cd'); create table part_ef_gh partition of list_parted for values in ('ef', 'gh'); create table part_null_xy partition of list_parted for values in (null, 'xy'); explain (costs off) select * from list_parted; explain (costs off) select * from list_parted where a is null; explain (costs off) select * from list_parted where a is not null; explain (costs off) select * from list_parted where a in ('ab', 'cd', 'ef'); explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd'); explain (costs off) select * from list_parted where a = 'ab'; create table range_list_parted ( a int, b char(2) ) partition by range (a); create table part_1_10 partition of range_list_parted for values from (1) to (10) partition by list (b); create table part_1_10_ab partition of part_1_10 for values in ('ab'); create table part_1_10_cd partition of part_1_10 for values in ('cd'); create table part_10_20 partition of range_list_parted for values from (10) to (20) partition by list (b); create table part_10_20_ab partition of part_10_20 for values in ('ab'); create table part_10_20_cd partition of part_10_20 for values in ('cd'); create table part_21_30 partition of range_list_parted for values from (21) to (30) partition by list (b); create table part_21_30_ab partition of part_21_30 for values in ('ab'); create table part_21_30_cd partition of part_21_30 for values in ('cd'); create table part_40_inf partition of range_list_parted for values from (40) to (maxvalue) partition by list (b); create table part_40_inf_ab partition of part_40_inf for values in ('ab'); create table part_40_inf_cd partition of part_40_inf for values in ('cd'); create table part_40_inf_null partition of part_40_inf for values in (null); explain (costs off) select * from range_list_parted; explain (costs off) select * from range_list_parted where a = 5; explain (costs off) select * from range_list_parted where b = 'ab'; explain (costs off) select * from range_list_parted where a between 3 and 23 and b in ('ab'); /* Should select no rows because range partition key cannot be null */ explain (costs off) select * from range_list_parted where a is null; /* Should only select rows from the null-accepting partition */ explain (costs off) select * from range_list_parted where b is null; explain (costs off) select * from range_list_parted where a is not null and a < 67; explain (costs off) select * from range_list_parted where a >= 30; drop table list_parted; drop table range_list_parted; -- check that constraint exclusion is able to cope with the partition -- constraint emitted for multi-column range partitioned tables create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c); create table mcrparted_def partition of mcrparted default; create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1); create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10); create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10); create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10); create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20); create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0, mcrparted_def explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1, mcrparted_def explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2, mcrparted_def explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all partitions explain (costs off) select * from mcrparted where a > -1; -- scans all partitions explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > 10; -- scans mcrparted4 explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def -- check that partitioned table Appends cope with being referenced in -- subplans create table parted_minmax (a int, b varchar(16)) partition by range (a); create table parted_minmax1 partition of parted_minmax for values from (1) to (10); create index parted_minmax1i on parted_minmax1 (a, b); insert into parted_minmax values (1,'12345'); explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; select min(a), max(a) from parted_minmax where b = '12345'; drop table parted_minmax; -- Test code that uses Append nodes in place of MergeAppend when the -- partition ordering matches the desired ordering. create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c); -- MergeAppend must be used when a default partition exists explain (costs off) select * from mcrparted order by a, abs(b), c; drop table mcrparted_def; -- Append is used for a RANGE partitioned table with no default -- and no subpartitions explain (costs off) select * from mcrparted order by a, abs(b), c; -- Append is used with subpaths in reverse order with backwards index scans explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc; -- check that Append plan is used containing a MergeAppend for sub-partitions -- that are unordered. drop table mcrparted5; create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a); create table mcrparted5a partition of mcrparted5 for values in(20); create table mcrparted5_def partition of mcrparted5 default; explain (costs off) select * from mcrparted order by a, abs(b), c; drop table mcrparted5_def; -- check that an Append plan is used and the sub-partitions are flattened -- into the main Append when the sub-partition is unordered but contains -- just a single sub-partition. explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; -- check that Append is used when the sub-partitioned tables are pruned -- during planning. explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; create table mclparted (a int) partition by list(a); create table mclparted1 partition of mclparted for values in(1); create table mclparted2 partition of mclparted for values in(2); create index on mclparted (a); -- Ensure an Append is used for a list partition with an order by. explain (costs off) select * from mclparted order by a; -- Ensure a MergeAppend is used when a partition exists with interleaved -- datums in the partition bound. create table mclparted3_5 partition of mclparted for values in(3,5); create table mclparted4 partition of mclparted for values in(4); explain (costs off) select * from mclparted order by a; drop table mclparted; -- Ensure subplans which don't have a path with the correct pathkeys get -- sorted correctly. drop index mcrparted_a_abs_c_idx; create index on mcrparted1 (a, abs(b), c); create index on mcrparted2 (a, abs(b), c); create index on mcrparted3 (a, abs(b), c); create index on mcrparted4 (a, abs(b), c); explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1; set enable_bitmapscan = 0; -- Ensure Append node can be used when the partition is ordered by some -- pathkeys which were deemed redundant. explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c; reset enable_bitmapscan; drop table mcrparted; -- Ensure LIST partitions allow an Append to be used instead of a MergeAppend create table bool_lp (b bool) partition by list(b); create table bool_lp_true partition of bool_lp for values in(true); create table bool_lp_false partition of bool_lp for values in(false); create index on bool_lp (b); explain (costs off) select * from bool_lp order by b; drop table bool_lp; -- Ensure const bool quals can be properly detected as redundant create table bool_rp (b bool, a int) partition by range(b,a); create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000); create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000); create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000); create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000); create index on bool_rp (b,a); explain (costs off) select * from bool_rp where b = true order by b,a; explain (costs off) select * from bool_rp where b = false order by b,a; explain (costs off) select * from bool_rp where b = true order by a; explain (costs off) select * from bool_rp where b = false order by a; drop table bool_rp; -- Ensure an Append scan is chosen when the partition order is a subset of -- the required order. create table range_parted (a int, b int, c int) partition by range(a, b); create table range_parted1 partition of range_parted for values from (0,0) to (10,10); create table range_parted2 partition of range_parted for values from (10,10) to (20,20); create index on range_parted (a,b,c); explain (costs off) select * from range_parted order by a,b,c; explain (costs off) select * from range_parted order by a desc,b desc,c desc; drop table range_parted; -- Check that we allow access to a child table's statistics when the user -- has permissions only for the parent table. create table permtest_parent (a int, b text, c text) partition by list (a); create table permtest_child (b text, c text, a int) partition by list (b); create table permtest_grandchild (c text, b text, a int); alter table permtest_child attach partition permtest_grandchild for values in ('a'); alter table permtest_parent attach partition permtest_child for values in (1); create index on permtest_parent (left(c, 3)); insert into permtest_parent select 1, 'a', left(md5(i::text), 5) from generate_series(0, 100) i; analyze permtest_parent; create role regress_no_child_access; revoke all on permtest_grandchild from regress_no_child_access; grant select on permtest_parent to regress_no_child_access; set session authorization regress_no_child_access; -- without stats access, these queries would produce hash join plans: explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and left(p1.c, 3) ~ 'a1$'; reset session authorization; revoke all on permtest_parent from regress_no_child_access; grant select(a,c) on permtest_parent to regress_no_child_access; set session authorization regress_no_child_access; explain (costs off) select p2.a, p1.c from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; -- we will not have access to the expression index's stats here: explain (costs off) select p2.a, p1.c from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and left(p1.c, 3) ~ 'a1$'; reset session authorization; revoke all on permtest_parent from regress_no_child_access; drop role regress_no_child_access; drop table permtest_parent; -- Verify that constraint errors across partition root / child are -- handled correctly (Bug #16293) CREATE TABLE errtst_parent ( partid int not null, shdata int not null, data int NOT NULL DEFAULT 0, CONSTRAINT shdata_small CHECK(shdata < 3) ) PARTITION BY RANGE (partid); -- fast defaults lead to attribute mapping being used in one -- direction, but not the other CREATE TABLE errtst_child_fastdef ( partid int not null, shdata int not null, CONSTRAINT shdata_small CHECK(shdata < 3) ); -- no remapping in either direction necessary CREATE TABLE errtst_child_plaindef ( partid int not null, shdata int not null, data int NOT NULL DEFAULT 0, CONSTRAINT shdata_small CHECK(shdata < 3), CHECK(data < 10) ); -- remapping in both direction CREATE TABLE errtst_child_reorder ( data int NOT NULL DEFAULT 0, shdata int not null, partid int not null, CONSTRAINT shdata_small CHECK(shdata < 3), CHECK(data < 10) ); ALTER TABLE errtst_child_fastdef ADD COLUMN data int NOT NULL DEFAULT 0; ALTER TABLE errtst_child_fastdef ADD CONSTRAINT errtest_child_fastdef_data_check CHECK (data < 10); ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_fastdef FOR VALUES FROM (0) TO (10); ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_plaindef FOR VALUES FROM (10) TO (20); ALTER TABLE errtst_parent ATTACH PARTITION errtst_child_reorder FOR VALUES FROM (20) TO (30); -- insert without child check constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '5'); INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '5'); INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '5'); -- within partition update without child check constraint violation BEGIN; UPDATE errtst_parent SET data = data + 1 WHERE partid = 0; UPDATE errtst_parent SET data = data + 1 WHERE partid = 10; UPDATE errtst_parent SET data = data + 1 WHERE partid = 20; ROLLBACK; -- direct leaf partition update, without partition id violation BEGIN; UPDATE errtst_child_fastdef SET partid = 1 WHERE partid = 0; UPDATE errtst_child_plaindef SET partid = 11 WHERE partid = 10; UPDATE errtst_child_reorder SET partid = 21 WHERE partid = 20; ROLLBACK; -- partition move, without child check constraint violation BEGIN; UPDATE errtst_parent SET partid = 10, data = data + 1 WHERE partid = 0; UPDATE errtst_parent SET partid = 20, data = data + 1 WHERE partid = 10; UPDATE errtst_parent SET partid = 0, data = data + 1 WHERE partid = 20; ROLLBACK; DROP TABLE errtst_parent;