Use MINVALUE/MAXVALUE instead of UNBOUNDED for range partition bounds.
Previously, UNBOUNDED meant no lower bound when used in the FROM list, and no upper bound when used in the TO list, which was OK for single-column range partitioning, but problematic with multiple columns. For example, an upper bound of (10.0, UNBOUNDED) would not be collocated with a lower bound of (10.0, UNBOUNDED), thus making it difficult or impossible to define contiguous multi-column range partitions in some cases. Fix this by using MINVALUE and MAXVALUE instead of UNBOUNDED to represent a partition column that is unbounded below or above respectively. This syntax removes any ambiguity, and ensures that if one partition's lower bound equals another partition's upper bound, then the partitions are contiguous. Also drop the constraint prohibiting finite values after an unbounded column, and just document the fact that any values after MINVALUE or MAXVALUE are ignored. Previously it was necessary to repeat UNBOUNDED multiple times, which was needlessly verbose. Note: Forces a post-PG 10 beta2 initdb. Report by Amul Sul, original patch by Amit Langote with some additional hacking by me. Discussion: https://postgr.es/m/CAAJ_b947mowpLdxL3jo3YLKngRjrq9+Ej4ymduQTfYR+8=YAYQ@mail.gmail.com
This commit is contained in:
parent
866f4a7c21
commit
d363d42bb9
@ -87,8 +87,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
|
|||||||
<phrase>and <replaceable class="PARAMETER">partition_bound_spec</replaceable> is:</phrase>
|
<phrase>and <replaceable class="PARAMETER">partition_bound_spec</replaceable> is:</phrase>
|
||||||
|
|
||||||
IN ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | NULL } [, ...] ) |
|
IN ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | NULL } [, ...] ) |
|
||||||
FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | UNBOUNDED } [, ...] )
|
FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | MINVALUE | MAXVALUE } [, ...] )
|
||||||
TO ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | UNBOUNDED } [, ...] )
|
TO ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replaceable class="PARAMETER">string_literal</replaceable> | MINVALUE | MAXVALUE } [, ...] )
|
||||||
|
|
||||||
<phrase><replaceable class="PARAMETER">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
|
<phrase><replaceable class="PARAMETER">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
|
||||||
|
|
||||||
@ -269,10 +269,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
|
|||||||
<para>
|
<para>
|
||||||
Each of the values specified in
|
Each of the values specified in
|
||||||
the <replaceable class="PARAMETER">partition_bound_spec</> is
|
the <replaceable class="PARAMETER">partition_bound_spec</> is
|
||||||
a literal, <literal>NULL</literal>, or <literal>UNBOUNDED</literal>.
|
a literal, <literal>NULL</literal>, <literal>MINVALUE</literal>, or
|
||||||
Each literal value must be either a numeric constant that is coercible
|
<literal>MAXVALUE</literal>. Each literal value must be either a
|
||||||
to the corresponding partition key column's type, or a string literal
|
numeric constant that is coercible to the corresponding partition key
|
||||||
that is valid input for that type.
|
column's type, or a string literal that is valid input for that type.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -300,13 +300,46 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Writing <literal>UNBOUNDED</literal> in <literal>FROM</literal>
|
The special values <literal>MINVALUE</> and <literal>MAXVALUE</>
|
||||||
signifies <literal>-infinity</literal> as the lower bound of the
|
may be used when creating a range partition to indicate that there
|
||||||
corresponding column, whereas when written in <literal>TO</literal>,
|
is no lower or upper bound on the column's value. For example, a
|
||||||
it signifies <literal>+infinity</literal> as the upper bound.
|
partition defined using <literal>FROM (MINVALUE) TO (10)</> allows
|
||||||
All items following an <literal>UNBOUNDED</literal> item within
|
any values less than 10, and a partition defined using
|
||||||
a <literal>FROM</literal> or <literal>TO</literal> list must also
|
<literal>FROM (10) TO (MAXVALUE)</> allows any values greater than
|
||||||
be <literal>UNBOUNDED</literal>.
|
or equal to 10.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When creating a range partition involving more than one column, it
|
||||||
|
can also make sense to use <literal>MAXVALUE</> as part of the lower
|
||||||
|
bound, and <literal>MINVALUE</> as part of the upper bound. For
|
||||||
|
example, a partition defined using
|
||||||
|
<literal>FROM (0, MAXVALUE) TO (10, MAXVALUE)</> allows any rows
|
||||||
|
where the first partition key column is greater than 0 and less than
|
||||||
|
or equal to 10. Similarly, a partition defined using
|
||||||
|
<literal>FROM ('a', MINVALUE) TO ('b', MINVALUE)</> allows any rows
|
||||||
|
where the first partition key column starts with "a".
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Note that any values after <literal>MINVALUE</> or
|
||||||
|
<literal>MAXVALUE</> in a partition bound are ignored; so the bound
|
||||||
|
<literal>(10, MINVALUE, 0)</> is equivalent to
|
||||||
|
<literal>(10, MINVALUE, 10)</> and <literal>(10, MINVALUE, MINVALUE)</>
|
||||||
|
and <literal>(10, MINVALUE, MAXVALUE)</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Also note that some element types, such as <literal>timestamp</>,
|
||||||
|
have a notion of "infinity", which is just another value that can
|
||||||
|
be stored. This is different from <literal>MINVALUE</> and
|
||||||
|
<literal>MAXVALUE</>, which are not real values that can be stored,
|
||||||
|
but rather they are ways of saying that the value is unbounded.
|
||||||
|
<literal>MAXVALUE</> can be thought of as being greater than any
|
||||||
|
other value, including "infinity" and <literal>MINVALUE</> as being
|
||||||
|
less than any other value, including "minus infinity". Thus the range
|
||||||
|
<literal>FROM ('infinity') TO (MAXVALUE)</> is not an empty range; it
|
||||||
|
allows precisely one value to be stored — "infinity".
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1610,7 +1643,7 @@ CREATE TABLE measurement_y2016m07
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
CREATE TABLE measurement_ym_older
|
CREATE TABLE measurement_ym_older
|
||||||
PARTITION OF measurement_year_month
|
PARTITION OF measurement_year_month
|
||||||
FOR VALUES FROM (unbounded, unbounded) TO (2016, 11);
|
FOR VALUES FROM (MINVALUE, 0) TO (2016, 11);
|
||||||
|
|
||||||
CREATE TABLE measurement_ym_y2016m11
|
CREATE TABLE measurement_ym_y2016m11
|
||||||
PARTITION OF measurement_year_month
|
PARTITION OF measurement_year_month
|
||||||
|
@ -67,23 +67,14 @@
|
|||||||
* is an upper bound.
|
* is an upper bound.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Ternary value to represent what's contained in a range bound datum */
|
|
||||||
typedef enum RangeDatumContent
|
|
||||||
{
|
|
||||||
RANGE_DATUM_FINITE = 0, /* actual datum stored elsewhere */
|
|
||||||
RANGE_DATUM_NEG_INF, /* negative infinity */
|
|
||||||
RANGE_DATUM_POS_INF /* positive infinity */
|
|
||||||
} RangeDatumContent;
|
|
||||||
|
|
||||||
typedef struct PartitionBoundInfoData
|
typedef struct PartitionBoundInfoData
|
||||||
{
|
{
|
||||||
char strategy; /* list or range bounds? */
|
char strategy; /* list or range bounds? */
|
||||||
int ndatums; /* Length of the datums following array */
|
int ndatums; /* Length of the datums following array */
|
||||||
Datum **datums; /* Array of datum-tuples with key->partnatts
|
Datum **datums; /* Array of datum-tuples with key->partnatts
|
||||||
* datums each */
|
* datums each */
|
||||||
RangeDatumContent **content; /* what's contained in each range bound
|
PartitionRangeDatumKind **kind; /* The kind of each range bound datum;
|
||||||
* datum? (see the above enum); NULL for
|
* NULL for list partitioned tables */
|
||||||
* list partitioned tables */
|
|
||||||
int *indexes; /* Partition indexes; one entry per member of
|
int *indexes; /* Partition indexes; one entry per member of
|
||||||
* the datums array (plus one if range
|
* the datums array (plus one if range
|
||||||
* partitioned table) */
|
* partitioned table) */
|
||||||
@ -110,7 +101,7 @@ typedef struct PartitionRangeBound
|
|||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
Datum *datums; /* range bound datums */
|
Datum *datums; /* range bound datums */
|
||||||
RangeDatumContent *content; /* what's contained in each datum? */
|
PartitionRangeDatumKind *kind; /* the kind of each datum */
|
||||||
bool lower; /* this is the lower (vs upper) bound */
|
bool lower; /* this is the lower (vs upper) bound */
|
||||||
} PartitionRangeBound;
|
} PartitionRangeBound;
|
||||||
|
|
||||||
@ -136,10 +127,10 @@ static List *generate_partition_qual(Relation rel);
|
|||||||
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
|
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
|
||||||
List *datums, bool lower);
|
List *datums, bool lower);
|
||||||
static int32 partition_rbound_cmp(PartitionKey key,
|
static int32 partition_rbound_cmp(PartitionKey key,
|
||||||
Datum *datums1, RangeDatumContent *content1, bool lower1,
|
Datum *datums1, PartitionRangeDatumKind *kind1,
|
||||||
PartitionRangeBound *b2);
|
bool lower1, PartitionRangeBound *b2);
|
||||||
static int32 partition_rbound_datum_cmp(PartitionKey key,
|
static int32 partition_rbound_datum_cmp(PartitionKey key,
|
||||||
Datum *rb_datums, RangeDatumContent *rb_content,
|
Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
|
||||||
Datum *tuple_datums);
|
Datum *tuple_datums);
|
||||||
|
|
||||||
static int32 partition_bound_cmp(PartitionKey key,
|
static int32 partition_bound_cmp(PartitionKey key,
|
||||||
@ -366,29 +357,25 @@ RelationBuildPartitionDesc(Relation rel)
|
|||||||
bool is_distinct = false;
|
bool is_distinct = false;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
/* Is current bound is distinct from the previous? */
|
/* Is the current bound distinct from the previous one? */
|
||||||
for (j = 0; j < key->partnatts; j++)
|
for (j = 0; j < key->partnatts; j++)
|
||||||
{
|
{
|
||||||
Datum cmpval;
|
Datum cmpval;
|
||||||
|
|
||||||
if (prev == NULL)
|
if (prev == NULL || cur->kind[j] != prev->kind[j])
|
||||||
{
|
{
|
||||||
is_distinct = true;
|
is_distinct = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If either of them has infinite element, we can't equate
|
* If the bounds are both MINVALUE or MAXVALUE, stop now
|
||||||
* them. Even when both are infinite, they'd have
|
* and treat them as equal, since any values after this
|
||||||
* opposite signs, because only one of cur and prev is a
|
* point must be ignored.
|
||||||
* lower bound).
|
|
||||||
*/
|
*/
|
||||||
if (cur->content[j] != RANGE_DATUM_FINITE ||
|
if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
|
||||||
prev->content[j] != RANGE_DATUM_FINITE)
|
|
||||||
{
|
|
||||||
is_distinct = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
cmpval = FunctionCall2Coll(&key->partsupfunc[j],
|
cmpval = FunctionCall2Coll(&key->partsupfunc[j],
|
||||||
key->partcollation[j],
|
key->partcollation[j],
|
||||||
cur->datums[j],
|
cur->datums[j],
|
||||||
@ -513,8 +500,9 @@ RelationBuildPartitionDesc(Relation rel)
|
|||||||
|
|
||||||
case PARTITION_STRATEGY_RANGE:
|
case PARTITION_STRATEGY_RANGE:
|
||||||
{
|
{
|
||||||
boundinfo->content = (RangeDatumContent **) palloc(ndatums *
|
boundinfo->kind = (PartitionRangeDatumKind **)
|
||||||
sizeof(RangeDatumContent *));
|
palloc(ndatums *
|
||||||
|
sizeof(PartitionRangeDatumKind *));
|
||||||
boundinfo->indexes = (int *) palloc((ndatums + 1) *
|
boundinfo->indexes = (int *) palloc((ndatums + 1) *
|
||||||
sizeof(int));
|
sizeof(int));
|
||||||
|
|
||||||
@ -524,18 +512,17 @@ RelationBuildPartitionDesc(Relation rel)
|
|||||||
|
|
||||||
boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
|
boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
|
||||||
sizeof(Datum));
|
sizeof(Datum));
|
||||||
boundinfo->content[i] = (RangeDatumContent *)
|
boundinfo->kind[i] = (PartitionRangeDatumKind *)
|
||||||
palloc(key->partnatts *
|
palloc(key->partnatts *
|
||||||
sizeof(RangeDatumContent));
|
sizeof(PartitionRangeDatumKind));
|
||||||
for (j = 0; j < key->partnatts; j++)
|
for (j = 0; j < key->partnatts; j++)
|
||||||
{
|
{
|
||||||
if (rbounds[i]->content[j] == RANGE_DATUM_FINITE)
|
if (rbounds[i]->kind[j] == PARTITION_RANGE_DATUM_VALUE)
|
||||||
boundinfo->datums[i][j] =
|
boundinfo->datums[i][j] =
|
||||||
datumCopy(rbounds[i]->datums[j],
|
datumCopy(rbounds[i]->datums[j],
|
||||||
key->parttypbyval[j],
|
key->parttypbyval[j],
|
||||||
key->parttyplen[j]);
|
key->parttyplen[j]);
|
||||||
/* Remember, we are storing the tri-state value. */
|
boundinfo->kind[i][j] = rbounds[i]->kind[j];
|
||||||
boundinfo->content[i][j] = rbounds[i]->content[j];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -617,17 +604,14 @@ partition_bounds_equal(PartitionKey key,
|
|||||||
for (j = 0; j < key->partnatts; j++)
|
for (j = 0; j < key->partnatts; j++)
|
||||||
{
|
{
|
||||||
/* For range partitions, the bounds might not be finite. */
|
/* For range partitions, the bounds might not be finite. */
|
||||||
if (b1->content != NULL)
|
if (b1->kind != NULL)
|
||||||
{
|
{
|
||||||
/*
|
/* The different kinds of bound all differ from each other */
|
||||||
* A finite bound always differs from an infinite bound, and
|
if (b1->kind[i][j] != b2->kind[i][j])
|
||||||
* different kinds of infinities differ from each other.
|
|
||||||
*/
|
|
||||||
if (b1->content[i][j] != b2->content[i][j])
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Non-finite bounds are equal without further examination. */
|
/* Non-finite bounds are equal without further examination. */
|
||||||
if (b1->content[i][j] != RANGE_DATUM_FINITE)
|
if (b1->kind[i][j] != PARTITION_RANGE_DATUM_VALUE)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,7 +720,7 @@ check_new_partition_bound(char *relname, Relation parent,
|
|||||||
* First check if the resulting range would be empty with
|
* First check if the resulting range would be empty with
|
||||||
* specified lower and upper bounds
|
* specified lower and upper bounds
|
||||||
*/
|
*/
|
||||||
if (partition_rbound_cmp(key, lower->datums, lower->content, true,
|
if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
|
||||||
upper) >= 0)
|
upper) >= 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
@ -754,18 +738,18 @@ check_new_partition_bound(char *relname, Relation parent,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Test whether the new lower bound (which is treated
|
* Test whether the new lower bound (which is treated
|
||||||
* inclusively as part of the new partition) lies inside an
|
* inclusively as part of the new partition) lies inside
|
||||||
* existing partition, or in a gap.
|
* an existing partition, or in a gap.
|
||||||
*
|
*
|
||||||
* If it's inside an existing partition, the bound at
|
* If it's inside an existing partition, the bound at
|
||||||
* offset + 1 will be the upper bound of that partition,
|
* offset + 1 will be the upper bound of that partition,
|
||||||
* and its index will be >= 0.
|
* and its index will be >= 0.
|
||||||
*
|
*
|
||||||
* If it's in a gap, the bound at offset + 1 will be the
|
* If it's in a gap, the bound at offset + 1 will be the
|
||||||
* lower bound of the next partition, and its index will be
|
* lower bound of the next partition, and its index will
|
||||||
* -1. This is also true if there is no next partition,
|
* be -1. This is also true if there is no next partition,
|
||||||
* since the index array is initialised with an extra -1 at
|
* since the index array is initialised with an extra -1
|
||||||
* the end.
|
* at the end.
|
||||||
*/
|
*/
|
||||||
offset = partition_bound_bsearch(key, boundinfo, lower,
|
offset = partition_bound_bsearch(key, boundinfo, lower,
|
||||||
true, &equal);
|
true, &equal);
|
||||||
@ -774,9 +758,9 @@ check_new_partition_bound(char *relname, Relation parent,
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Check that the new partition will fit in the gap.
|
* Check that the new partition will fit in the gap.
|
||||||
* For it to fit, the new upper bound must be less than
|
* For it to fit, the new upper bound must be less
|
||||||
* or equal to the lower bound of the next partition,
|
* than or equal to the lower bound of the next
|
||||||
* if there is one.
|
* partition, if there is one.
|
||||||
*/
|
*/
|
||||||
if (offset + 1 < boundinfo->ndatums)
|
if (offset + 1 < boundinfo->ndatums)
|
||||||
{
|
{
|
||||||
@ -788,8 +772,9 @@ check_new_partition_bound(char *relname, Relation parent,
|
|||||||
if (cmpval < 0)
|
if (cmpval < 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The new partition overlaps with the existing
|
* The new partition overlaps with the
|
||||||
* partition between offset + 1 and offset + 2.
|
* existing partition between offset + 1 and
|
||||||
|
* offset + 2.
|
||||||
*/
|
*/
|
||||||
overlap = true;
|
overlap = true;
|
||||||
with = boundinfo->indexes[offset + 2];
|
with = boundinfo->indexes[offset + 2];
|
||||||
@ -1399,8 +1384,8 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
|
|||||||
*
|
*
|
||||||
* Constructs an Expr for the key column (returned in *keyCol) and Consts
|
* Constructs an Expr for the key column (returned in *keyCol) and Consts
|
||||||
* for the lower and upper range limits (returned in *lower_val and
|
* 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 MINVALUE/MAXVALUE limits, NULL is returned instead of
|
||||||
* All of these structures are freshly palloc'd.
|
* a Const. All of these structures are freshly palloc'd.
|
||||||
*
|
*
|
||||||
* *partexprs_item points to the cell containing the next expression in
|
* *partexprs_item points to the cell containing the next expression in
|
||||||
* the key->partexprs list, or NULL. It may be advanced upon return.
|
* the key->partexprs list, or NULL. It may be advanced upon return.
|
||||||
@ -1432,12 +1417,12 @@ get_range_key_properties(PartitionKey key, int keynum,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get appropriate Const nodes for the bounds */
|
/* Get appropriate Const nodes for the bounds */
|
||||||
if (!ldatum->infinite)
|
if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE)
|
||||||
*lower_val = castNode(Const, copyObject(ldatum->value));
|
*lower_val = castNode(Const, copyObject(ldatum->value));
|
||||||
else
|
else
|
||||||
*lower_val = NULL;
|
*lower_val = NULL;
|
||||||
|
|
||||||
if (!udatum->infinite)
|
if (udatum->kind == PARTITION_RANGE_DATUM_VALUE)
|
||||||
*upper_val = castNode(Const, copyObject(udatum->value));
|
*upper_val = castNode(Const, copyObject(udatum->value));
|
||||||
else
|
else
|
||||||
*upper_val = NULL;
|
*upper_val = NULL;
|
||||||
@ -1471,18 +1456,16 @@ get_range_key_properties(PartitionKey key, int keynum,
|
|||||||
* AND
|
* AND
|
||||||
* (b < bu) OR (b = bu AND c < cu))
|
* (b < bu) OR (b = bu AND c < cu))
|
||||||
*
|
*
|
||||||
* If cu happens to be UNBOUNDED, we need not emit any expression for it, so
|
* If a bound datum is either MINVALUE or MAXVALUE, these expressions are
|
||||||
* the last line would be:
|
* simplified using the fact that any value is greater than MINVALUE and less
|
||||||
|
* than MAXVALUE. So, for example, if cu = MAXVALUE, c < cu is automatically
|
||||||
|
* true, and we need not emit any expression for it, and the last line becomes
|
||||||
*
|
*
|
||||||
* (b < bu) OR (b = bu), which is simplified to (b <= bu)
|
* (b < bu) OR (b = bu), which is simplified to (b <= bu)
|
||||||
*
|
*
|
||||||
* In most common cases with only one partition column, say a, the following
|
* 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
|
* expression tree will be generated: a IS NOT NULL AND a >= al AND a < au
|
||||||
*
|
*
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* If we end up with an empty result list, we return a single-member list
|
* 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.
|
* containing a constant TRUE, because callers expect a non-empty list.
|
||||||
*/
|
*/
|
||||||
@ -1585,9 +1568,10 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
|
|||||||
&lower_val, &upper_val);
|
&lower_val, &upper_val);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If either or both of lower_val and upper_val is NULL, they are
|
* If either value is NULL, the corresponding partition bound is
|
||||||
* unequal, because being NULL means the column is unbounded in the
|
* either MINVALUE or MAXVALUE, and we treat them as unequal, because
|
||||||
* respective direction.
|
* even if they're the same, there is no common value to equate the
|
||||||
|
* key column with.
|
||||||
*/
|
*/
|
||||||
if (!lower_val || !upper_val)
|
if (!lower_val || !upper_val)
|
||||||
break;
|
break;
|
||||||
@ -1668,12 +1652,15 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For the non-last columns of this arm, use the EQ operator.
|
* 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 column of this arm, use GT, unless this is the
|
||||||
|
* last column of the whole bound check, or the next bound
|
||||||
|
* datum is MINVALUE, in which case use GE.
|
||||||
*/
|
*/
|
||||||
if (j - i < current_or_arm)
|
if (j - i < current_or_arm)
|
||||||
strategy = BTEqualStrategyNumber;
|
strategy = BTEqualStrategyNumber;
|
||||||
else if ((ldatum_next && ldatum_next->infinite) ||
|
else if (j == key->partnatts - 1 ||
|
||||||
j == key->partnatts - 1)
|
(ldatum_next &&
|
||||||
|
ldatum_next->kind == PARTITION_RANGE_DATUM_MINVALUE))
|
||||||
strategy = BTGreaterEqualStrategyNumber;
|
strategy = BTGreaterEqualStrategyNumber;
|
||||||
else
|
else
|
||||||
strategy = BTGreaterStrategyNumber;
|
strategy = BTGreaterStrategyNumber;
|
||||||
@ -1691,11 +1678,13 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* For the non-last columns of this arm, use the EQ operator.
|
* For the non-last columns of this arm, use the EQ operator.
|
||||||
* For the last finite-valued column, use LE.
|
* For the last column of this arm, use LT, unless the next
|
||||||
|
* bound datum is MAXVALUE, in which case use LE.
|
||||||
*/
|
*/
|
||||||
if (j - i < current_or_arm)
|
if (j - i < current_or_arm)
|
||||||
strategy = BTEqualStrategyNumber;
|
strategy = BTEqualStrategyNumber;
|
||||||
else if (udatum_next && udatum_next->infinite)
|
else if (udatum_next &&
|
||||||
|
udatum_next->kind == PARTITION_RANGE_DATUM_MAXVALUE)
|
||||||
strategy = BTLessEqualStrategyNumber;
|
strategy = BTLessEqualStrategyNumber;
|
||||||
else
|
else
|
||||||
strategy = BTLessStrategyNumber;
|
strategy = BTLessStrategyNumber;
|
||||||
@ -1716,11 +1705,15 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
|
|||||||
if (j - i > current_or_arm)
|
if (j - i > current_or_arm)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We need not emit the next arm if the new column that will
|
* We must not emit any more arms if the new column that will
|
||||||
* be considered is unbounded.
|
* be considered is unbounded, or this one was.
|
||||||
*/
|
*/
|
||||||
need_next_lower_arm = ldatum_next && !ldatum_next->infinite;
|
if (!lower_val || !ldatum_next ||
|
||||||
need_next_upper_arm = udatum_next && !udatum_next->infinite;
|
ldatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
|
||||||
|
need_next_lower_arm = false;
|
||||||
|
if (!upper_val || !udatum_next ||
|
||||||
|
udatum_next->kind != PARTITION_RANGE_DATUM_VALUE)
|
||||||
|
need_next_upper_arm = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2092,8 +2085,8 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
|
|||||||
bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
|
bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
|
||||||
bound->index = index;
|
bound->index = index;
|
||||||
bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
|
bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
|
||||||
bound->content = (RangeDatumContent *) palloc0(key->partnatts *
|
bound->kind = (PartitionRangeDatumKind *) palloc0(key->partnatts *
|
||||||
sizeof(RangeDatumContent));
|
sizeof(PartitionRangeDatumKind));
|
||||||
bound->lower = lower;
|
bound->lower = lower;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -2102,12 +2095,9 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
|
|||||||
PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
|
PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
|
||||||
|
|
||||||
/* What's contained in this range datum? */
|
/* What's contained in this range datum? */
|
||||||
bound->content[i] = !datum->infinite
|
bound->kind[i] = datum->kind;
|
||||||
? RANGE_DATUM_FINITE
|
|
||||||
: (lower ? RANGE_DATUM_NEG_INF
|
|
||||||
: RANGE_DATUM_POS_INF);
|
|
||||||
|
|
||||||
if (bound->content[i] == RANGE_DATUM_FINITE)
|
if (datum->kind == PARTITION_RANGE_DATUM_VALUE)
|
||||||
{
|
{
|
||||||
Const *val = castNode(Const, datum->value);
|
Const *val = castNode(Const, datum->value);
|
||||||
|
|
||||||
@ -2130,7 +2120,7 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
|
|||||||
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
|
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
|
||||||
PartitionKey key = (PartitionKey) arg;
|
PartitionKey key = (PartitionKey) arg;
|
||||||
|
|
||||||
return partition_rbound_cmp(key, b1->datums, b1->content, b1->lower, b2);
|
return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2148,13 +2138,13 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
|
|||||||
*/
|
*/
|
||||||
static int32
|
static int32
|
||||||
partition_rbound_cmp(PartitionKey key,
|
partition_rbound_cmp(PartitionKey key,
|
||||||
Datum *datums1, RangeDatumContent *content1, bool lower1,
|
Datum *datums1, PartitionRangeDatumKind *kind1,
|
||||||
PartitionRangeBound *b2)
|
bool lower1, PartitionRangeBound *b2)
|
||||||
{
|
{
|
||||||
int32 cmpval = 0; /* placate compiler */
|
int32 cmpval = 0; /* placate compiler */
|
||||||
int i;
|
int i;
|
||||||
Datum *datums2 = b2->datums;
|
Datum *datums2 = b2->datums;
|
||||||
RangeDatumContent *content2 = b2->content;
|
PartitionRangeDatumKind *kind2 = b2->kind;
|
||||||
bool lower2 = b2->lower;
|
bool lower2 = b2->lower;
|
||||||
|
|
||||||
for (i = 0; i < key->partnatts; i++)
|
for (i = 0; i < key->partnatts; i++)
|
||||||
@ -2162,28 +2152,21 @@ partition_rbound_cmp(PartitionKey key,
|
|||||||
/*
|
/*
|
||||||
* First, handle cases where the column is unbounded, which should not
|
* First, handle cases where the column is unbounded, which should not
|
||||||
* invoke the comparison procedure, and should not consider any later
|
* invoke the comparison procedure, and should not consider any later
|
||||||
* columns.
|
* columns. Note that the PartitionRangeDatumKind enum elements
|
||||||
|
* compare the same way as the values they represent.
|
||||||
*/
|
*/
|
||||||
if (content1[i] != RANGE_DATUM_FINITE ||
|
if (kind1[i] < kind2[i])
|
||||||
content2[i] != RANGE_DATUM_FINITE)
|
return -1;
|
||||||
{
|
else if (kind1[i] > kind2[i])
|
||||||
/*
|
return 1;
|
||||||
* If the bound values are equal, fall through and compare whether
|
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
|
||||||
* they are upper or lower bounds.
|
|
||||||
*/
|
|
||||||
if (content1[i] == content2[i])
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Otherwise, one bound is definitely larger than the other */
|
/*
|
||||||
if (content1[i] == RANGE_DATUM_NEG_INF)
|
* The column bounds are both MINVALUE or both MAXVALUE. No later
|
||||||
return -1;
|
* columns should be considered, but we still need to compare
|
||||||
else if (content1[i] == RANGE_DATUM_POS_INF)
|
* whether they are upper or lower bounds.
|
||||||
return 1;
|
*/
|
||||||
else if (content2[i] == RANGE_DATUM_NEG_INF)
|
break;
|
||||||
return 1;
|
|
||||||
else if (content2[i] == RANGE_DATUM_POS_INF)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
|
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
|
||||||
key->partcollation[i],
|
key->partcollation[i],
|
||||||
@ -2208,12 +2191,12 @@ partition_rbound_cmp(PartitionKey key,
|
|||||||
/*
|
/*
|
||||||
* partition_rbound_datum_cmp
|
* partition_rbound_datum_cmp
|
||||||
*
|
*
|
||||||
* Return whether range bound (specified in rb_datums, rb_content, and
|
* Return whether range bound (specified in rb_datums, rb_kind, and rb_lower)
|
||||||
* rb_lower) <=, =, >= partition key of tuple (tuple_datums)
|
* is <, =, or > partition key of tuple (tuple_datums)
|
||||||
*/
|
*/
|
||||||
static int32
|
static int32
|
||||||
partition_rbound_datum_cmp(PartitionKey key,
|
partition_rbound_datum_cmp(PartitionKey key,
|
||||||
Datum *rb_datums, RangeDatumContent *rb_content,
|
Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
|
||||||
Datum *tuple_datums)
|
Datum *tuple_datums)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -2221,8 +2204,10 @@ partition_rbound_datum_cmp(PartitionKey key,
|
|||||||
|
|
||||||
for (i = 0; i < key->partnatts; i++)
|
for (i = 0; i < key->partnatts; i++)
|
||||||
{
|
{
|
||||||
if (rb_content[i] != RANGE_DATUM_FINITE)
|
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
|
||||||
return rb_content[i] == RANGE_DATUM_NEG_INF ? -1 : 1;
|
return -1;
|
||||||
|
else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
|
||||||
|
return 1;
|
||||||
|
|
||||||
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
|
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
|
||||||
key->partcollation[i],
|
key->partcollation[i],
|
||||||
@ -2238,7 +2223,7 @@ partition_rbound_datum_cmp(PartitionKey key,
|
|||||||
/*
|
/*
|
||||||
* partition_bound_cmp
|
* partition_bound_cmp
|
||||||
*
|
*
|
||||||
* Return whether the bound at offset in boundinfo is <=, =, >= the argument
|
* Return whether the bound at offset in boundinfo is <, =, or > the argument
|
||||||
* specified in *probe.
|
* specified in *probe.
|
||||||
*/
|
*/
|
||||||
static int32
|
static int32
|
||||||
@ -2259,7 +2244,7 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
|
|||||||
|
|
||||||
case PARTITION_STRATEGY_RANGE:
|
case PARTITION_STRATEGY_RANGE:
|
||||||
{
|
{
|
||||||
RangeDatumContent *content = boundinfo->content[offset];
|
PartitionRangeDatumKind *kind = boundinfo->kind[offset];
|
||||||
|
|
||||||
if (probe_is_bound)
|
if (probe_is_bound)
|
||||||
{
|
{
|
||||||
@ -2271,12 +2256,12 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
|
|||||||
bool lower = boundinfo->indexes[offset] < 0;
|
bool lower = boundinfo->indexes[offset] < 0;
|
||||||
|
|
||||||
cmpval = partition_rbound_cmp(key,
|
cmpval = partition_rbound_cmp(key,
|
||||||
bound_datums, content, lower,
|
bound_datums, kind, lower,
|
||||||
(PartitionRangeBound *) probe);
|
(PartitionRangeBound *) probe);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cmpval = partition_rbound_datum_cmp(key,
|
cmpval = partition_rbound_datum_cmp(key,
|
||||||
bound_datums, content,
|
bound_datums, kind,
|
||||||
(Datum *) probe);
|
(Datum *) probe);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4458,7 +4458,7 @@ _copyPartitionRangeDatum(const PartitionRangeDatum *from)
|
|||||||
{
|
{
|
||||||
PartitionRangeDatum *newnode = makeNode(PartitionRangeDatum);
|
PartitionRangeDatum *newnode = makeNode(PartitionRangeDatum);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(infinite);
|
COPY_SCALAR_FIELD(kind);
|
||||||
COPY_NODE_FIELD(value);
|
COPY_NODE_FIELD(value);
|
||||||
COPY_LOCATION_FIELD(location);
|
COPY_LOCATION_FIELD(location);
|
||||||
|
|
||||||
|
@ -2849,7 +2849,7 @@ _equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *
|
|||||||
static bool
|
static bool
|
||||||
_equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b)
|
_equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b)
|
||||||
{
|
{
|
||||||
COMPARE_SCALAR_FIELD(infinite);
|
COMPARE_SCALAR_FIELD(kind);
|
||||||
COMPARE_NODE_FIELD(value);
|
COMPARE_NODE_FIELD(value);
|
||||||
COMPARE_LOCATION_FIELD(location);
|
COMPARE_LOCATION_FIELD(location);
|
||||||
|
|
||||||
|
@ -3582,7 +3582,7 @@ _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
|
|||||||
{
|
{
|
||||||
WRITE_NODE_TYPE("PARTITIONRANGEDATUM");
|
WRITE_NODE_TYPE("PARTITIONRANGEDATUM");
|
||||||
|
|
||||||
WRITE_BOOL_FIELD(infinite);
|
WRITE_ENUM_FIELD(kind, PartitionRangeDatumKind);
|
||||||
WRITE_NODE_FIELD(value);
|
WRITE_NODE_FIELD(value);
|
||||||
WRITE_LOCATION_FIELD(location);
|
WRITE_LOCATION_FIELD(location);
|
||||||
}
|
}
|
||||||
|
@ -2404,7 +2404,7 @@ _readPartitionRangeDatum(void)
|
|||||||
{
|
{
|
||||||
READ_LOCALS(PartitionRangeDatum);
|
READ_LOCALS(PartitionRangeDatum);
|
||||||
|
|
||||||
READ_BOOL_FIELD(infinite);
|
READ_ENUM_FIELD(kind, PartitionRangeDatumKind);
|
||||||
READ_NODE_FIELD(value);
|
READ_NODE_FIELD(value);
|
||||||
READ_LOCATION_FIELD(location);
|
READ_LOCATION_FIELD(location);
|
||||||
|
|
||||||
|
@ -2696,11 +2696,21 @@ range_datum_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
PartitionRangeDatum:
|
PartitionRangeDatum:
|
||||||
UNBOUNDED
|
MINVALUE
|
||||||
{
|
{
|
||||||
PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
|
PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
|
||||||
|
|
||||||
n->infinite = true;
|
n->kind = PARTITION_RANGE_DATUM_MINVALUE;
|
||||||
|
n->value = NULL;
|
||||||
|
n->location = @1;
|
||||||
|
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| MAXVALUE
|
||||||
|
{
|
||||||
|
PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
|
||||||
|
|
||||||
|
n->kind = PARTITION_RANGE_DATUM_MAXVALUE;
|
||||||
n->value = NULL;
|
n->value = NULL;
|
||||||
n->location = @1;
|
n->location = @1;
|
||||||
|
|
||||||
@ -2710,7 +2720,7 @@ PartitionRangeDatum:
|
|||||||
{
|
{
|
||||||
PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
|
PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
|
||||||
|
|
||||||
n->infinite = false;
|
n->kind = PARTITION_RANGE_DATUM_VALUE;
|
||||||
n->value = $1;
|
n->value = $1;
|
||||||
n->location = @1;
|
n->location = @1;
|
||||||
|
|
||||||
|
@ -3365,7 +3365,6 @@ transformPartitionBound(ParseState *pstate, Relation parent,
|
|||||||
*cell2;
|
*cell2;
|
||||||
int i,
|
int i,
|
||||||
j;
|
j;
|
||||||
bool seen_unbounded;
|
|
||||||
|
|
||||||
if (spec->strategy != PARTITION_STRATEGY_RANGE)
|
if (spec->strategy != PARTITION_STRATEGY_RANGE)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -3382,39 +3381,6 @@ transformPartitionBound(ParseState *pstate, Relation parent,
|
|||||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||||
errmsg("TO must specify exactly one value per partitioning column")));
|
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.
|
|
||||||
*/
|
|
||||||
seen_unbounded = false;
|
|
||||||
foreach(cell1, spec->lowerdatums)
|
|
||||||
{
|
|
||||||
PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum,
|
|
||||||
lfirst(cell1));
|
|
||||||
|
|
||||||
if (ldatum->infinite)
|
|
||||||
seen_unbounded = true;
|
|
||||||
else if (seen_unbounded)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("cannot specify finite value after UNBOUNDED"),
|
|
||||||
parser_errposition(pstate, exprLocation((Node *) ldatum))));
|
|
||||||
}
|
|
||||||
seen_unbounded = false;
|
|
||||||
foreach(cell1, spec->upperdatums)
|
|
||||||
{
|
|
||||||
PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum,
|
|
||||||
lfirst(cell1));
|
|
||||||
|
|
||||||
if (rdatum->infinite)
|
|
||||||
seen_unbounded = true;
|
|
||||||
else if (seen_unbounded)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("cannot specify finite value after UNBOUNDED"),
|
|
||||||
parser_errposition(pstate, exprLocation((Node *) rdatum))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transform all the constants */
|
/* Transform all the constants */
|
||||||
i = j = 0;
|
i = j = 0;
|
||||||
result_spec->lowerdatums = result_spec->upperdatums = NIL;
|
result_spec->lowerdatums = result_spec->upperdatums = NIL;
|
||||||
|
@ -8715,8 +8715,10 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
castNode(PartitionRangeDatum, lfirst(cell));
|
castNode(PartitionRangeDatum, lfirst(cell));
|
||||||
|
|
||||||
appendStringInfoString(buf, sep);
|
appendStringInfoString(buf, sep);
|
||||||
if (datum->infinite)
|
if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
|
||||||
appendStringInfoString(buf, "UNBOUNDED");
|
appendStringInfoString(buf, "MINVALUE");
|
||||||
|
else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
|
||||||
|
appendStringInfoString(buf, "MAXVALUE");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Const *val = castNode(Const, datum->value);
|
Const *val = castNode(Const, datum->value);
|
||||||
@ -8733,8 +8735,10 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
castNode(PartitionRangeDatum, lfirst(cell));
|
castNode(PartitionRangeDatum, lfirst(cell));
|
||||||
|
|
||||||
appendStringInfoString(buf, sep);
|
appendStringInfoString(buf, sep);
|
||||||
if (datum->infinite)
|
if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
|
||||||
appendStringInfoString(buf, "UNBOUNDED");
|
appendStringInfoString(buf, "MINVALUE");
|
||||||
|
else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
|
||||||
|
appendStringInfoString(buf, "MAXVALUE");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Const *val = castNode(Const, datum->value);
|
Const *val = castNode(Const, datum->value);
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201706241
|
#define CATALOG_VERSION_NO 201707211
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -809,16 +809,24 @@ typedef struct PartitionBoundSpec
|
|||||||
} PartitionBoundSpec;
|
} PartitionBoundSpec;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PartitionRangeDatum - can be either a value or UNBOUNDED
|
* PartitionRangeDatum - one of the values in a range partition bound
|
||||||
*
|
*
|
||||||
* "value" is an A_Const in raw grammar output, a Const after analysis
|
* This can be MINVALUE, MAXVALUE or a specific bounded value.
|
||||||
*/
|
*/
|
||||||
|
typedef enum PartitionRangeDatumKind
|
||||||
|
{
|
||||||
|
PARTITION_RANGE_DATUM_MINVALUE = -1, /* less than any other value */
|
||||||
|
PARTITION_RANGE_DATUM_VALUE = 0, /* a specific (bounded) value */
|
||||||
|
PARTITION_RANGE_DATUM_MAXVALUE = 1 /* greater than any other value */
|
||||||
|
} PartitionRangeDatumKind;
|
||||||
|
|
||||||
typedef struct PartitionRangeDatum
|
typedef struct PartitionRangeDatum
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
|
|
||||||
bool infinite; /* true if UNBOUNDED */
|
PartitionRangeDatumKind kind;
|
||||||
Node *value; /* null if UNBOUNDED */
|
Node *value; /* Const (or A_Const in raw tree), if kind is
|
||||||
|
* PARTITION_RANGE_DATUM_VALUE, else NULL */
|
||||||
|
|
||||||
int location; /* token location, or -1 if unknown */
|
int location; /* token location, or -1 if unknown */
|
||||||
} PartitionRangeDatum;
|
} PartitionRangeDatum;
|
||||||
|
@ -512,15 +512,8 @@ 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);
|
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
|
ERROR: TO must specify exactly one value per partitioning column
|
||||||
-- cannot specify null values in range bounds
|
-- 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 (maxvalue);
|
||||||
ERROR: cannot specify NULL in range bound
|
ERROR: cannot specify NULL in range bound
|
||||||
-- cannot specify finite values after UNBOUNDED 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...
|
|
||||||
^
|
|
||||||
DROP TABLE range_parted_multicol;
|
|
||||||
-- check if compatible with the specified parent
|
-- check if compatible with the specified parent
|
||||||
-- cannot create as partition of a non-partitioned table
|
-- cannot create as partition of a non-partitioned table
|
||||||
CREATE TABLE unparted (
|
CREATE TABLE unparted (
|
||||||
@ -578,11 +571,11 @@ ERROR: cannot create range partition with empty range
|
|||||||
-- note that the range '[1, 1)' has no elements
|
-- 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 fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (1);
|
||||||
ERROR: cannot create range partition with empty range
|
ERROR: cannot create range partition with empty range
|
||||||
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (1);
|
CREATE TABLE part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (2);
|
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
|
||||||
ERROR: partition "fail_part" would overlap partition "part0"
|
ERROR: partition "fail_part" would overlap partition "part0"
|
||||||
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
|
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 (maxvalue);
|
||||||
ERROR: partition "fail_part" would overlap partition "part1"
|
ERROR: partition "fail_part" would overlap partition "part1"
|
||||||
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
|
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 part3 PARTITION OF range_parted2 FOR VALUES FROM (30) TO (40);
|
||||||
@ -595,18 +588,18 @@ CREATE TABLE range_parted3 (
|
|||||||
a int,
|
a int,
|
||||||
b int
|
b int
|
||||||
) PARTITION BY RANGE (a, (b+1));
|
) PARTITION BY RANGE (a, (b+1));
|
||||||
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, unbounded);
|
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, 1);
|
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, 1);
|
||||||
ERROR: partition "fail_part" would overlap partition "part00"
|
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, minvalue) TO (1, 1);
|
||||||
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
|
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, maxvalue);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
|
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
|
||||||
ERROR: partition "fail_part" would overlap partition "part12"
|
ERROR: partition "fail_part" would overlap partition "part12"
|
||||||
-- cannot create a partition that says column b is allowed to range
|
-- cannot create a partition that says column b is allowed to range
|
||||||
-- from -infinity to +infinity, while there exist partitions that have
|
-- from -infinity to +infinity, while there exist partitions that have
|
||||||
-- more specific ranges
|
-- 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, minvalue) TO (1, maxvalue);
|
||||||
ERROR: partition "fail_part" would overlap partition "part10"
|
ERROR: partition "fail_part" would overlap partition "part10"
|
||||||
-- check schema propagation from parent
|
-- check schema propagation from parent
|
||||||
CREATE TABLE parted (
|
CREATE TABLE parted (
|
||||||
@ -708,7 +701,7 @@ Number of partitions: 3 (Use \d+ to list them.)
|
|||||||
|
|
||||||
-- check that we get the expected partition constraints
|
-- 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 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 (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
|
||||||
\d+ unbounded_range_part
|
\d+ unbounded_range_part
|
||||||
Table "public.unbounded_range_part"
|
Table "public.unbounded_range_part"
|
||||||
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
@ -716,11 +709,11 @@ CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UN
|
|||||||
a | integer | | | | plain | |
|
a | integer | | | | plain | |
|
||||||
b | integer | | | | plain | |
|
b | integer | | | | plain | |
|
||||||
c | 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 (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0)
|
||||||
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
|
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL))
|
||||||
|
|
||||||
DROP TABLE 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 (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
|
||||||
\d+ range_parted4_1
|
\d+ range_parted4_1
|
||||||
Table "public.range_parted4_1"
|
Table "public.range_parted4_1"
|
||||||
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
@ -728,10 +721,10 @@ CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUND
|
|||||||
a | integer | | | | plain | |
|
a | integer | | | | plain | |
|
||||||
b | integer | | | | plain | |
|
b | integer | | | | plain | |
|
||||||
c | 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 (MINVALUE, 0, 0) TO (1, MAXVALUE, 0)
|
||||||
Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
|
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, MAXVALUE);
|
||||||
\d+ range_parted4_2
|
\d+ range_parted4_2
|
||||||
Table "public.range_parted4_2"
|
Table "public.range_parted4_2"
|
||||||
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
@ -739,10 +732,10 @@ CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5
|
|||||||
a | integer | | | | plain | |
|
a | integer | | | | plain | |
|
||||||
b | integer | | | | plain | |
|
b | integer | | | | plain | |
|
||||||
c | 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, MAXVALUE)
|
||||||
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))))
|
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, MINVALUE) TO (9, MAXVALUE, 0);
|
||||||
\d+ range_parted4_3
|
\d+ range_parted4_3
|
||||||
Table "public.range_parted4_3"
|
Table "public.range_parted4_3"
|
||||||
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
@ -750,7 +743,7 @@ CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, U
|
|||||||
a | integer | | | | plain | |
|
a | integer | | | | plain | |
|
||||||
b | integer | | | | plain | |
|
b | integer | | | | plain | |
|
||||||
c | 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, MINVALUE) TO (9, MAXVALUE, 0)
|
||||||
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))
|
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;
|
DROP TABLE range_parted4;
|
||||||
|
@ -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 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_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_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 (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_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_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);
|
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
|
-- check that constraint exclusion is able to cope with the partition
|
||||||
-- constraint emitted for multi-column range partitioned tables
|
-- 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 (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 (minvalue, 0, 0) to (1, 1, 1);
|
||||||
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
|
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 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 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 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 (maxvalue, 0, 0);
|
||||||
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
|
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
------------------------------
|
------------------------------
|
||||||
|
@ -288,7 +288,7 @@ select tableoid::regclass, * from list_parted;
|
|||||||
|
|
||||||
-- some more tests to exercise tuple-routing with multi-level partitioning
|
-- 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_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 (minvalue) to (1);
|
||||||
create table part_gg2 partition of part_gg for values from (1) to (10) partition by range (b);
|
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_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);
|
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
|
-- check multi-column range partitioning expression enforces the same
|
||||||
-- constraint as what tuple-routing would determine it to be
|
-- 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 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 mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, maxvalue, 0);
|
||||||
create table mcrparted1 partition of mcrparted for values from (2, 1, unbounded) to (10, 5, 10);
|
create table mcrparted1 partition of mcrparted for values from (2, 1, minvalue) to (10, 5, 10);
|
||||||
create table mcrparted2 partition of mcrparted for values from (10, 6, unbounded) to (10, unbounded, unbounded);
|
create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, 0);
|
||||||
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 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 (21, unbounded, unbounded) to (30, 20, unbounded);
|
create table mcrparted4 partition of mcrparted for values from (21, minvalue, 0) to (30, 20, maxvalue);
|
||||||
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (unbounded, unbounded, unbounded);
|
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (maxvalue, 0, 0);
|
||||||
-- routed to mcrparted0
|
-- routed to mcrparted0
|
||||||
insert into mcrparted values (0, 1, 1);
|
insert into mcrparted values (0, 1, 1);
|
||||||
insert into mcrparted0 values (0, 1, 1);
|
insert into mcrparted0 values (0, 1, 1);
|
||||||
@ -526,3 +526,121 @@ drop role regress_coldesc_role;
|
|||||||
drop table inserttest3;
|
drop table inserttest3;
|
||||||
drop table brtrigpartcon;
|
drop table brtrigpartcon;
|
||||||
drop function brtrigpartcon1trigf();
|
drop function brtrigpartcon1trigf();
|
||||||
|
-- check multi-column range partitioning with minvalue/maxvalue constraints
|
||||||
|
create table mcrparted (a text, b int) partition by range(a, b);
|
||||||
|
create table mcrparted_lt_b partition of mcrparted for values from (minvalue, 0) to ('b', minvalue);
|
||||||
|
create table mcrparted_b partition of mcrparted for values from ('b', minvalue) to ('c', minvalue);
|
||||||
|
create table mcrparted_c_to_common partition of mcrparted for values from ('c', minvalue) to ('common', minvalue);
|
||||||
|
create table mcrparted_common_lt_0 partition of mcrparted for values from ('common', minvalue) to ('common', 0);
|
||||||
|
create table mcrparted_common_0_to_10 partition of mcrparted for values from ('common', 0) to ('common', 10);
|
||||||
|
create table mcrparted_common_ge_10 partition of mcrparted for values from ('common', 10) to ('common', maxvalue);
|
||||||
|
create table mcrparted_gt_common_lt_d partition of mcrparted for values from ('common', maxvalue) to ('d', minvalue);
|
||||||
|
create table mcrparted_ge_d partition of mcrparted for values from ('d', minvalue) to (maxvalue, 0);
|
||||||
|
\d+ mcrparted
|
||||||
|
Table "public.mcrparted"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition key: RANGE (a, b)
|
||||||
|
Partitions: mcrparted_b FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE),
|
||||||
|
mcrparted_common_0_to_10 FOR VALUES FROM ('common', 0) TO ('common', 10),
|
||||||
|
mcrparted_common_ge_10 FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE),
|
||||||
|
mcrparted_common_lt_0 FOR VALUES FROM ('common', MINVALUE) TO ('common', 0),
|
||||||
|
mcrparted_c_to_common FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE),
|
||||||
|
mcrparted_ge_d FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0),
|
||||||
|
mcrparted_gt_common_lt_d FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE),
|
||||||
|
mcrparted_lt_b FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE)
|
||||||
|
|
||||||
|
\d+ mcrparted_lt_b
|
||||||
|
Table "public.mcrparted_lt_b"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM (MINVALUE, 0) TO ('b', MINVALUE)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a < 'b'::text))
|
||||||
|
|
||||||
|
\d+ mcrparted_b
|
||||||
|
Table "public.mcrparted_b"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('b', MINVALUE) TO ('c', MINVALUE)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'b'::text) AND (a < 'c'::text))
|
||||||
|
|
||||||
|
\d+ mcrparted_c_to_common
|
||||||
|
Table "public.mcrparted_c_to_common"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('c', MINVALUE) TO ('common', MINVALUE)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'c'::text) AND (a < 'common'::text))
|
||||||
|
|
||||||
|
\d+ mcrparted_common_lt_0
|
||||||
|
Table "public.mcrparted_common_lt_0"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('common', MINVALUE) TO ('common', 0)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b < 0))
|
||||||
|
|
||||||
|
\d+ mcrparted_common_0_to_10
|
||||||
|
Table "public.mcrparted_common_0_to_10"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('common', 0) TO ('common', 10)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 0) AND (b < 10))
|
||||||
|
|
||||||
|
\d+ mcrparted_common_ge_10
|
||||||
|
Table "public.mcrparted_common_ge_10"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('common', 10) TO ('common', MAXVALUE)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a = 'common'::text) AND (b >= 10))
|
||||||
|
|
||||||
|
\d+ mcrparted_gt_common_lt_d
|
||||||
|
Table "public.mcrparted_gt_common_lt_d"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('common', MAXVALUE) TO ('d', MINVALUE)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a > 'common'::text) AND (a < 'd'::text))
|
||||||
|
|
||||||
|
\d+ mcrparted_ge_d
|
||||||
|
Table "public.mcrparted_ge_d"
|
||||||
|
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||||
|
--------+---------+-----------+----------+---------+----------+--------------+-------------
|
||||||
|
a | text | | | | extended | |
|
||||||
|
b | integer | | | | plain | |
|
||||||
|
Partition of: mcrparted FOR VALUES FROM ('d', MINVALUE) TO (MAXVALUE, 0)
|
||||||
|
Partition constraint: ((a IS NOT NULL) AND (b IS NOT NULL) AND (a >= 'd'::text))
|
||||||
|
|
||||||
|
insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),
|
||||||
|
('comm', -10), ('common', -10), ('common', 0), ('common', 10),
|
||||||
|
('commons', 0), ('d', -10), ('e', 0);
|
||||||
|
select tableoid::regclass, * from mcrparted order by a, b;
|
||||||
|
tableoid | a | b
|
||||||
|
--------------------------+---------+-----
|
||||||
|
mcrparted_lt_b | aaa | 0
|
||||||
|
mcrparted_b | b | 0
|
||||||
|
mcrparted_b | bz | 10
|
||||||
|
mcrparted_c_to_common | c | -10
|
||||||
|
mcrparted_c_to_common | comm | -10
|
||||||
|
mcrparted_common_lt_0 | common | -10
|
||||||
|
mcrparted_common_0_to_10 | common | 0
|
||||||
|
mcrparted_common_ge_10 | common | 10
|
||||||
|
mcrparted_gt_common_lt_d | commons | 0
|
||||||
|
mcrparted_ge_d | d | -10
|
||||||
|
mcrparted_ge_d | e | 0
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
drop table mcrparted;
|
||||||
|
@ -483,12 +483,7 @@ 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);
|
CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a') TO ('z', 1);
|
||||||
|
|
||||||
-- cannot specify null values in range bounds
|
-- 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 (maxvalue);
|
||||||
|
|
||||||
-- cannot specify finite values after UNBOUNDED 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);
|
|
||||||
DROP TABLE range_parted_multicol;
|
|
||||||
|
|
||||||
-- check if compatible with the specified parent
|
-- check if compatible with the specified parent
|
||||||
|
|
||||||
@ -542,10 +537,10 @@ CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (1) TO (0);
|
|||||||
-- note that the range '[1, 1)' has no elements
|
-- 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 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 part0 PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (1);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (unbounded) TO (2);
|
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (minvalue) TO (2);
|
||||||
CREATE TABLE part1 PARTITION OF range_parted2 FOR VALUES FROM (1) TO (10);
|
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 (maxvalue);
|
||||||
CREATE TABLE part2 PARTITION OF range_parted2 FOR VALUES FROM (20) TO (30);
|
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 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);
|
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
|
||||||
@ -557,18 +552,18 @@ CREATE TABLE range_parted3 (
|
|||||||
b int
|
b int
|
||||||
) PARTITION BY RANGE (a, (b+1));
|
) PARTITION BY RANGE (a, (b+1));
|
||||||
|
|
||||||
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, unbounded);
|
CREATE TABLE part00 PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) TO (0, maxvalue);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, unbounded) TO (0, 1);
|
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0, minvalue) 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, minvalue) TO (1, 1);
|
||||||
CREATE TABLE part11 PARTITION OF range_parted3 FOR VALUES FROM (1, 1) TO (1, 10);
|
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, maxvalue);
|
||||||
CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (1, 10) TO (1, 20);
|
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
|
-- cannot create a partition that says column b is allowed to range
|
||||||
-- from -infinity to +infinity, while there exist partitions that have
|
-- from -infinity to +infinity, while there exist partitions that have
|
||||||
-- more specific ranges
|
-- 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, minvalue) TO (1, maxvalue);
|
||||||
|
|
||||||
-- check schema propagation from parent
|
-- check schema propagation from parent
|
||||||
|
|
||||||
@ -626,14 +621,14 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
|
|||||||
|
|
||||||
-- check that we get the expected partition constraints
|
-- 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 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 (MINVALUE, 0, 0) TO (MAXVALUE, 0, 0);
|
||||||
\d+ unbounded_range_part
|
\d+ unbounded_range_part
|
||||||
DROP TABLE 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 (MINVALUE, 0, 0) TO (1, MAXVALUE, 0);
|
||||||
\d+ range_parted4_1
|
\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, MAXVALUE);
|
||||||
\d+ range_parted4_2
|
\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, MINVALUE) TO (9, MAXVALUE, 0);
|
||||||
\d+ range_parted4_3
|
\d+ range_parted4_3
|
||||||
DROP TABLE range_parted4;
|
DROP TABLE range_parted4;
|
||||||
|
|
||||||
|
@ -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 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_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_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 (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_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_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);
|
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
|
-- check that constraint exclusion is able to cope with the partition
|
||||||
-- constraint emitted for multi-column range partitioned tables
|
-- 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 (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 (minvalue, 0, 0) to (1, 1, 1);
|
||||||
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
|
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 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 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 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 (maxvalue, 0, 0);
|
||||||
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
|
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
|
||||||
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2
|
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2
|
||||||
|
@ -169,7 +169,7 @@ select tableoid::regclass, * from list_parted;
|
|||||||
|
|
||||||
-- some more tests to exercise tuple-routing with multi-level partitioning
|
-- 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_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 (minvalue) to (1);
|
||||||
create table part_gg2 partition of part_gg for values from (1) to (10) partition by range (b);
|
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_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);
|
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
|
-- check multi-column range partitioning expression enforces the same
|
||||||
-- constraint as what tuple-routing would determine it to be
|
-- 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 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 mcrparted0 partition of mcrparted for values from (minvalue, 0, 0) to (1, maxvalue, 0);
|
||||||
create table mcrparted1 partition of mcrparted for values from (2, 1, unbounded) to (10, 5, 10);
|
create table mcrparted1 partition of mcrparted for values from (2, 1, minvalue) to (10, 5, 10);
|
||||||
create table mcrparted2 partition of mcrparted for values from (10, 6, unbounded) to (10, unbounded, unbounded);
|
create table mcrparted2 partition of mcrparted for values from (10, 6, minvalue) to (10, maxvalue, 0);
|
||||||
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 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 (21, unbounded, unbounded) to (30, 20, unbounded);
|
create table mcrparted4 partition of mcrparted for values from (21, minvalue, 0) to (30, 20, maxvalue);
|
||||||
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (unbounded, unbounded, unbounded);
|
create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to (maxvalue, 0, 0);
|
||||||
|
|
||||||
-- routed to mcrparted0
|
-- routed to mcrparted0
|
||||||
insert into mcrparted values (0, 1, 1);
|
insert into mcrparted values (0, 1, 1);
|
||||||
@ -360,3 +360,30 @@ drop role regress_coldesc_role;
|
|||||||
drop table inserttest3;
|
drop table inserttest3;
|
||||||
drop table brtrigpartcon;
|
drop table brtrigpartcon;
|
||||||
drop function brtrigpartcon1trigf();
|
drop function brtrigpartcon1trigf();
|
||||||
|
|
||||||
|
-- check multi-column range partitioning with minvalue/maxvalue constraints
|
||||||
|
create table mcrparted (a text, b int) partition by range(a, b);
|
||||||
|
create table mcrparted_lt_b partition of mcrparted for values from (minvalue, 0) to ('b', minvalue);
|
||||||
|
create table mcrparted_b partition of mcrparted for values from ('b', minvalue) to ('c', minvalue);
|
||||||
|
create table mcrparted_c_to_common partition of mcrparted for values from ('c', minvalue) to ('common', minvalue);
|
||||||
|
create table mcrparted_common_lt_0 partition of mcrparted for values from ('common', minvalue) to ('common', 0);
|
||||||
|
create table mcrparted_common_0_to_10 partition of mcrparted for values from ('common', 0) to ('common', 10);
|
||||||
|
create table mcrparted_common_ge_10 partition of mcrparted for values from ('common', 10) to ('common', maxvalue);
|
||||||
|
create table mcrparted_gt_common_lt_d partition of mcrparted for values from ('common', maxvalue) to ('d', minvalue);
|
||||||
|
create table mcrparted_ge_d partition of mcrparted for values from ('d', minvalue) to (maxvalue, 0);
|
||||||
|
|
||||||
|
\d+ mcrparted
|
||||||
|
\d+ mcrparted_lt_b
|
||||||
|
\d+ mcrparted_b
|
||||||
|
\d+ mcrparted_c_to_common
|
||||||
|
\d+ mcrparted_common_lt_0
|
||||||
|
\d+ mcrparted_common_0_to_10
|
||||||
|
\d+ mcrparted_common_ge_10
|
||||||
|
\d+ mcrparted_gt_common_lt_d
|
||||||
|
\d+ mcrparted_ge_d
|
||||||
|
|
||||||
|
insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),
|
||||||
|
('comm', -10), ('common', -10), ('common', 0), ('common', 10),
|
||||||
|
('commons', 0), ('d', -10), ('e', 0);
|
||||||
|
select tableoid::regclass, * from mcrparted order by a, b;
|
||||||
|
drop table mcrparted;
|
||||||
|
@ -1562,6 +1562,7 @@ PartitionKey
|
|||||||
PartitionListValue
|
PartitionListValue
|
||||||
PartitionRangeBound
|
PartitionRangeBound
|
||||||
PartitionRangeDatum
|
PartitionRangeDatum
|
||||||
|
PartitionRangeDatumKind
|
||||||
PartitionSpec
|
PartitionSpec
|
||||||
PartitionedChildRelInfo
|
PartitionedChildRelInfo
|
||||||
PasswordType
|
PasswordType
|
||||||
|
Loading…
x
Reference in New Issue
Block a user