Commit f8bffe9e authored by Robert Haas's avatar Robert Haas

Fix multi-column range partitioning constraints.

The old logic was just plain wrong.

Report by Olaf Gawenda.  Patch by Amit Langote, reviewed by
Beena Emerson and by me.  Minor adjustments by me also.
parent 4e37b3e1
This diff is collapsed.
......@@ -182,6 +182,20 @@ list_length(const List *l)
(cell1) != NULL && (cell2) != NULL; \
(cell1) = lnext(cell1), (cell2) = lnext(cell2))
/*
* for_both_cell -
* a convenience macro which loops through two lists starting from the
* specified cells of each. This macro loops through both lists at the same
* time, stopping when either list runs out of elements. Depending on the
* requirements of the call site, it may also be wise to assert that the
* lengths of the two lists are equal, and initcell1 and initcell2 are at
* the same position in the respective lists.
*/
#define for_both_cell(cell1, initcell1, cell2, initcell2) \
for ((cell1) = (initcell1), (cell2) = (initcell2); \
(cell1) != NULL && (cell2) != NULL; \
(cell1) = lnext(cell1), (cell2) = lnext(cell2))
/*
* forthree -
* the same for three lists
......
......@@ -1828,3 +1828,93 @@ 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 mcrparted0 partition of mcrparted for values from (unbounded, unbounded, unbounded) 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 (unbounded, unbounded, unbounded);
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
QUERY PLAN
------------------------------
Append
-> Seq Scan on mcrparted0
Filter: (a = 0)
(3 rows)
explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1
QUERY PLAN
---------------------------------------------
Append
-> Seq Scan on mcrparted1
Filter: ((a = 10) AND (abs(b) < 5))
(3 rows)
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2
QUERY PLAN
---------------------------------------------
Append
-> Seq Scan on mcrparted1
Filter: ((a = 10) AND (abs(b) = 5))
-> Seq Scan on mcrparted2
Filter: ((a = 10) AND (abs(b) = 5))
(5 rows)
explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all partitions
QUERY PLAN
------------------------------
Append
-> Seq Scan on mcrparted0
Filter: (abs(b) = 5)
-> Seq Scan on mcrparted1
Filter: (abs(b) = 5)
-> Seq Scan on mcrparted2
Filter: (abs(b) = 5)
-> Seq Scan on mcrparted3
Filter: (abs(b) = 5)
-> Seq Scan on mcrparted5
Filter: (abs(b) = 5)
(11 rows)
explain (costs off) select * from mcrparted where a > -1; -- scans all partitions
QUERY PLAN
-------------------------------------
Append
-> Seq Scan on mcrparted0
Filter: (a > '-1'::integer)
-> Seq Scan on mcrparted1
Filter: (a > '-1'::integer)
-> Seq Scan on mcrparted2
Filter: (a > '-1'::integer)
-> Seq Scan on mcrparted3
Filter: (a > '-1'::integer)
-> Seq Scan on mcrparted4
Filter: (a > '-1'::integer)
-> Seq Scan on mcrparted5
Filter: (a > '-1'::integer)
(13 rows)
explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > 10; -- scans mcrparted4
QUERY PLAN
-----------------------------------------------------------
Append
-> Seq Scan on mcrparted4
Filter: ((c > 10) AND (a = 20) AND (abs(b) = 10))
(3 rows)
explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5
QUERY PLAN
-----------------------------------------
Append
-> Seq Scan on mcrparted3
Filter: ((c > 20) AND (a = 20))
-> Seq Scan on mcrparted4
Filter: ((c > 20) AND (a = 20))
-> Seq Scan on mcrparted5
Filter: ((c > 20) AND (a = 20))
(7 rows)
drop table mcrparted;
......@@ -435,3 +435,62 @@ revoke all on key_desc from someone_else;
revoke all on key_desc_1 from someone_else;
drop role someone_else;
drop table key_desc, key_desc_1;
-- check multi-column range partitioning expression enforces the same
-- constraint as what tuple-routing would determine it to be
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
create table mcrparted0 partition of mcrparted for values from (unbounded, unbounded, unbounded) to (1, unbounded, unbounded);
create table mcrparted1 partition of mcrparted for values from (2, 1, unbounded) to (10, 5, 10);
create table mcrparted2 partition of mcrparted for values from (10, 6, unbounded) to (10, unbounded, unbounded);
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 (21, unbounded, unbounded) to (30, 20, unbounded);
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (unbounded, unbounded, unbounded);
-- routed to mcrparted0
insert into mcrparted values (0, 1, 1);
insert into mcrparted0 values (0, 1, 1);
-- routed to mcparted1
insert into mcrparted values (9, 1000, 1);
insert into mcrparted1 values (9, 1000, 1);
insert into mcrparted values (10, 5, -1);
insert into mcrparted1 values (10, 5, -1);
insert into mcrparted values (2, 1, 0);
insert into mcrparted1 values (2, 1, 0);
-- routed to mcparted2
insert into mcrparted values (10, 6, 1000);
insert into mcrparted2 values (10, 6, 1000);
insert into mcrparted values (10, 1000, 1000);
insert into mcrparted2 values (10, 1000, 1000);
-- no partition exists, nor does mcrparted3 accept it
insert into mcrparted values (11, 1, -1);
ERROR: no partition of relation "mcrparted" found for row
DETAIL: Partition key of the failing row contains (a, abs(b), c) = (11, 1, -1).
insert into mcrparted3 values (11, 1, -1);
ERROR: new row for relation "mcrparted3" violates partition constraint
DETAIL: Failing row contains (11, 1, -1).
-- routed to mcrparted5
insert into mcrparted values (30, 21, 20);
insert into mcrparted5 values (30, 21, 20);
insert into mcrparted4 values (30, 21, 20); -- error
ERROR: new row for relation "mcrparted4" violates partition constraint
DETAIL: Failing row contains (30, 21, 20).
-- check rows
select tableoid::regclass::text, * from mcrparted order by 1;
tableoid | a | b | c
------------+----+------+------
mcrparted0 | 0 | 1 | 1
mcrparted0 | 0 | 1 | 1
mcrparted1 | 9 | 1000 | 1
mcrparted1 | 9 | 1000 | 1
mcrparted1 | 10 | 5 | -1
mcrparted1 | 10 | 5 | -1
mcrparted1 | 2 | 1 | 0
mcrparted1 | 2 | 1 | 0
mcrparted2 | 10 | 6 | 1000
mcrparted2 | 10 | 6 | 1000
mcrparted2 | 10 | 1000 | 1000
mcrparted2 | 10 | 1000 | 1000
mcrparted5 | 30 | 21 | 20
mcrparted5 | 30 | 21 | 20
(14 rows)
-- cleanup
drop table mcrparted;
......@@ -643,3 +643,21 @@ 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 mcrparted0 partition of mcrparted for values from (unbounded, unbounded, unbounded) 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 (unbounded, unbounded, unbounded);
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2
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
drop table mcrparted;
......@@ -289,3 +289,46 @@ revoke all on key_desc from someone_else;
revoke all on key_desc_1 from someone_else;
drop role someone_else;
drop table key_desc, key_desc_1;
-- check multi-column range partitioning expression enforces the same
-- constraint as what tuple-routing would determine it to be
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
create table mcrparted0 partition of mcrparted for values from (unbounded, unbounded, unbounded) to (1, unbounded, unbounded);
create table mcrparted1 partition of mcrparted for values from (2, 1, unbounded) to (10, 5, 10);
create table mcrparted2 partition of mcrparted for values from (10, 6, unbounded) to (10, unbounded, unbounded);
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 (21, unbounded, unbounded) to (30, 20, unbounded);
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (unbounded, unbounded, unbounded);
-- routed to mcrparted0
insert into mcrparted values (0, 1, 1);
insert into mcrparted0 values (0, 1, 1);
-- routed to mcparted1
insert into mcrparted values (9, 1000, 1);
insert into mcrparted1 values (9, 1000, 1);
insert into mcrparted values (10, 5, -1);
insert into mcrparted1 values (10, 5, -1);
insert into mcrparted values (2, 1, 0);
insert into mcrparted1 values (2, 1, 0);
-- routed to mcparted2
insert into mcrparted values (10, 6, 1000);
insert into mcrparted2 values (10, 6, 1000);
insert into mcrparted values (10, 1000, 1000);
insert into mcrparted2 values (10, 1000, 1000);
-- no partition exists, nor does mcrparted3 accept it
insert into mcrparted values (11, 1, -1);
insert into mcrparted3 values (11, 1, -1);
-- routed to mcrparted5
insert into mcrparted values (30, 21, 20);
insert into mcrparted5 values (30, 21, 20);
insert into mcrparted4 values (30, 21, 20); -- error
-- check rows
select tableoid::regclass::text, * from mcrparted order by 1;
-- cleanup
drop table mcrparted;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment