From 52574b20e53e5da94ffe256ae30b0bb19503659a Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 29 Jun 2017 13:50:27 +0900 Subject: [PATCH 2/2] Relax some rules about unbounded range partition bounds Currently, unbounded means - or + infinity depending on whether it appears in FROM or TO, respectively. That rule limits the usefulness of being able to specify unboundedness for range partition columns at all. For example, in case of multi-column partition key, one cannot specify the same bound value as both a partition's upper bound and the next partition's lower bound to establish them as contiguous non-overlapping partitions, if there is unbounded value in the suffix of the bound tuple, because such a specification would be considered to be overlapping with the current rule. Adjust syntax to allow an explicit sign to be specified for unbounded values. Also, replace the 'unbounded' keyword with 'infinity', because that's what seems to make sense with a sign. Adjust the logic in get_qual_for_range() to consider the possibility that an infinity in a lower bound could really be +infinity and one in an upper bound could be -infinity. Add tests for the new syntax, tuple-routing, and partition constraint checking for the new use cases allowed by the relaxed syntax. --- doc/src/sgml/ref/create_table.sgml | 16 +++--- src/backend/catalog/partition.c | 74 +++++++++++++++++++--------- src/backend/parser/gram.y | 36 ++++++++------ src/backend/parser/parse_utilcmd.c | 42 +++++++++++----- src/backend/utils/adt/ruleutils.c | 22 ++++++++- src/include/nodes/parsenodes.h | 8 +-- src/include/parser/kwlist.h | 1 + src/test/regress/expected/create_table.out | 79 +++++++++++++++++++++--------- src/test/regress/expected/inherit.out | 6 +-- src/test/regress/expected/insert.out | 61 ++++++++++++++++++++--- src/test/regress/sql/create_table.sql | 50 +++++++++++++------ src/test/regress/sql/inherit.sql | 6 +-- src/test/regress/sql/insert.sql | 34 ++++++++++--- 13 files changed, 315 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index b15c19d3d0..d146291d9a 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -87,8 +87,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI and partition_bound_spec is: IN ( { numeric_literal | string_literal | NULL } [, ...] ) | -FROM ( { numeric_literal | string_literal | UNBOUNDED } [, ...] ) - TO ( { numeric_literal | string_literal | UNBOUNDED } [, ...] ) +FROM ( { numeric_literal | string_literal | [ + | - ] infinity } [, ...] ) + TO ( { numeric_literal | string_literal | [ + | - ] infinity } [, ...] ) index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are: @@ -300,13 +300,11 @@ FROM ( { numeric_literal | - Writing UNBOUNDED in FROM - signifies -infinity as the lower bound of the - corresponding column, whereas when written in TO, - it signifies +infinity as the upper bound. - All items following an UNBOUNDED item within - a FROM or TO list must also - be UNBOUNDED. + In the absence of an explicit sign, infinity specified in the + FROM list signifies -infinity, whereas +infinity + if specified in the TO. Note that one cannot + specify a finite value after specifying (+/-) infinity in either + the FROM or the TO list. diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 457b2fef66..0e2b60e5a8 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -377,15 +377,13 @@ RelationBuildPartitionDesc(Relation rel) } /* - * If either of them has infinite element, we can't equate - * them. Even when both are infinite, they'd have - * opposite signs, because only one of cur and prev is a - * lower bound). + * If either of them has infinite element, we can't invoke + * the comparison procedure. */ if (cur->content[j] != RANGE_DATUM_FINITE || prev->content[j] != RANGE_DATUM_FINITE) { - is_distinct = true; + is_distinct = (cur->content[j] != prev->content[j]); break; } cmpval = FunctionCall2Coll(&key->partsupfunc[j], @@ -1392,7 +1390,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) * * Constructs an Expr for the key column (returned in *keyCol) and Consts * for the lower and upper range limits (returned in *lower_val and - * *upper_val). For UNBOUNDED limits, NULL is returned instead of a Const. + * *upper_val). For infinite limits, NULL is returned instead of a Const. * All of these structures are freshly palloc'd. * * *partexprs_item points to the cell containing the next expression in @@ -1464,17 +1462,21 @@ get_range_key_properties(PartitionKey key, int keynum, * AND * (b < bu) OR (b = bu AND c < cu)) * - * If cu happens to be UNBOUNDED, we need not emit any expression for it, so - * the last line would be: + * If cl happens to be -infinity, we need not emit any expression for it, so + * the AND sub-expression corresponding to the lower bound would be: * - * (b < bu) OR (b = bu), which is simplified to (b <= bu) + * (b > bl) OR (b = bl), which is simplified to (b >= bl) * - * In most common cases with only one partition column, say a, the following - * expression tree will be generated: a IS NOT NULL AND a >= al AND a < au + * But if cl were to be +infinity (because the range partition bound syntax + * allows that), no row with b == bl would qualify for this partition, because + * c >= +infinity will never be true. So, the expression would simply be: + * b > bl. Similarly, if cu happens to be -infinity, the AND sub-expression + * for the upper bound would be b < bu, because no row with b = bu would + * qualify for this partition. * - * If all values of both lower and upper bounds are UNBOUNDED, the partition - * does not really have a constraint, except the IS NOT NULL constraint for - * partition keys. + * In most common cases with only one partition column, say a, the following + * expression tree will be generated: a IS NOT NULL AND a >= al AND a < au, + * provided both al and au are finite values. * * If we end up with an empty result list, we return a single-member list * containing a constant TRUE, because callers expect a non-empty list. @@ -1658,15 +1660,24 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) if (need_next_lower_arm && lower_val) { uint16 strategy; + DefElem *inf; + bool next_is_neg_inf = false; + + if (ldatum_next && ldatum_next->infinite) + { + inf = castNode(DefElem, ldatum_next->value); + if (strcmp(inf->defname, "negative_infinity") == 0) + next_is_neg_inf = true; + } /* * For the non-last columns of this arm, use the EQ operator. - * For the last or the last finite-valued column, use GE. + * For the last or the last finite-valued column (provided the + * next infinite column is actually -infinity), use GE. */ if (j - i < current_or_arm) strategy = BTEqualStrategyNumber; - else if ((ldatum_next && ldatum_next->infinite) || - j == key->partnatts - 1) + else if (next_is_neg_inf || j == key->partnatts - 1) strategy = BTGreaterEqualStrategyNumber; else strategy = BTGreaterStrategyNumber; @@ -1681,14 +1692,24 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) if (need_next_upper_arm && upper_val) { uint16 strategy; + DefElem *inf; + bool next_is_pos_inf = false; + + if (udatum_next && udatum_next->infinite) + { + inf = castNode(DefElem, udatum_next->value); + if (strcmp(inf->defname, "positive_infinity") == 0) + next_is_pos_inf = true; + } /* * For the non-last columns of this arm, use the EQ operator. - * For the last finite-valued column, use LE. + * For the last finite-valued column (provided the next + * infinite column is actually +infinity), use LE. */ if (j - i < current_or_arm) strategy = BTEqualStrategyNumber; - else if (udatum_next && udatum_next->infinite) + else if (next_is_pos_inf) strategy = BTLessEqualStrategyNumber; else strategy = BTLessStrategyNumber; @@ -2093,12 +2114,19 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) foreach(lc, datums) { PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc)); + DefElem *inf; /* What's contained in this range datum? */ - bound->content[i] = !datum->infinite - ? RANGE_DATUM_FINITE - : (lower ? RANGE_DATUM_NEG_INF - : RANGE_DATUM_POS_INF); + if (!datum->infinite) + bound->content[i] = RANGE_DATUM_FINITE; + else + { + inf = castNode(DefElem, datum->value); + if (strcmp(inf->defname, "negative_infinity") == 0) + bound->content[i] = RANGE_DATUM_NEG_INF; + else + bound->content[i] = RANGE_DATUM_POS_INF; + } if (bound->content[i] == RANGE_DATUM_FINITE) { diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0f3998ff89..111f35ce8b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -634,8 +634,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); HANDLER HAVING HEADER_P HOLD HOUR_P IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P - INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P - INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER + INCLUDING INCREMENT INDEX INDEXES INFINITY INHERIT INHERITS INITIALLY + INLINE_P INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION JOIN @@ -2681,6 +2681,23 @@ partbound_datum: Sconst { $$ = makeStringConst($1, @1); } | NumericOnly { $$ = makeAConst($1, @1); } | NULL_P { $$ = makeNullAConst(@1); } + | '-' INFINITY + { + $$ = (Node *) makeDefElem("negative_infinity", NULL, @1); + } + | '+' INFINITY + { + $$ = (Node *) makeDefElem("positive_infinity", NULL, @1); + } + /* + * If a user skips the sign, whether it's the negative or the + * positive infinity is determined during the parse analysis + * by seeing which of FROM and TO lists the bound is in. + */ + | INFINITY + { + $$ = (Node *) makeDefElem("infinity", NULL, @1); + } ; partbound_datum_list: @@ -2696,21 +2713,11 @@ range_datum_list: ; PartitionRangeDatum: - UNBOUNDED - { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->infinite = true; - n->value = NULL; - n->location = @1; - - $$ = (Node *) n; - } - | partbound_datum + partbound_datum { PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - n->infinite = false; + n->infinite = IsA($1, DefElem); n->value = $1; n->location = @1; @@ -14714,6 +14721,7 @@ unreserved_keyword: | INCREMENT | INDEX | INDEXES + | INFINITY | INHERIT | INHERITS | INLINE_P diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index ee5f3a3a52..2734677855 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -3365,7 +3365,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, *cell2; int i, j; - bool seen_unbounded; + bool seen_infinity; if (spec->strategy != PARTITION_STRATEGY_RANGE) ereport(ERROR, @@ -3383,35 +3383,51 @@ transformPartitionBound(ParseState *pstate, Relation parent, errmsg("TO must specify exactly one value per partitioning column"))); /* - * Check that no finite value follows an UNBOUNDED item in either of - * lower and upper bound lists. + * Check that no finite value follows an infinity value in either of + * lower and upper bound lists. While at it, convert the "infinite" + * elements in the lowerdatums list into "negative_infinity" and those + * in the upperdatums list into "positive_infinity". */ - seen_unbounded = false; + seen_infinity = false; foreach(cell1, spec->lowerdatums) { PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum, lfirst(cell1)); if (ldatum->infinite) - seen_unbounded = true; - else if (seen_unbounded) + { + DefElem *inf; + + seen_infinity = true; + inf = castNode(DefElem, ldatum->value); + if (strcmp(inf->defname, "infinity") == 0) + inf->defname = pstrdup("negative_infinity"); + } + else if (seen_infinity) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot specify finite value after UNBOUNDED"), + errmsg("cannot specify finite value after infinity"), parser_errposition(pstate, exprLocation((Node *) ldatum)))); } - seen_unbounded = false; + seen_infinity = false; foreach(cell1, spec->upperdatums) { PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum, lfirst(cell1)); if (rdatum->infinite) - seen_unbounded = true; - else if (seen_unbounded) + { + DefElem *inf; + + seen_infinity = true; + inf = castNode(DefElem, rdatum->value); + if (strcmp(inf->defname, "infinity") == 0) + inf->defname = pstrdup("positive_infinity"); + } + else if (seen_infinity) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot specify finite value after UNBOUNDED"), + errmsg("cannot specify finite value after infinity"), parser_errposition(pstate, exprLocation((Node *) rdatum)))); } @@ -3444,7 +3460,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, coltype = get_partition_col_typid(key, i); coltypmod = get_partition_col_typmod(key, i); - if (ldatum->value) + if (ldatum->value && !IsA(ldatum->value, DefElem)) { con = castNode(A_Const, ldatum->value); value = transformPartitionBoundValue(pstate, con, @@ -3458,7 +3474,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, ldatum->value = (Node *) value; } - if (rdatum->value) + if (rdatum->value && !IsA(rdatum->value, DefElem)) { con = castNode(A_Const, rdatum->value); value = transformPartitionBoundValue(pstate, con, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 18d9e27d1e..e093e503b3 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8681,7 +8681,16 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, sep); if (datum->infinite) - appendStringInfoString(buf, "UNBOUNDED"); + { + DefElem *inf; + + inf = castNode(DefElem, datum->value); + if (strcmp(inf->defname, + "negative_infinity") == 0) + appendStringInfoString(buf, "-infinity"); + else + appendStringInfoString(buf, "+infinity"); + } else { Const *val = castNode(Const, datum->value); @@ -8699,7 +8708,16 @@ get_rule_expr(Node *node, deparse_context *context, appendStringInfoString(buf, sep); if (datum->infinite) - appendStringInfoString(buf, "UNBOUNDED"); + { + DefElem *inf; + + inf = castNode(DefElem, datum->value); + if (strcmp(inf->defname, + "negative_infinity") == 0) + appendStringInfoString(buf, "-infinity"); + else + appendStringInfoString(buf, "+infinity"); + } else { Const *val = castNode(Const, datum->value); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 1d96169d34..5fea805717 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -811,14 +811,16 @@ typedef struct PartitionBoundSpec /* * PartitionRangeDatum - can be either a value or UNBOUNDED * - * "value" is an A_Const in raw grammar output, a Const after analysis + * "value" is an A_Const in raw grammar output, a Const after analysis, if + * it represents a finite value. For (-/+) infinity, it contains a suitably + * decorated DefElem instead, both before and after analysis. */ typedef struct PartitionRangeDatum { NodeTag type; - bool infinite; /* true if UNBOUNDED */ - Node *value; /* null if UNBOUNDED */ + bool infinite; /* true if (-/+) infinity */ + Node *value; int location; /* token location, or -1 if unknown */ } PartitionRangeDatum; diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index f50e45e886..dff0af69bd 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -198,6 +198,7 @@ PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD) PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD) PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD) PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD) +PG_KEYWORD("infinity", INFINITY, UNRESERVED_KEYWORD) PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD) PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD) PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD) diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index b6f794e1c2..ab5665b0c8 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -512,13 +512,13 @@ ERROR: FROM must specify exactly one value per partitioning column CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z', 1); ERROR: TO must specify exactly one value per partitioning column -- cannot specify null values in range bounds -CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (unbounded); +CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (infinity); ERROR: cannot specify NULL in range bound --- cannot specify finite values after UNBOUNDED has been specified +-- cannot specify finite values after infinity has been specified CREATE TABLE range_parted_multicol (a int, b int, c int) PARTITION BY RANGE (a, b, c); -CREATE TABLE fail_part PARTITION OF range_parted_multicol FOR VALUES FROM (1, UNBOUNDED, 1) TO (UNBOUNDED, 1, 1); -ERROR: cannot specify finite value after UNBOUNDED -LINE 1: ...ge_parted_multicol FOR VALUES FROM (1, UNBOUNDED, 1) TO (UNB... +CREATE TABLE fail_part PARTITION OF range_parted_multicol FOR VALUES FROM (1, infinity, 1) TO (infinity, 1, 1); +ERROR: cannot specify finite value after infinity +LINE 1: ...nge_parted_multicol FOR VALUES FROM (1, infinity, 1) TO (inf... ^ DROP TABLE range_parted_multicol; -- check if compatible with the specified parent @@ -578,11 +578,11 @@ ERROR: cannot create range partition with empty range -- note that the range '[1, 1)' has no elements CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1); ERROR: cannot create range partition with empty range -CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (1); -CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (2); +CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (infinity) TO (1); +CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (infinity) TO (2); ERROR: partition "fail_part" would overlap partition "part0" CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10); -CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (unbounded); +CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (infinity); ERROR: partition "fail_part" would overlap partition "part1" CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30); CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40); @@ -595,19 +595,54 @@ CREATE TABLE range_parted3 ( a int, b int ) PARTITION BY RANGE (a, (b+1)); -CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, unbounded); -CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, 1); +CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, infinity) TO (0, infinity); +CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, infinity) TO (0, 1); ERROR: partition "fail_part" would overlap partition "part00" -CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, unbounded) TO (1, 1); +CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, infinity) TO (1, 1); CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10); -CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, unbounded); +CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, infinity); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20); ERROR: partition "fail_part" would overlap partition "part12" -- cannot create a partition that says column b is allowed to range -- from -infinity to +infinity, while there exist partitions that have -- more specific ranges -CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, unbounded) TO (1, unbounded); +CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, infinity) TO (1, infinity); ERROR: partition "fail_part" would overlap partition "part10" +-- check that range partition bound syntax allows -/+ to be specified for +-- infinity values in arbitrary ways. +create table multicol_range (a text, b numeric) partition by range (a, b); +create table multicol_range_1 partition of multicol_range for values from ('a', infinity) to ('b', -infinity); +create table multicol_range_2 partition of multicol_range for values from ('b', infinity) to ('c', -infinity); +-- Accepts 'd', but not strings lexically greater than 'd' +create table multicol_range_3 partition of multicol_range for values from ('c', infinity) to ('d', infinity); +\d+ multicol_range_3 + Table "public.multicol_range_3" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+----------+--------------+------------- + a | text | | | | extended | | + b | numeric | | | | main | | +Partition of: multicol_range FOR VALUES FROM ('c', -infinity) TO ('d', +infinity) +Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a <= 'd'::text)) + +-- Does not accept 'd'; only strings lexically greater than 'd' +create table multicol_range_4 partition of multicol_range for values from ('d', +infinity) to (infinity, infinity); +\d+ multicol_range_4 + Table "public.multicol_range_4" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+----------+--------------+------------- + a | text | | | | extended | | + b | numeric | | | | main | | +Partition of: multicol_range FOR VALUES FROM ('d', +infinity) TO (+infinity, +infinity) +Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'd'::text)) + +-- Some combinations would make the resulting range effectivly empty, which +-- are promptly rejected +create table multicol_range_err partition of multicol_range for values from ('e', +infinity) to (-infinity, infinity); +ERROR: cannot create range partition with empty range +create table multicol_range_err partition of multicol_range for values from ('e', infinity) to ('e', -infinity); +ERROR: cannot create range partition with empty range +create table multicol_range_err partition of multicol_range for values from (+infinity, infinity) to (-infinity, infinity); +ERROR: cannot create range partition with empty range -- check schema propagation from parent CREATE TABLE parted ( a text, @@ -708,7 +743,7 @@ Number of partitions: 3 (Use \d+ to list them.) -- check that we get the expected partition constraints CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c); -CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED); +CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (infinity, infinity, infinity) TO (infinity, infinity, infinity); \d+ unbounded_range_part Table "public.unbounded_range_part" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -716,11 +751,11 @@ CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UN a | integer | | | | plain | | b | integer | | | | plain | | c | integer | | | | plain | | -Partition of: range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED) +Partition of: range_parted4 FOR VALUES FROM (-infinity, -infinity, -infinity) TO (+infinity, +infinity, +infinity) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL)) DROP TABLE unbounded_range_part; -CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED); +CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (infinity, infinity, infinity) TO (1, infinity, infinity); \d+ range_parted4_1 Table "public.range_parted4_1" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -728,10 +763,10 @@ CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUND a | integer | | | | plain | | b | integer | | | | plain | | c | integer | | | | plain | | -Partition of: range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED) +Partition of: range_parted4 FOR VALUES FROM (-infinity, -infinity, -infinity) TO (1, +infinity, +infinity) Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1)) -CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED); +CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, infinity); \d+ range_parted4_2 Table "public.range_parted4_2" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -739,10 +774,10 @@ CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5 a | integer | | | | plain | | b | integer | | | | plain | | c | integer | | | | plain | | -Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED) +Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, +infinity) 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)))) -CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED); +CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, infinity) TO (9, infinity, infinity); \d+ range_parted4_3 Table "public.range_parted4_3" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -750,12 +785,12 @@ CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, U a | integer | | | | plain | | b | integer | | | | plain | | c | integer | | | | plain | | -Partition of: range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED) +Partition of: range_parted4 FOR VALUES FROM (6, 8, -infinity) TO (9, +infinity, +infinity) 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)) DROP TABLE range_parted4; -- cleanup -DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; +DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3, multicol_range; -- comments on partitioned tables columns CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a); COMMENT ON TABLE parted_col_comment IS 'Am partitioned table'; diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 35d182d599..924a24f97b 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1718,7 +1718,7 @@ 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 (unbounded) partition by list (b); +create table part_40_inf partition of range_list_parted for values from (40) to (+infinity) 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); @@ -1831,12 +1831,12 @@ 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 mcrparted0 partition of mcrparted for values from (-infinity, -infinity, -infinity) 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); +create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (+infinity, +infinity, +infinity); explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0 QUERY PLAN ------------------------------ diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out index d1153f410b..6047c5b0a6 100644 --- a/src/test/regress/expected/insert.out +++ b/src/test/regress/expected/insert.out @@ -288,7 +288,7 @@ select tableoid::regclass, * from list_parted; -- some more tests to exercise tuple-routing with multi-level partitioning create table part_gg partition of list_parted for values in ('gg') partition by range (b); -create table part_gg1 partition of part_gg for values from (unbounded) to (1); +create table part_gg1 partition of part_gg for values from (infinity) to (1); create table part_gg2 partition of part_gg for values from (1) to (10) partition by range (b); create table part_gg2_1 partition of part_gg2 for values from (1) to (5); create table part_gg2_2 partition of part_gg2 for values from (5) to (10); @@ -439,12 +439,12 @@ 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 mcrparted0 partition of mcrparted for values from (infinity, infinity, infinity) to (1, infinity, infinity); +create table mcrparted1 partition of mcrparted for values from (2, 1, infinity) to (10, 5, 10); +create table mcrparted2 partition of mcrparted for values from (10, 6, infinity) to (10, infinity, infinity); 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); +create table mcrparted4 partition of mcrparted for values from (21, infinity, infinity) to (30, 20, infinity); +create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (infinity, infinity, infinity); -- routed to mcrparted0 insert into mcrparted values (0, 1, 1); insert into mcrparted0 values (0, 1, 1); @@ -508,3 +508,52 @@ ERROR: new row for relation "brtrigpartcon1" violates partition constraint DETAIL: Failing row contains (2, hi there). drop table brtrigpartcon; drop function brtrigpartcon1trigf(); +-- check that tuple-routing works and partition constraint are enforced +-- properly when infinity values are specified with arbitrary signs in +-- FROM and TO of range partition bounds. +create table blogs (a text, b numeric) partition by range (a, b); +create table blogs_a partition of blogs for values from ('a', infinity) to ('b', -infinity); +create table blogs_b partition of blogs for values from ('b', infinity) to ('c', -infinity); +-- Note that this one admits just the letter 'd' +create table blogs_c_and_d partition of blogs for values from ('c', infinity) to ('d', infinity); +-- This one allows strings lexically greater than letter 'd' +create table blogs_d_to_z partition of blogs for values from ('d', +infinity) to (infinity, infinity); +\d+ blogs + Table "public.blogs" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+----------+--------------+------------- + a | text | | | | extended | | + b | numeric | | | | main | | +Partition key: RANGE (a, b) +Partitions: blogs_a FOR VALUES FROM ('a', -infinity) TO ('b', -infinity), + blogs_b FOR VALUES FROM ('b', -infinity) TO ('c', -infinity), + blogs_c_and_d FOR VALUES FROM ('c', -infinity) TO ('d', +infinity), + blogs_d_to_z FOR VALUES FROM ('d', +infinity) TO (+infinity, +infinity) + +insert into blogs values ('and there it was', 0), ('because thats why', -1), ('come on', 12345), ('d', 132), ('drag', 908.0), ('zappa the legend', 1); +select tableoid::regclass::text, * from blogs order by 1; + tableoid | a | b +---------------+-------------------+------- + blogs_a | and there it was | 0 + blogs_b | because thats why | -1 + blogs_c_and_d | come on | 12345 + blogs_c_and_d | d | 132 + blogs_d_to_z | drag | 908.0 + blogs_d_to_z | zappa the legend | 1 +(6 rows) + +-- indmissible +insert into blogs_a values ('b', 1); +ERROR: new row for relation "blogs_a" violates partition constraint +DETAIL: Failing row contains (b, 1). +insert into blogs_b values ('c', 1); +ERROR: new row for relation "blogs_b" violates partition constraint +DETAIL: Failing row contains (c, 1). +insert into blogs_c_and_d values ('dr', 1); +ERROR: new row for relation "blogs_c_and_d" violates partition constraint +DETAIL: Failing row contains (dr, 1). +insert into blogs_d_to_z values ('d', 1); +ERROR: new row for relation "blogs_d_to_z" violates partition constraint +DETAIL: Failing row contains (d, 1). +-- cleanup +drop table blogs; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb7aa5bbc6..f0bdc7116c 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -483,11 +483,11 @@ CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('z CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z', 1); -- cannot specify null values in range bounds -CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (unbounded); +CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (null) TO (infinity); --- cannot specify finite values after UNBOUNDED has been specified +-- cannot specify finite values after infinity has been specified CREATE TABLE range_parted_multicol (a int, b int, c int) PARTITION BY RANGE (a, b, c); -CREATE TABLE fail_part PARTITION OF range_parted_multicol FOR VALUES FROM (1, UNBOUNDED, 1) TO (UNBOUNDED, 1, 1); +CREATE TABLE fail_part PARTITION OF range_parted_multicol FOR VALUES FROM (1, infinity, 1) TO (infinity, 1, 1); DROP TABLE range_parted_multicol; -- check if compatible with the specified parent @@ -542,10 +542,10 @@ CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0); -- note that the range '[1, 1)' has no elements CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1); -CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (1); -CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (2); +CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (infinity) TO (1); +CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (infinity) TO (2); CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10); -CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (unbounded); +CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (9) TO (infinity); CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30); CREATE TABLE part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40); CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30); @@ -557,18 +557,36 @@ CREATE TABLE range_parted3 ( b int ) PARTITION BY RANGE (a, (b+1)); -CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, unbounded); -CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, 1); +CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, infinity) TO (0, infinity); +CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, infinity) TO (0, 1); -CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, unbounded) TO (1, 1); +CREATE TABLE part10 PARTITION OF range_parted3 FOR VALUES FROM (1, infinity) TO (1, 1); CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10); -CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, unbounded); +CREATE TABLE part12 PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, infinity); CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20); -- cannot create a partition that says column b is allowed to range -- from -infinity to +infinity, while there exist partitions that have -- more specific ranges -CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, unbounded) TO (1, unbounded); +CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, infinity) TO (1, infinity); + +-- check that range partition bound syntax allows -/+ to be specified for +-- infinity values in arbitrary ways. +create table multicol_range (a text, b numeric) partition by range (a, b); +create table multicol_range_1 partition of multicol_range for values from ('a', infinity) to ('b', -infinity); +create table multicol_range_2 partition of multicol_range for values from ('b', infinity) to ('c', -infinity); +-- Accepts 'd', but not strings lexically greater than 'd' +create table multicol_range_3 partition of multicol_range for values from ('c', infinity) to ('d', infinity); +\d+ multicol_range_3 +-- Does not accept 'd'; only strings lexically greater than 'd' +create table multicol_range_4 partition of multicol_range for values from ('d', +infinity) to (infinity, infinity); +\d+ multicol_range_4 + +-- Some combinations would make the resulting range effectivly empty, which +-- are promptly rejected +create table multicol_range_err partition of multicol_range for values from ('e', +infinity) to (-infinity, infinity); +create table multicol_range_err partition of multicol_range for values from ('e', infinity) to ('e', -infinity); +create table multicol_range_err partition of multicol_range for values from (+infinity, infinity) to (-infinity, infinity); -- check schema propagation from parent @@ -626,19 +644,19 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10); -- check that we get the expected partition constraints CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c); -CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED); +CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (infinity, infinity, infinity) TO (infinity, infinity, infinity); \d+ unbounded_range_part DROP TABLE unbounded_range_part; -CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED); +CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (infinity, infinity, infinity) TO (1, infinity, infinity); \d+ range_parted4_1 -CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED); +CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, infinity); \d+ range_parted4_2 -CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED); +CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, infinity) TO (9, infinity, infinity); \d+ range_parted4_3 DROP TABLE range_parted4; -- cleanup -DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; +DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3, multicol_range; -- comments on partitioned tables columns CREATE TABLE parted_col_comment (a int, b text) PARTITION BY LIST (a); diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index 70fe971d51..663ecc8190 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -623,7 +623,7 @@ 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 (unbounded) partition by list (b); +create table part_40_inf partition of range_list_parted for values from (40) to (+infinity) 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); @@ -647,12 +647,12 @@ 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 mcrparted0 partition of mcrparted for values from (-infinity, -infinity, -infinity) 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); +create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (+infinity, +infinity, +infinity); 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 diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql index 83c3ad8f53..dd3baab93a 100644 --- a/src/test/regress/sql/insert.sql +++ b/src/test/regress/sql/insert.sql @@ -169,7 +169,7 @@ select tableoid::regclass, * from list_parted; -- some more tests to exercise tuple-routing with multi-level partitioning create table part_gg partition of list_parted for values in ('gg') partition by range (b); -create table part_gg1 partition of part_gg for values from (unbounded) to (1); +create table part_gg1 partition of part_gg for values from (infinity) to (1); create table part_gg2 partition of part_gg for values from (1) to (10) partition by range (b); create table part_gg2_1 partition of part_gg2 for values from (1) to (5); create table part_gg2_2 partition of part_gg2 for values from (5) to (10); @@ -293,12 +293,12 @@ 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 mcrparted0 partition of mcrparted for values from (infinity, infinity, infinity) to (1, infinity, infinity); +create table mcrparted1 partition of mcrparted for values from (2, 1, infinity) to (10, 5, 10); +create table mcrparted2 partition of mcrparted for values from (10, 6, infinity) to (10, infinity, infinity); 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); +create table mcrparted4 partition of mcrparted for values from (21, infinity, infinity) to (30, 20, infinity); +create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (infinity, infinity, infinity); -- routed to mcrparted0 insert into mcrparted values (0, 1, 1); @@ -342,3 +342,25 @@ insert into brtrigpartcon values (1, 'hi there'); insert into brtrigpartcon1 values (1, 'hi there'); drop table brtrigpartcon; drop function brtrigpartcon1trigf(); + +-- check that tuple-routing works and partition constraint are enforced +-- properly when infinity values are specified with arbitrary signs in +-- FROM and TO of range partition bounds. +create table blogs (a text, b numeric) partition by range (a, b); +create table blogs_a partition of blogs for values from ('a', infinity) to ('b', -infinity); +create table blogs_b partition of blogs for values from ('b', infinity) to ('c', -infinity); +-- Note that this one admits just the letter 'd' +create table blogs_c_and_d partition of blogs for values from ('c', infinity) to ('d', infinity); +-- This one allows strings lexically greater than letter 'd' +create table blogs_d_to_z partition of blogs for values from ('d', +infinity) to (infinity, infinity); +\d+ blogs +insert into blogs values ('and there it was', 0), ('because thats why', -1), ('come on', 12345), ('d', 132), ('drag', 908.0), ('zappa the legend', 1); +select tableoid::regclass::text, * from blogs order by 1; +-- indmissible +insert into blogs_a values ('b', 1); +insert into blogs_b values ('c', 1); +insert into blogs_c_and_d values ('dr', 1); +insert into blogs_d_to_z values ('d', 1); + +-- cleanup +drop table blogs; -- 2.11.0