Skip to content

Commit 9361f6f

Browse files
committed
After a MINVALUE/MAXVALUE bound, allow only more of the same.
In the old syntax, which used UNBOUNDED, we had a similar restriction, but commit d363d42, which changed the syntax, eliminated it. Put it back. Patch by me, reviewed by Dean Rasheed. Discussion: http://postgr.es/m/CA+Tgmobs+pLPC27tS3gOpEAxAffHrq5w509cvkwTf9pF6cWYbg@mail.gmail.com
1 parent c29145f commit 9361f6f

File tree

8 files changed

+102
-37
lines changed

8 files changed

+102
-37
lines changed

doc/src/sgml/ref/create_table.sgml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
324324
</para>
325325

326326
<para>
327-
Note that any values after <literal>MINVALUE</> or
328-
<literal>MAXVALUE</> in a partition bound are ignored; so the bound
329-
<literal>(10, MINVALUE, 0)</> is equivalent to
330-
<literal>(10, MINVALUE, 10)</> and <literal>(10, MINVALUE, MINVALUE)</>
331-
and <literal>(10, MINVALUE, MAXVALUE)</>.
327+
Note that if <literal>MINVALUE</> or <literal>MAXVALUE</> is used for
328+
one column of a partitioning bound, the same value must be used for all
329+
subsequent columns. For example, <literal>(10, MINVALUE, 0)</> is not
330+
a valid bound; you should write <literal>(10, MINVALUE, MINVALUE)</>.
332331
</para>
333332

334333
<para>
@@ -1665,7 +1664,7 @@ CREATE TABLE measurement_y2016m07
16651664
<programlisting>
16661665
CREATE TABLE measurement_ym_older
16671666
PARTITION OF measurement_year_month
1668-
FOR VALUES FROM (MINVALUE, 0) TO (2016, 11);
1667+
FOR VALUES FROM (MINVALUE, MINVALUE) TO (2016, 11);
16691668

16701669
CREATE TABLE measurement_ym_y2016m11
16711670
PARTITION OF measurement_year_month

src/backend/parser/parse_utilcmd.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ static void transformConstraintAttrs(CreateStmtContext *cxt,
135135
static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
136136
static void setSchemaName(char *context_schema, char **stmt_schema_name);
137137
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd);
138+
static void validateInfiniteBounds(ParseState *pstate, List *blist);
138139
static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con,
139140
const char *colName, Oid colType, int32 colTypmod);
140141

@@ -3397,6 +3398,13 @@ transformPartitionBound(ParseState *pstate, Relation parent,
33973398
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
33983399
errmsg("TO must specify exactly one value per partitioning column")));
33993400

3401+
/*
3402+
* Once we see MINVALUE or MAXVALUE for one column, the remaining
3403+
* columns must be the same.
3404+
*/
3405+
validateInfiniteBounds(pstate, spec->lowerdatums);
3406+
validateInfiniteBounds(pstate, spec->upperdatums);
3407+
34003408
/* Transform all the constants */
34013409
i = j = 0;
34023410
result_spec->lowerdatums = result_spec->upperdatums = NIL;
@@ -3468,6 +3476,46 @@ transformPartitionBound(ParseState *pstate, Relation parent,
34683476
return result_spec;
34693477
}
34703478

3479+
/*
3480+
* validateInfiniteBounds
3481+
*
3482+
* Check that a MAXVALUE or MINVALUE specification in a partition bound is
3483+
* followed only by more of the same.
3484+
*/
3485+
static void
3486+
validateInfiniteBounds(ParseState *pstate, List *blist)
3487+
{
3488+
ListCell *lc;
3489+
PartitionRangeDatumKind kind = PARTITION_RANGE_DATUM_VALUE;
3490+
3491+
foreach(lc, blist)
3492+
{
3493+
PartitionRangeDatum *prd = castNode(PartitionRangeDatum, lfirst(lc));
3494+
3495+
if (kind == prd->kind)
3496+
continue;
3497+
3498+
switch (kind)
3499+
{
3500+
case PARTITION_RANGE_DATUM_VALUE:
3501+
kind = prd->kind;
3502+
break;
3503+
3504+
case PARTITION_RANGE_DATUM_MAXVALUE:
3505+
ereport(ERROR,
3506+
(errcode(ERRCODE_DATATYPE_MISMATCH),
3507+
errmsg("every bound following MAXVALUE must also be MAXVALUE"),
3508+
parser_errposition(pstate, exprLocation((Node *) prd))));
3509+
3510+
case PARTITION_RANGE_DATUM_MINVALUE:
3511+
ereport(ERROR,
3512+
(errcode(ERRCODE_DATATYPE_MISMATCH),
3513+
errmsg("every bound following MINVALUE must also be MINVALUE"),
3514+
parser_errposition(pstate, exprLocation((Node *) prd))));
3515+
}
3516+
}
3517+
}
3518+
34713519
/*
34723520
* Transform one constant in a partition bound spec
34733521
*/

src/test/regress/expected/create_table.out

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,27 +723,27 @@ Number of partitions: 3 (Use \d+ to list them.)
723723

724724
-- check that we get the expected partition constraints
725725
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
726-
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
726+
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
727727
\d+ unbounded_range_part
728728
Table "public.unbounded_range_part"
729729
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
730730
--------+---------+-----------+----------+---------+---------+--------------+-------------
731731
a | integer | | | | plain | |
732732
b | integer | | | | plain | |
733733
c | integer | | | | plain | |
734-
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0)
734+
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE)
735735
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
736736

737737
DROP TABLE unbounded_range_part;
738-
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
738+
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
739739
\d+ range_parted4_1
740740
Table "public.range_parted4_1"
741741
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
742742
--------+---------+-----------+----------+---------+---------+--------------+-------------
743743
a | integer | | | | plain | |
744744
b | integer | | | | plain | |
745745
c | integer | | | | plain | |
746-
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0)
746+
Partition of: range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE)
747747
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
748748

749749
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
@@ -757,15 +757,15 @@ CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5
757757
Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE)
758758
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
759759

760-
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0);
760+
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
761761
\d+ range_parted4_3
762762
Table "public.range_parted4_3"
763763
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
764764
--------+---------+-----------+----------+---------+---------+--------------+-------------
765765
a | integer | | | | plain | |
766766
b | integer | | | | plain | |
767767
c | integer | | | | plain | |
768-
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0)
768+
Partition of: range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE)
769769
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
770770

771771
DROP TABLE range_parted4;

src/test/regress/expected/inherit.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,12 +1853,12 @@ drop table range_list_parted;
18531853
-- check that constraint exclusion is able to cope with the partition
18541854
-- constraint emitted for multi-column range partitioned tables
18551855
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
1856-
create table mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, 1, 1);
1856+
create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1);
18571857
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
18581858
create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10);
18591859
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10);
18601860
create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20);
1861-
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, 0, 0);
1861+
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue);
18621862
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
18631863
QUERY PLAN
18641864
------------------------------

src/test/regress/expected/insert.out

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -574,15 +574,28 @@ revoke all on key_desc from someone_else;
574574
revoke all on key_desc_1 from someone_else;
575575
drop role someone_else;
576576
drop table key_desc, key_desc_1;
577+
-- test minvalue/maxvalue restrictions
578+
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
579+
create table mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, maxvalue, maxvalue);
580+
ERROR: every bound following MINVALUE must also be MINVALUE
581+
LINE 1: ...partition of mcrparted for values from (minvalue, 0, 0) to (...
582+
^
583+
create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, minvalue);
584+
ERROR: every bound following MAXVALUE must also be MAXVALUE
585+
LINE 1: ...r values from (10, 6, minvalue) to (10, maxvalue, minvalue);
586+
^
587+
create table mcrparted4 partition of mcrparted for values from (21, minvalue, 0) to (30, 20, minvalue);
588+
ERROR: every bound following MINVALUE must also be MINVALUE
589+
LINE 1: ...ition of mcrparted for values from (21, minvalue, 0) to (30,...
590+
^
577591
-- check multi-column range partitioning expression enforces the same
578592
-- constraint as what tuple-routing would determine it to be
579-
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
580-
create table mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, maxvalue, 0);
593+
create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, maxvalue, maxvalue);
581594
create table mcrparted1 partition of mcrparted for values from (2, 1, minvalue) to (10, 5, 10);
582-
create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, 0);
595+
create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, maxvalue);
583596
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10);
584-
create table mcrparted4 partition of mcrparted for values from (21, minvalue, 0) to (30, 20, maxvalue);
585-
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (maxvalue, 0, 0);
597+
create table mcrparted4 partition of mcrparted for values from (21, minvalue, minvalue) to (30, 20, maxvalue);
598+
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (maxvalue, maxvalue, maxvalue);
586599
-- routed to mcrparted0
587600
insert into mcrparted values (0, 1, 1);
588601
insert into mcrparted0 values (0, 1, 1);
@@ -666,37 +679,37 @@ drop table brtrigpartcon;
666679
drop function brtrigpartcon1trigf();
667680
-- check multi-column range partitioning with minvalue/maxvalue constraints
668681
create table mcrparted (a text, b int) partition by range(a, b);
669-
create table mcrparted1_lt_b partition of mcrparted for values from (minvalue, 0) to ('b', minvalue);
682+
create table mcrparted1_lt_b partition of mcrparted for values from (minvalue, minvalue) to ('b', minvalue);
670683
create table mcrparted2_b partition of mcrparted for values from ('b', minvalue) to ('c', minvalue);
671684
create table mcrparted3_c_to_common partition of mcrparted for values from ('c', minvalue) to ('common', minvalue);
672685
create table mcrparted4_common_lt_0 partition of mcrparted for values from ('common', minvalue) to ('common', 0);
673686
create table mcrparted5_common_0_to_10 partition of mcrparted for values from ('common', 0) to ('common', 10);
674687
create table mcrparted6_common_ge_10 partition of mcrparted for values from ('common', 10) to ('common', maxvalue);
675688
create table mcrparted7_gt_common_lt_d partition of mcrparted for values from ('common', maxvalue) to ('d', minvalue);
676-
create table mcrparted8_ge_d partition of mcrparted for values from ('d', minvalue) to (maxvalue, 0);
689+
create table mcrparted8_ge_d partition of mcrparted for values from ('d', minvalue) to (maxvalue, maxvalue);
677690
\d+ mcrparted
678691
Table "public.mcrparted"
679692
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
680693
--------+---------+-----------+----------+---------+----------+--------------+-------------
681694
a | text | | | | extended | |
682695
b | integer | | | | plain | |
683696
Partition key: RANGE (a, b)
684-
Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE),
697+
Partitions: mcrparted1_lt_b FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE),
685698
mcrparted2_b FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE),
686699
mcrparted3_c_to_common FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE),
687700
mcrparted4_common_lt_0 FOR VALUES FROM ('common', MINVALUE) TO ('common', 0),
688701
mcrparted5_common_0_to_10 FOR VALUES FROM ('common', 0) TO ('common', 10),
689702
mcrparted6_common_ge_10 FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE),
690703
mcrparted7_gt_common_lt_d FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE),
691-
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
704+
mcrparted8_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
692705

693706
\d+ mcrparted1_lt_b
694707
Table "public.mcrparted1_lt_b"
695708
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
696709
--------+---------+-----------+----------+---------+----------+--------------+-------------
697710
a | text | | | | extended | |
698711
b | integer | | | | plain | |
699-
Partition of: mcrparted FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE)
712+
Partition of: mcrparted FOR VALUES FROM (MINVALUE, MINVALUE) TO ('b', MINVALUE)
700713
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
701714

702715
\d+ mcrparted2_b
@@ -759,7 +772,7 @@ Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::te
759772
--------+---------+-----------+----------+---------+----------+--------------+-------------
760773
a | text | | | | extended | |
761774
b | integer | | | | plain | |
762-
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
775+
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, MAXVALUE)
763776
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
764777

765778
insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),

src/test/regress/sql/create_table.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,14 +641,14 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
641641

642642
-- check that we get the expected partition constraints
643643
CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
644-
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
644+
CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (MAXVALUE, MAXVALUE, MAXVALUE);
645645
\d+ unbounded_range_part
646646
DROP TABLE unbounded_range_part;
647-
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
647+
CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (MINVALUE, MINVALUE, MINVALUE) TO (1, MAXVALUE, MAXVALUE);
648648
\d+ range_parted4_1
649649
CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, MAXVALUE);
650650
\d+ range_parted4_2
651-
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, 0);
651+
CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, MINVALUE) TO (9, MAXVALUE, MAXVALUE);
652652
\d+ range_parted4_3
653653
DROP TABLE range_parted4;
654654

src/test/regress/sql/inherit.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,12 +664,12 @@ drop table range_list_parted;
664664
-- check that constraint exclusion is able to cope with the partition
665665
-- constraint emitted for multi-column range partitioned tables
666666
create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c);
667-
create table mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, 1, 1);
667+
create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1);
668668
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
669669
create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10);
670670
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10);
671671
create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20);
672-
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, 0, 0);
672+
create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue);
673673
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
674674
explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1
675675
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy