On 5 July 2017 at 10:43, Amit Langote <[email protected]> wrote:
>> So the more I think about this, the more I think that a cleaner design
>> would be as follows:
>>
>> 1). Don't allow UNBOUNDED, except in the first column, where it can
>> keep it's current meaning.
>>
>> 2). Allow the partition bounds to have fewer columns than the
>> partition definition, and have that mean the same as it would have
>> meant if you were partitioning by that many columns. So, for
>> example, if you were partitioning by (col1,col2), you'd be allowed
>> to define a partition like so:
>>
>> FROM (x) TO (y)
>>
>> and it would mean
>>
>> x <= col1 < y
>>
>> Or you'd be able to define a partition like
>>
>> FROM (x1,x2) TO (y)
>>
>> which would mean
>>
>> (col1 > x1) OR (col1 = x1 AND col2 >= x2) AND col1 < y
>>
>> 3). Don't allow any value after UNBOUNDED (i.e., only specify
>> UNBOUNDED once in a partition bound).
>
> I assume we don't need the ability of specifying ABOVE/BELOW in this design.
>
Yes that's right.
> In retrospect, that sounds like something that was implemented in the
> earlier versions of the patch, whereby there was no ability to specify
> UNBOUNDED on a per-column basis. So the syntax was:
>
> FROM { (x [, ...]) | UNBOUNDED } TO { (y [, ...]) | UNBOUNDED }
>
Yes, that's where I ended up too.
> But, it was pointed out to me [1] that that doesn't address the use case,
> for example, where part1 goes up to (10, 10) and part2 goes from (10, 10)
> up to (10, unbounded).
>
> The new design will limit the usage of unbounded range partitions at the
> tail ends.
>
True, but I don't think that's really a problem. When the first column
is a discrete type, an upper bound of (10, unbounded) can be rewritten
as (11) in the new design. When it's a continuous type, e.g. floating
point, it can no longer be represented, because (10.0, unbounded)
really means (col1 <= 10.0). But we've already decided not to support
anything other than inclusive lower bounds and exclusive upper bounds,
so allowing this upper bound goes against that design choice.
>> Of course, it's pretty late in the day to be proposing this kind of
>> redesign, but I fear that if we don't tackle it now, it will just be
>> harder to deal with in the future.
>>
>> Actually, a quick, simple hacky implementation might be to just fill
>> in any omitted values in a partition bound with negative infinity
>> internally, and when printing a bound, omit any values after an
>> infinite value. But really, I think we'd want to tidy up the
>> implementation, and I think a number of things would actually get much
>> simpler. For example, get_qual_for_range() could simply stop when it
>> reached the end of the list of values for the bound, and it wouldn't
>> need to worry about an unbounded value following a bounded one.
>>
>> Thoughts?
>
> I cooked up a patch for the "hacky" implementation for now, just as you
> described in the above paragraph. Will you be willing to give it a look?
> I will also think about the non-hacky way of implementing this.
>
OK, I'll take a look.
Meanwhile, I already had a go at the "non-hacky" implementation (WIP
patch attached). The more I worked on it, the simpler things got,
which I think is a good sign.
Part-way through, I realised that the PartitionRangeDatum Node type is
no longer needed, because each bound value is now necessarily finite,
so the lowerdatums and upperdatums lists in a PartitionBoundSpec can
now be made into lists of Const nodes, making them match the
listdatums field used for LIST partitioning, and then a whole lot of
related code gets simplified.
It needed a little bit more code in partition.c to track individual
bound sizes, but there were a number of other places that could be
simplified, so overall this represents a reduction in the code size
and complexity.
It's not complete (e.g., no doc updates yet), but it passes all the
tests, and so far seems to work as I would expect.
Regards,
Dean
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
new file mode 100644
index 7da2058..aade9f5
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -66,23 +66,26 @@
* is an upper bound.
*/
-/* Ternary value to represent what's contained in a range bound datum */
-typedef enum RangeDatumContent
+/* Type of an individual bound of a range partition */
+typedef enum RangeBoundKind
{
- RANGE_DATUM_FINITE = 0, /* actual datum stored elsewhere */
- RANGE_DATUM_NEG_INF, /* negative infinity */
- RANGE_DATUM_POS_INF /* positive infinity */
-} RangeDatumContent;
+ RANGE_BOUND_FINITE = 0, /* actual bound stored in the datums array */
+ RANGE_BOUND_NEG_INF, /* negative infinity; NULL datums array */
+ RANGE_BOUND_POS_INF /* positive infinity; NULL datums array */
+} RangeBoundKind;
typedef struct PartitionBoundInfoData
{
char strategy; /* list or range bounds? */
int ndatums; /* Length of the datums following array */
- Datum **datums; /* Array of datum-tuples with key->partnatts
- * datums each */
- RangeDatumContent **content; /* what's contained in each range bound
- * datum? (see the above enum); NULL for
+ Datum **datums; /* Array of datum-tuples with up to
+ * key->partnatts datums each */
+ RangeBoundKind *rbound_kind; /* The type of each range bound; one per
+ * member of the datums array; NULL for
* list partitioned tables */
+ int *rbound_ndatums; /* The number of datums in each range bound;
+ * one per member of the datums array; NULL
+ * for list partitioned tables */
int *indexes; /* Partition indexes; one entry per member of
* the datums array (plus one if range
* partitioned table) */
@@ -108,8 +111,11 @@ typedef struct PartitionListValue
typedef struct PartitionRangeBound
{
int index;
- Datum *datums; /* range bound datums */
- RangeDatumContent *content; /* what's contained in each datum? */
+ RangeBoundKind kind; /* type of range bound */
+ int ndatums; /* number of range bound datums; 0 for
+ * unbounded ranges */
+ Datum *datums; /* range bound datums; NULL for unbounded
+ * ranges */
bool lower; /* this is the lower (vs upper) bound */
} PartitionRangeBound;
@@ -123,11 +129,8 @@ static Oid get_partition_operator(Partit
static Expr *make_partition_op_expr(PartitionKey key, int keynum,
uint16 strategy, Expr *arg1, Expr *arg2);
static void get_range_key_properties(PartitionKey key, int keynum,
- PartitionRangeDatum *ldatum,
- PartitionRangeDatum *udatum,
ListCell **partexprs_item,
- Expr **keyCol,
- Const **lower_val, Const **upper_val);
+ Expr **keyCol);
static List *get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec);
static List *get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec);
static List *generate_partition_qual(Relation rel);
@@ -135,11 +138,11 @@ static List *generate_partition_qual(Rel
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
List *datums, bool lower);
static int32 partition_rbound_cmp(PartitionKey key,
- Datum *datums1, RangeDatumContent *content1, bool lower1,
- PartitionRangeBound *b2);
+ Datum *datums1, int ndatums1, RangeBoundKind kind1,
+ bool lower1, PartitionRangeBound *b2);
static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, RangeDatumContent *rb_content,
- Datum *tuple_datums);
+ Datum *rb_datums, int rb_ndatums,
+ RangeBoundKind rb_kind, Datum *tuple_datums);
static int32 partition_bound_cmp(PartitionKey key,
PartitionBoundInfo boundinfo,
@@ -366,36 +369,27 @@ RelationBuildPartitionDesc(Relation rel)
int j;
/* Is current bound is distinct from the previous? */
- for (j = 0; j < key->partnatts; j++)
+ if (prev == NULL ||
+ cur->kind != prev->kind ||
+ cur->ndatums != prev->ndatums)
{
- Datum cmpval;
-
- if (prev == NULL)
+ is_distinct = true;
+ }
+ else
+ {
+ for (j = 0; j < cur->ndatums; j++)
{
- is_distinct = true;
- break;
- }
+ Datum cmpval;
- /*
- * 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 (cur->content[j] != RANGE_DATUM_FINITE ||
- prev->content[j] != RANGE_DATUM_FINITE)
- {
- is_distinct = true;
- break;
- }
- cmpval = FunctionCall2Coll(&key->partsupfunc[j],
- key->partcollation[j],
- cur->datums[j],
- prev->datums[j]);
- if (DatumGetInt32(cmpval) != 0)
- {
- is_distinct = true;
- break;
+ cmpval = FunctionCall2Coll(&key->partsupfunc[j],
+ key->partcollation[j],
+ cur->datums[j],
+ prev->datums[j]);
+ if (DatumGetInt32(cmpval) != 0)
+ {
+ is_distinct = true;
+ break;
+ }
}
}
@@ -454,8 +448,8 @@ RelationBuildPartitionDesc(Relation rel)
palloc0(sizeof(PartitionBoundInfoData));
boundinfo->strategy = key->strategy;
boundinfo->ndatums = ndatums;
- boundinfo->null_index = -1;
boundinfo->datums = (Datum **) palloc0(ndatums * sizeof(Datum *));
+ boundinfo->null_index = -1;
/* Initialize mapping array with invalid values */
mapping = (int *) palloc(sizeof(int) * nparts);
@@ -512,8 +506,11 @@ RelationBuildPartitionDesc(Relation rel)
case PARTITION_STRATEGY_RANGE:
{
- boundinfo->content = (RangeDatumContent **) palloc(ndatums *
- sizeof(RangeDatumContent *));
+ boundinfo->rbound_kind = (RangeBoundKind *)
+ palloc(ndatums *
+ sizeof(RangeBoundKind));
+ boundinfo->rbound_ndatums = (int *) palloc(ndatums *
+ sizeof(int));
boundinfo->indexes = (int *) palloc((ndatums + 1) *
sizeof(int));
@@ -521,20 +518,20 @@ RelationBuildPartitionDesc(Relation rel)
{
int j;
- boundinfo->datums[i] = (Datum *) palloc(key->partnatts *
- sizeof(Datum));
- boundinfo->content[i] = (RangeDatumContent *)
- palloc(key->partnatts *
- sizeof(RangeDatumContent));
- for (j = 0; j < key->partnatts; j++)
+ boundinfo->rbound_kind[i] = rbounds[i]->kind;
+ boundinfo->rbound_ndatums[i] = rbounds[i]->ndatums;
+ if (rbounds[i]->ndatums > 0)
{
- if (rbounds[i]->content[j] == RANGE_DATUM_FINITE)
+ boundinfo->datums[i] = (Datum *) palloc(rbounds[i]->ndatums *
+ sizeof(Datum));
+
+ for (j = 0; j < rbounds[i]->ndatums; j++)
+ {
boundinfo->datums[i][j] =
datumCopy(rbounds[i]->datums[j],
key->parttypbyval[j],
key->parttyplen[j]);
- /* Remember, we are storing the tri-state value. */
- boundinfo->content[i][j] = rbounds[i]->content[j];
+ }
}
/*
@@ -611,25 +608,30 @@ partition_bounds_equal(PartitionKey key,
for (i = 0; i < b1->ndatums; i++)
{
+ int ndatums;
int j;
- for (j = 0; j < key->partnatts; j++)
+ if (key->strategy == PARTITION_STRATEGY_RANGE)
{
- /* For range partitions, the bounds might not be finite. */
- if (b1->content != NULL)
- {
- /*
- * A finite bound always differs from an infinite bound, and
- * different kinds of infinities differ from each other.
- */
- if (b1->content[i][j] != b2->content[i][j])
- return false;
+ /* Fields only used for range partitions */
+ if (b1->rbound_kind[i] != b2->rbound_kind[i])
+ return false;
- /* Non-finite bounds are equal without further examination. */
- if (b1->content[i][j] != RANGE_DATUM_FINITE)
- continue;
- }
+ if (b1->rbound_ndatums[i] != b2->rbound_ndatums[i])
+ return false;
+ /* Number of datums can vary by bound */
+ ndatums = b1->rbound_ndatums[i];
+ }
+ else
+ /* Number of datums is the same for each bound */
+ ndatums = key->partnatts;
+
+ if (b1->indexes[i] != b2->indexes[i])
+ return false;
+
+ for (j = 0; j < ndatums; j++)
+ {
/*
* Compare the actual values. Note that it would be both incorrect
* and unsafe to invoke the comparison operator derived from the
@@ -646,9 +648,6 @@ partition_bounds_equal(PartitionKey key,
key->parttyplen[j]))
return false;
}
-
- if (b1->indexes[i] != b2->indexes[i])
- return false;
}
/* There are ndatums+1 indexes in case of range partitions */
@@ -735,8 +734,8 @@ check_new_partition_bound(char *relname,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key, lower->datums, lower->content, true,
- upper) >= 0)
+ if (partition_rbound_cmp(key, lower->datums, lower->ndatums,
+ lower->kind, true, upper) >= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot create range partition with empty range"),
@@ -745,78 +744,62 @@ check_new_partition_bound(char *relname,
if (partdesc->nparts > 0)
{
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- int off1,
- off2;
- bool equal = false;
+ int offset;
+ bool equal;
Assert(boundinfo && boundinfo->ndatums > 0 &&
boundinfo->strategy == PARTITION_STRATEGY_RANGE);
/*
- * Firstly, find the greatest range bound that is less
- * than or equal to the new lower bound.
+ * Test whether the new lower bound (which is treated
+ * inclusively as part of the new partition) lies inside an
+ * existing partition, or in a gap.
+ *
+ * If it's in a gap, the next index value will be -1 (the
+ * lower bound of the next partition). This is also true
+ * if there is no next partition, since the index array is
+ * initialised with an extra -1 at the end.
+ *
+ * Note that this also allows for the possibility that the
+ * new lower bound equals an existing upper bound.
*/
- off1 = partition_bound_bsearch(key, boundinfo, lower, true,
- &equal);
+ offset = partition_bound_bsearch(key, boundinfo, lower,
+ true, &equal);
- /*
- * off1 == -1 means that all existing bounds are greater
- * than the new lower bound. In that case and the case
- * where no partition is defined between the bounds at
- * off1 and off1 + 1, we have a "gap" in the range that
- * could be occupied by the new partition. We confirm if
- * so by checking whether the new upper bound is confined
- * within the gap.
- */
- if (!equal && boundinfo->indexes[off1 + 1] < 0)
+ if (boundinfo->indexes[offset + 1] < 0)
{
- off2 = partition_bound_bsearch(key, boundinfo, upper,
- true, &equal);
-
/*
- * If the new upper bound is returned to be equal to
- * the bound at off2, the latter must be the upper
- * bound of some partition with which the new
- * partition clearly overlaps.
- *
- * Also, if bound at off2 is not same as the one
- * returned for the new lower bound (IOW, off1 !=
- * off2), then the new partition overlaps at least one
- * partition.
+ * Check that the new partition will fit in the gap.
+ * For it to fit, the new upper bound must be less than
+ * or equal to the lower bound of the next partition,
+ * if there is one.
*/
- if (equal || off1 != off2)
+ if (offset + 1 < boundinfo->ndatums)
{
- overlap = true;
+ int32 cmpval;
- /*
- * The bound at off2 could be the lower bound of
- * the partition with which the new partition
- * overlaps. In that case, use the upper bound
- * (that is, the bound at off2 + 1) to get the
- * index of that partition.
- */
- if (boundinfo->indexes[off2] < 0)
- with = boundinfo->indexes[off2 + 1];
- else
- with = boundinfo->indexes[off2];
+ cmpval = partition_bound_cmp(key, boundinfo,
+ offset + 1, upper,
+ true);
+ if (cmpval < 0)
+ {
+ /*
+ * The new partition overlaps with the existing
+ * partition between offset + 1 and offset + 2.
+ */
+ overlap = true;
+ with = boundinfo->indexes[offset + 2];
+ }
}
}
else
{
/*
- * Equal has been set to true and there is no "gap"
- * between the bound at off1 and that at off1 + 1, so
- * the new partition will overlap some partition. In
- * the former case, the new lower bound is found to be
- * equal to the bound at off1, which could only ever
- * be true if the latter is the lower bound of some
- * partition. It's clear in such a case that the new
- * partition overlaps that partition, whose index we
- * get using its upper bound (that is, using the bound
- * at off1 + 1).
+ * The new partition overlaps with the existing
+ * partition between offset and offset + 1.
*/
overlap = true;
- with = boundinfo->indexes[off1 + 1];
+ with = boundinfo->indexes[offset + 1];
}
}
@@ -1410,21 +1393,15 @@ get_qual_for_list(PartitionKey key, Part
* This is a subroutine for get_qual_for_range, and its API is pretty
* specialized to that caller.
*
- * 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.
- * All of these structures are freshly palloc'd.
+ * Constructs an Expr for the key column (returned in *keyCol).
*
* *partexprs_item points to the cell containing the next expression in
* the key->partexprs list, or NULL. It may be advanced upon return.
*/
static void
get_range_key_properties(PartitionKey key, int keynum,
- PartitionRangeDatum *ldatum,
- PartitionRangeDatum *udatum,
ListCell **partexprs_item,
- Expr **keyCol,
- Const **lower_val, Const **upper_val)
+ Expr **keyCol)
{
/* Get partition key expression for this column */
if (key->partattrs[keynum] != 0)
@@ -1443,17 +1420,6 @@ get_range_key_properties(PartitionKey ke
*keyCol = copyObject(lfirst(*partexprs_item));
*partexprs_item = lnext(*partexprs_item);
}
-
- /* Get appropriate Const nodes for the bounds */
- if (!ldatum->infinite)
- *lower_val = castNode(Const, copyObject(ldatum->value));
- else
- *lower_val = NULL;
-
- if (!udatum->infinite)
- *upper_val = castNode(Const, copyObject(udatum->value));
- else
- *upper_val = NULL;
}
/*
@@ -1484,17 +1450,16 @@ get_range_key_properties(PartitionKey ke
* 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:
- *
- * (b < bu) OR (b = bu), which is simplified to (b <= bu)
+ * The upper and lower bound tuples may also contain fewer values than the
+ * number of columns in the partition key, in which case the relevant
+ * expression is shortened.
*
* 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
*
- * 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 either bound is UNBOUNDED, the matching bound tuples list is empty and
+ * 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
* containing a constant TRUE, because callers expect a non-empty list.
@@ -1509,8 +1474,6 @@ get_qual_for_range(PartitionKey key, Par
*partexprs_item_saved;
int i,
j;
- PartitionRangeDatum *ldatum,
- *udatum;
Expr *keyCol;
Const *lower_val,
*upper_val;
@@ -1582,8 +1545,8 @@ get_qual_for_range(PartitionKey key, Par
Datum test_result;
bool isNull;
- ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
- udatum = castNode(PartitionRangeDatum, lfirst(cell2));
+ lower_val = castNode(Const, lfirst(cell1));
+ upper_val = castNode(Const, lfirst(cell2));
/*
* Since get_range_key_properties() modifies partexprs_item, and we
@@ -1592,18 +1555,7 @@ get_qual_for_range(PartitionKey key, Par
*/
partexprs_item_saved = partexprs_item;
- get_range_key_properties(key, i, ldatum, udatum,
- &partexprs_item,
- &keyCol,
- &lower_val, &upper_val);
-
- /*
- * If either or both of lower_val and upper_val is NULL, they are
- * unequal, because being NULL means the column is unbounded in the
- * respective direction.
- */
- if (!lower_val || !upper_val)
- break;
+ get_range_key_properties(key, i, &partexprs_item, &keyCol);
/* Create the test expression */
estate = CreateExecutorState();
@@ -1643,7 +1595,7 @@ get_qual_for_range(PartitionKey key, Par
lower_or_start_datum = cell1;
upper_or_start_datum = cell2;
- /* OR will have as many arms as there are key columns left. */
+ /* OR will have up to as many arms as there are key columns left. */
num_or_arms = key->partnatts - i;
current_or_arm = 0;
lower_or_arms = upper_or_arms = NIL;
@@ -1653,27 +1605,23 @@ get_qual_for_range(PartitionKey key, Par
List *lower_or_arm_args = NIL,
*upper_or_arm_args = NIL;
- /* Restart scan of columns from the i'th one */
+ /*
+ * Restart scan of columns from the i'th one. We cannot use forboth
+ * here because the upper and lower bounds may have different numbers
+ * of values.
+ */
j = i;
partexprs_item = partexprs_item_saved;
- for_both_cell(cell1, lower_or_start_datum, cell2, upper_or_start_datum)
+ for (cell1 = lower_or_start_datum, cell2 = upper_or_start_datum;
+ cell1 != NULL || cell2 != NULL;
+ cell1 = cell1 ? lnext(cell1) : NULL,
+ cell2 = cell2 ? lnext(cell2) : NULL)
{
- PartitionRangeDatum *ldatum_next = NULL,
- *udatum_next = NULL;
+ lower_val = cell1 ? castNode(Const, lfirst(cell1)) : NULL;
+ upper_val = cell2 ? castNode(Const, lfirst(cell2)) : NULL;
- ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
- if (lnext(cell1))
- ldatum_next = castNode(PartitionRangeDatum,
- lfirst(lnext(cell1)));
- udatum = castNode(PartitionRangeDatum, lfirst(cell2));
- if (lnext(cell2))
- udatum_next = castNode(PartitionRangeDatum,
- lfirst(lnext(cell2)));
- get_range_key_properties(key, j, ldatum, udatum,
- &partexprs_item,
- &keyCol,
- &lower_val, &upper_val);
+ get_range_key_properties(key, j, &partexprs_item, &keyCol);
if (need_next_lower_arm && lower_val)
{
@@ -1681,12 +1629,11 @@ get_qual_for_range(PartitionKey key, Par
/*
* 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 the bound, use GE. Otherwise use GT.
*/
if (j - i < current_or_arm)
strategy = BTEqualStrategyNumber;
- else if ((ldatum_next && ldatum_next->infinite) ||
- j == key->partnatts - 1)
+ else if (lnext(cell1) == NULL)
strategy = BTGreaterEqualStrategyNumber;
else
strategy = BTGreaterStrategyNumber;
@@ -1704,12 +1651,10 @@ get_qual_for_range(PartitionKey key, Par
/*
* For the non-last columns of this arm, use the EQ operator.
- * For the last finite-valued column, use LE.
+ * Otherwise use LT.
*/
if (j - i < current_or_arm)
strategy = BTEqualStrategyNumber;
- else if (udatum_next && udatum_next->infinite)
- strategy = BTLessEqualStrategyNumber;
else
strategy = BTLessStrategyNumber;
@@ -1729,11 +1674,11 @@ get_qual_for_range(PartitionKey key, Par
if (j - i > current_or_arm)
{
/*
- * We need not emit the next arm if the new column that will
- * be considered is unbounded.
+ * We need not emit the next arm if there are no bounds on any
+ * of the remaining columns.
*/
- need_next_lower_arm = ldatum_next && !ldatum_next->infinite;
- need_next_upper_arm = udatum_next && !udatum_next->infinite;
+ need_next_lower_arm = (cell1 && lnext(cell1));
+ need_next_upper_arm = (cell2 && lnext(cell2));
break;
}
}
@@ -2091,9 +2036,8 @@ qsort_partition_list_value_cmp(const voi
/*
* make_one_range_bound
*
- * Return a PartitionRangeBound given a list of PartitionRangeDatum elements
- * and a flag telling whether the bound is lower or not. Made into a function
- * because there are multiple sites that want to use this facility.
+ * Return a PartitionRangeBound given a list of Const bound values and a flag
+ * indicating whether the bound is a lower bound or not.
*/
static PartitionRangeBound *
make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
@@ -2104,31 +2048,30 @@ make_one_range_bound(PartitionKey key, i
bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
bound->index = index;
- bound->datums = (Datum *) palloc0(key->partnatts * sizeof(Datum));
- bound->content = (RangeDatumContent *) palloc0(key->partnatts *
- sizeof(RangeDatumContent));
bound->lower = lower;
- i = 0;
- foreach(lc, datums)
+ /* Handle unbounded ranges, which have an empty list of bound values */
+ if (!datums)
{
- PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc));
-
- /* What's contained in this range datum? */
- bound->content[i] = !datum->infinite
- ? RANGE_DATUM_FINITE
- : (lower ? RANGE_DATUM_NEG_INF
- : RANGE_DATUM_POS_INF);
+ bound->kind = lower ? RANGE_BOUND_NEG_INF : RANGE_BOUND_POS_INF;
+ bound->ndatums = 0;
+ bound->datums = NULL;
+ return bound;
+ }
- if (bound->content[i] == RANGE_DATUM_FINITE)
- {
- Const *val = castNode(Const, datum->value);
+ /* Otherwise it's a finite bound; store the datum values in an array */
+ bound->kind = RANGE_BOUND_FINITE;
+ bound->ndatums = list_length(datums);
+ bound->datums = (Datum *) palloc0(bound->ndatums * sizeof(Datum));
- if (val->constisnull)
- elog(ERROR, "invalid range bound datum");
- bound->datums[i] = val->constvalue;
- }
+ i = 0;
+ foreach(lc, datums)
+ {
+ Const *val = castNode(Const, lfirst(lc));
+ if (val->constisnull)
+ elog(ERROR, "invalid range bound datum");
+ bound->datums[i] = val->constvalue;
i++;
}
@@ -2143,100 +2086,113 @@ qsort_partition_rbound_cmp(const void *a
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
PartitionKey key = (PartitionKey) arg;
- return partition_rbound_cmp(key, b1->datums, b1->content, b1->lower, b2);
+ return partition_rbound_cmp(key, b1->datums, b1->ndatums, b1->kind,
+ b1->lower, b2);
}
/*
* partition_rbound_cmp
*
* Return for two range bounds whether the 1st one (specified in datum1,
- * content1, and lower1) is <=, =, >= the bound specified in *b2
+ * ndatums1, kind1, and lower1) is <, =, > the bound specified in *b2
*/
static int32
partition_rbound_cmp(PartitionKey key,
- Datum *datums1, RangeDatumContent *content1, bool lower1,
- PartitionRangeBound *b2)
+ Datum *datums1, int ndatums1, RangeBoundKind kind1,
+ bool lower1, PartitionRangeBound *b2)
{
- int32 cmpval = 0; /* placate compiler */
int i;
Datum *datums2 = b2->datums;
- RangeDatumContent *content2 = b2->content;
bool lower2 = b2->lower;
- for (i = 0; i < key->partnatts; i++)
- {
- /*
- * First, handle cases involving infinity, which don't require
- * invoking the comparison proc.
- */
- if (content1[i] != RANGE_DATUM_FINITE &&
- content2[i] != RANGE_DATUM_FINITE)
+ /*
+ * Handle cases involving infinity, which don't require any values to be
+ * compared.
+ */
+ if (kind1 == RANGE_BOUND_NEG_INF)
+ return b2->kind == RANGE_BOUND_NEG_INF ? 0 : -1;
+ else if (kind1 == RANGE_BOUND_POS_INF)
+ return b2->kind == RANGE_BOUND_POS_INF ? 0 : 1;
+ else if (b2->kind != RANGE_BOUND_FINITE)
+ return b2->kind == RANGE_BOUND_NEG_INF ? 1 : -1;
- /*
- * Both are infinity, so they are equal unless one is negative
- * infinity and other positive (or vice versa)
- */
- return content1[i] == content2[i] ? 0
- : (content1[i] < content2[i] ? -1 : 1);
- else if (content1[i] != RANGE_DATUM_FINITE)
- return content1[i] == RANGE_DATUM_NEG_INF ? -1 : 1;
- else if (content2[i] != RANGE_DATUM_FINITE)
- return content2[i] == RANGE_DATUM_NEG_INF ? 1 : -1;
+ /* Compare values from the two range bounds */
+ for (i = 0; i < Min(ndatums1, b2->ndatums); i++)
+ {
+ int32 cmpval;
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
key->partcollation[i],
datums1[i],
datums2[i]));
if (cmpval != 0)
- break;
+ return cmpval;
}
/*
- * If the comparison is anything other than equal, we're done. If they
- * compare equal though, we still have to consider whether the boundaries
- * are inclusive or exclusive. Exclusive one is considered smaller of the
- * two.
+ * If one range has more values than the other, treat that range as
+ * larger, since all the shared values are now known to be equal.
*/
- if (cmpval == 0 && lower1 != lower2)
- cmpval = lower1 ? 1 : -1;
+ if (ndatums1 > b2->ndatums)
+ return 1;
+ else if (ndatums1 < b2->ndatums)
+ return -1;
- return cmpval;
+ /*
+ * The two bounds are equal as far as their values are concerned. If one
+ * is a lower (inclusive) bound and the other is an upper (exclusive)
+ * bound, treat the upper bound as smaller since it belongs to a partition
+ * that holds smaller values.
+ */
+ if (lower1)
+ return lower2 ? 0 : 1;
+ else
+ return lower2 ? -1 : 0;
}
/*
* partition_rbound_datum_cmp
*
- * Return whether range bound (specified in rb_datums, rb_content, and
- * rb_lower) <=, =, >= partition key of tuple (tuple_datums)
+ * Return whether range bound (specified in rb_datums, rb_ndatums, rb_kind,
+ * and rb_lower) <, =, > partition key of tuple (tuple_datums)
*/
static int32
partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, RangeDatumContent *rb_content,
- Datum *tuple_datums)
+ Datum *rb_datums, int rb_ndatums,
+ RangeBoundKind rb_kind, Datum *tuple_datums)
{
int i;
- int32 cmpval = -1;
- for (i = 0; i < key->partnatts; i++)
+ /* Handle UNBOUNDED ranges */
+ if (rb_kind == RANGE_BOUND_NEG_INF)
+ return -1;
+ if (rb_kind == RANGE_BOUND_POS_INF)
+ return 1;
+
+ /* Compare the range bound values with the tuple values */
+ for (i = 0; i < rb_ndatums; i++)
{
- if (rb_content[i] != RANGE_DATUM_FINITE)
- return rb_content[i] == RANGE_DATUM_NEG_INF ? -1 : 1;
+ int32 cmpval;
cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
key->partcollation[i],
rb_datums[i],
tuple_datums[i]));
if (cmpval != 0)
- break;
+ return cmpval;
}
- return cmpval;
+ /*
+ * All the tuple values are the same as the range bound values, but there
+ * may be more values in the tuple, in which case it is larger.
+ */
+ return key->partnatts > rb_ndatums ? -1 : 0;
}
/*
* partition_bound_cmp
*
- * Return whether the bound at offset in boundinfo is <=, =, >= the argument
+ * Return whether the bound at offset in boundinfo is <, =, > the argument
* specified in *probe.
*/
static int32
@@ -2257,7 +2213,8 @@ partition_bound_cmp(PartitionKey key, Pa
case PARTITION_STRATEGY_RANGE:
{
- RangeDatumContent *content = boundinfo->content[offset];
+ RangeBoundKind kind = boundinfo->rbound_kind[offset];
+ int ndatums = boundinfo->rbound_ndatums[offset];
if (probe_is_bound)
{
@@ -2268,13 +2225,13 @@ partition_bound_cmp(PartitionKey key, Pa
*/
bool lower = boundinfo->indexes[offset] < 0;
- cmpval = partition_rbound_cmp(key,
- bound_datums, content, lower,
+ cmpval = partition_rbound_cmp(key, bound_datums, ndatums,
+ kind, lower,
(PartitionRangeBound *) probe);
}
else
- cmpval = partition_rbound_datum_cmp(key,
- bound_datums, content,
+ cmpval = partition_rbound_datum_cmp(key, bound_datums,
+ ndatums, kind,
(Datum *) probe);
break;
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index 67ac814..c4036a9
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4453,18 +4453,6 @@ _copyPartitionBoundSpec(const PartitionB
return newnode;
}
-static PartitionRangeDatum *
-_copyPartitionRangeDatum(const PartitionRangeDatum *from)
-{
- PartitionRangeDatum *newnode = makeNode(PartitionRangeDatum);
-
- COPY_SCALAR_FIELD(infinite);
- COPY_NODE_FIELD(value);
- COPY_LOCATION_FIELD(location);
-
- return newnode;
-}
-
static PartitionCmd *
_copyPartitionCmd(const PartitionCmd *from)
{
@@ -5519,9 +5507,6 @@ copyObjectImpl(const void *from)
case T_PartitionBoundSpec:
retval = _copyPartitionBoundSpec(from);
break;
- case T_PartitionRangeDatum:
- retval = _copyPartitionRangeDatum(from);
- break;
case T_PartitionCmd:
retval = _copyPartitionCmd(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index 91d64b7..5d0ccc1
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2847,16 +2847,6 @@ _equalPartitionBoundSpec(const Partition
}
static bool
-_equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b)
-{
- COMPARE_SCALAR_FIELD(infinite);
- COMPARE_NODE_FIELD(value);
- COMPARE_LOCATION_FIELD(location);
-
- return true;
-}
-
-static bool
_equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
{
COMPARE_NODE_FIELD(name);
@@ -3669,9 +3659,6 @@ equal(const void *a, const void *b)
case T_PartitionBoundSpec:
retval = _equalPartitionBoundSpec(a, b);
break;
- case T_PartitionRangeDatum:
- retval = _equalPartitionRangeDatum(a, b);
- break;
case T_PartitionCmd:
retval = _equalPartitionCmd(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644
index 97ba25f..12e9d07
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1541,9 +1541,6 @@ exprLocation(const Node *expr)
case T_PartitionBoundSpec:
loc = ((const PartitionBoundSpec *) expr)->location;
break;
- case T_PartitionRangeDatum:
- loc = ((const PartitionRangeDatum *) expr)->location;
- break;
default:
/* for any other node type it's just unknown... */
loc = -1;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
new file mode 100644
index 3a23f0b..52332a7
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3550,16 +3550,6 @@ _outPartitionBoundSpec(StringInfo str, c
WRITE_LOCATION_FIELD(location);
}
-static void
-_outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
-{
- WRITE_NODE_TYPE("PARTITIONRANGEDATUM");
-
- WRITE_BOOL_FIELD(infinite);
- WRITE_NODE_FIELD(value);
- WRITE_LOCATION_FIELD(location);
-}
-
/*
* outNode -
* converts a Node into ascii string and append it to 'str'
@@ -4196,9 +4186,6 @@ outNode(StringInfo str, const void *obj)
case T_PartitionBoundSpec:
_outPartitionBoundSpec(str, obj);
break;
- case T_PartitionRangeDatum:
- _outPartitionRangeDatum(str, obj);
- break;
default:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
new file mode 100644
index 2988e8b..005fc6d
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2382,21 +2382,6 @@ _readPartitionBoundSpec(void)
}
/*
- * _readPartitionRangeDatum
- */
-static PartitionRangeDatum *
-_readPartitionRangeDatum(void)
-{
- READ_LOCALS(PartitionRangeDatum);
-
- READ_BOOL_FIELD(infinite);
- READ_NODE_FIELD(value);
- READ_LOCATION_FIELD(location);
-
- READ_DONE();
-}
-
-/*
* parseNodeString
*
* Given a character string representing a node tree, parseNodeString creates
@@ -2638,8 +2623,6 @@ parseNodeString(void)
return_value = _readExtensibleNode();
else if (MATCH("PARTITIONBOUNDSPEC", 18))
return_value = _readPartitionBoundSpec();
- else if (MATCH("PARTITIONRANGEDATUM", 19))
- return_value = _readPartitionRangeDatum();
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index 0f3998f..0e2e651
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -576,8 +576,8 @@ static Node *makeRecursiveViewSelect(cha
%type <partelem> part_elem
%type <list> part_params
%type <partboundspec> ForValues
-%type <node> partbound_datum PartitionRangeDatum
-%type <list> partbound_datum_list range_datum_list
+%type <node> partbound_datum
+%type <list> partbound_datum_list range_bound
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -2664,13 +2664,13 @@ ForValues:
}
/* a RANGE partition */
- | FOR VALUES FROM '(' range_datum_list ')' TO '(' range_datum_list ')'
+ | FOR VALUES FROM range_bound TO range_bound
{
PartitionBoundSpec *n = makeNode(PartitionBoundSpec);
n->strategy = PARTITION_STRATEGY_RANGE;
- n->lowerdatums = $5;
- n->upperdatums = $9;
+ n->lowerdatums = $4;
+ n->upperdatums = $6;
n->location = @3;
$$ = n;
@@ -2689,33 +2689,9 @@ partbound_datum_list:
{ $$ = lappend($1, $3); }
;
-range_datum_list:
- PartitionRangeDatum { $$ = list_make1($1); }
- | range_datum_list ',' PartitionRangeDatum
- { $$ = lappend($1, $3); }
- ;
-
-PartitionRangeDatum:
- UNBOUNDED
- {
- PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
-
- n->infinite = true;
- n->value = NULL;
- n->location = @1;
-
- $$ = (Node *) n;
- }
- | partbound_datum
- {
- PartitionRangeDatum *n = makeNode(PartitionRangeDatum);
-
- n->infinite = false;
- n->value = $1;
- n->location = @1;
-
- $$ = (Node *) n;
- }
+range_bound:
+ UNBOUNDED { $$ = NIL; }
+ | '(' partbound_datum_list ')' { $$ = $2; }
;
/*****************************************************************************
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index ee5f3a3..252baa6
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3361,11 +3361,9 @@ transformPartitionBound(ParseState *psta
}
else if (strategy == PARTITION_STRATEGY_RANGE)
{
- ListCell *cell1,
- *cell2;
+ ListCell *cell;
int i,
j;
- bool seen_unbounded;
if (spec->strategy != PARTITION_STRATEGY_RANGE)
ereport(ERROR,
@@ -3373,59 +3371,65 @@ transformPartitionBound(ParseState *psta
errmsg("invalid bound specification for a range partition"),
parser_errposition(pstate, exprLocation((Node *) spec))));
- if (list_length(spec->lowerdatums) != partnatts)
+ if (list_length(spec->lowerdatums) > partnatts)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("FROM must specify exactly one value per partitioning column")));
- if (list_length(spec->upperdatums) != partnatts)
+ errmsg("too many values in FROM list")));
+
+ if (list_length(spec->upperdatums) > partnatts)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("TO must specify exactly one value per partitioning column")));
+ errmsg("too many values in TO list")));
- /*
- * Check that no finite value follows an UNBOUNDED item in either of
- * lower and upper bound lists.
- */
- seen_unbounded = false;
- foreach(cell1, spec->lowerdatums)
+ /* Transform the lower bound values */
+ i = j = 0;
+ result_spec->lowerdatums = NIL;
+ foreach(cell, spec->lowerdatums)
{
- PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum,
- lfirst(cell1));
+ A_Const *con = castNode(A_Const, lfirst(cell));
+ char *colname;
+ Oid coltype;
+ int32 coltypmod;
+ Const *value;
- 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));
+ /* Get the column's name in case we need to output an error */
+ if (key->partattrs[i] != 0)
+ colname = get_relid_attribute_name(RelationGetRelid(parent),
+ key->partattrs[i]);
+ else
+ {
+ colname = deparse_expression((Node *) list_nth(partexprs, j),
+ deparse_context_for(RelationGetRelationName(parent),
+ RelationGetRelid(parent)),
+ false, false);
+ ++j;
+ }
+ /* Need its type data too */
+ coltype = get_partition_col_typid(key, i);
+ coltypmod = get_partition_col_typmod(key, i);
- if (rdatum->infinite)
- seen_unbounded = true;
- else if (seen_unbounded)
+ value = transformPartitionBoundValue(pstate, con,
+ colname,
+ coltype, coltypmod);
+ if (value->constisnull)
ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot specify finite value after UNBOUNDED"),
- parser_errposition(pstate, exprLocation((Node *) rdatum))));
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot specify NULL in range bound")));
+
+ result_spec->lowerdatums = lappend(result_spec->lowerdatums,
+ value);
+ ++i;
}
- /* Transform all the constants */
+ /* Transform the upper bound values */
i = j = 0;
- result_spec->lowerdatums = result_spec->upperdatums = NIL;
- forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
+ result_spec->upperdatums = NIL;
+ foreach(cell, spec->upperdatums)
{
- PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
- PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
+ A_Const *con = castNode(A_Const, lfirst(cell));
char *colname;
Oid coltype;
int32 coltypmod;
- A_Const *con;
Const *value;
/* Get the column's name in case we need to output an error */
@@ -3444,39 +3448,16 @@ transformPartitionBound(ParseState *psta
coltype = get_partition_col_typid(key, i);
coltypmod = get_partition_col_typmod(key, i);
- if (ldatum->value)
- {
- con = castNode(A_Const, ldatum->value);
- value = transformPartitionBoundValue(pstate, con,
- colname,
- coltype, coltypmod);
- if (value->constisnull)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot specify NULL in range bound")));
- ldatum = copyObject(ldatum); /* don't scribble on input */
- ldatum->value = (Node *) value;
- }
-
- if (rdatum->value)
- {
- con = castNode(A_Const, rdatum->value);
- value = transformPartitionBoundValue(pstate, con,
- colname,
- coltype, coltypmod);
- if (value->constisnull)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot specify NULL in range bound")));
- rdatum = copyObject(rdatum); /* don't scribble on input */
- rdatum->value = (Node *) value;
- }
+ value = transformPartitionBoundValue(pstate, con,
+ colname,
+ coltype, coltypmod);
+ if (value->constisnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot specify NULL in range bound")));
- result_spec->lowerdatums = lappend(result_spec->lowerdatums,
- ldatum);
result_spec->upperdatums = lappend(result_spec->upperdatums,
- rdatum);
-
+ value);
++i;
}
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index 18d9e27..4df1230
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8667,48 +8667,43 @@ get_rule_expr(Node *node, deparse_contex
break;
case PARTITION_STRATEGY_RANGE:
- Assert(spec->lowerdatums != NIL &&
- spec->upperdatums != NIL &&
- list_length(spec->lowerdatums) ==
- list_length(spec->upperdatums));
-
- appendStringInfoString(buf, "FOR VALUES FROM (");
- sep = "";
- foreach(cell, spec->lowerdatums)
+ /* Bounds may be empty, which means UNBOUNDED */
+ appendStringInfoString(buf, "FOR VALUES FROM ");
+ if (spec->lowerdatums)
{
- PartitionRangeDatum *datum =
- castNode(PartitionRangeDatum, lfirst(cell));
-
- appendStringInfoString(buf, sep);
- if (datum->infinite)
- appendStringInfoString(buf, "UNBOUNDED");
- else
+ appendStringInfoString(buf, "(");
+ sep = "";
+ foreach(cell, spec->lowerdatums)
{
- Const *val = castNode(Const, datum->value);
+ Const *val = castNode(Const, lfirst(cell));
+ appendStringInfoString(buf, sep);
get_const_expr(val, context, -1);
+ sep = ", ";
}
- sep = ", ";
+ appendStringInfoString(buf, ")");
}
- appendStringInfoString(buf, ") TO (");
- sep = "";
- foreach(cell, spec->upperdatums)
- {
- PartitionRangeDatum *datum =
- castNode(PartitionRangeDatum, lfirst(cell));
+ else
+ appendStringInfoString(buf, "UNBOUNDED");
- appendStringInfoString(buf, sep);
- if (datum->infinite)
- appendStringInfoString(buf, "UNBOUNDED");
- else
+ appendStringInfoString(buf, " TO ");
+ if (spec->upperdatums)
+ {
+ appendStringInfoString(buf, "(");
+ sep = "";
+ foreach(cell, spec->upperdatums)
{
- Const *val = castNode(Const, datum->value);
+ Const *val = castNode(Const, lfirst(cell));
+ appendStringInfoString(buf, sep);
get_const_expr(val, context, -1);
+ sep = ", ";
}
- sep = ", ";
+ appendStringInfoString(buf, ")");
}
- appendStringInfoString(buf, ")");
+ else
+ appendStringInfoString(buf, "UNBOUNDED");
+
break;
default:
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index 0152739..b800aab
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -466,7 +466,6 @@ typedef enum NodeTag
T_PartitionElem,
T_PartitionSpec,
T_PartitionBoundSpec,
- T_PartitionRangeDatum,
T_PartitionCmd,
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 1d96169..30a8ef8
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -802,28 +802,13 @@ typedef struct PartitionBoundSpec
List *listdatums; /* List of Consts (or A_Consts in raw tree) */
/* Partitioning info for RANGE strategy: */
- List *lowerdatums; /* List of PartitionRangeDatums */
- List *upperdatums; /* List of PartitionRangeDatums */
+ List *lowerdatums; /* List of Consts (or A_Consts in raw tree) */
+ List *upperdatums; /* List of Consts (or A_Consts in raw tree) */
int location; /* token location, or -1 if unknown */
} PartitionBoundSpec;
/*
- * PartitionRangeDatum - can be either a value or UNBOUNDED
- *
- * "value" is an A_Const in raw grammar output, a Const after analysis
- */
-typedef struct PartitionRangeDatum
-{
- NodeTag type;
-
- bool infinite; /* true if UNBOUNDED */
- Node *value; /* null if UNBOUNDED */
-
- int location; /* token location, or -1 if unknown */
-} PartitionRangeDatum;
-
-/*
* PartitionCmd - info for ALTER TABLE ATTACH/DETACH PARTITION commands
*/
typedef struct PartitionCmd
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
new file mode 100644
index fb8745b..f24fa58
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -505,22 +505,19 @@ CREATE TABLE fail_part PARTITION OF rang
ERROR: invalid bound specification for a range partition
LINE 1: ...BLE fail_part PARTITION OF range_parted FOR VALUES IN ('a');
^
--- each of start and end bounds must have same number of values as the
--- length of the partition key
+-- start and end bounds cannot have more values than the length of the partition key
CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM ('a', 1) TO ('z');
-ERROR: FROM must specify exactly one value per partitioning column
+ERROR: too many values in FROM list
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: too many values in TO list
-- 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 ('z');
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...
+-- cannot specify UNBOUNDED values in range bounds
+CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (UNBOUNDED) TO ('z');
+ERROR: syntax error at or near "UNBOUNDED"
+LINE 1: ...l_part PARTITION OF range_parted FOR VALUES FROM (UNBOUNDED)...
^
-DROP TABLE range_parted_multicol;
-- check if compatible with the specified parent
-- cannot create as partition of a non-partitioned table
CREATE TABLE unparted (
@@ -578,35 +575,35 @@ ERROR: cannot create range partition wi
-- 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 unbounded TO (1);
+CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM unbounded 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 unbounded;
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);
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (30);
ERROR: partition "fail_part" would overlap partition "part2"
CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM (10) TO (50);
-ERROR: partition "fail_part" would overlap partition "part3"
+ERROR: partition "fail_part" would overlap partition "part2"
-- now check for multi-column range partition key
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) TO (1);
+CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0) 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) 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 (2);
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) TO (2);
ERROR: partition "fail_part" would overlap partition "part10"
-- check schema propagation from parent
CREATE TABLE parted (
@@ -708,7 +705,7 @@ Number of partitions: 3 (Use \d+ to list
-- 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 UNBOUNDED TO UNBOUNDED;
\d+ unbounded_range_part
Table "public.unbounded_range_part"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -716,11 +713,11 @@ CREATE TABLE unbounded_range_part PARTIT
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 UNBOUNDED TO UNBOUNDED
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 UNBOUNDED TO (2);
\d+ range_parted4_1
Table "public.range_parted4_1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -728,10 +725,10 @@ CREATE TABLE range_parted4_1 PARTITION O
a | integer | | | | plain | |
b | integer | | | | plain | |
c | integer | | | | plain | |
-Partition of: range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED)
-Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) <= 1))
+Partition of: range_parted4 FOR VALUES FROM UNBOUNDED TO (2)
+Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (c IS NOT NULL) AND (abs(a) < 2))
-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, 8);
\d+ range_parted4_2
Table "public.range_parted4_2"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -739,10 +736,10 @@ CREATE TABLE range_parted4_2 PARTITION O
a | integer | | | | plain | |
b | integer | | | | plain | |
c | integer | | | | plain | |
-Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED)
-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 of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 8)
+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) < 8))))
-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) TO (10);
\d+ range_parted4_3
Table "public.range_parted4_3"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -750,8 +747,8 @@ CREATE TABLE range_parted4_3 PARTITION O
a | integer | | | | plain | |
b | integer | | | | plain | |
c | integer | | | | plain | |
-Partition of: range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED)
-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 of: range_parted4 FOR VALUES FROM (6, 8) TO (10)
+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) < 10))
DROP TABLE range_parted4;
-- cleanup
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
new file mode 100644
index 35d182d..b563ab0
--- 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
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 unbounded 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 unbounded to (1, 1, 1);
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10);
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10);
create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20);
-create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (unbounded, unbounded, unbounded);
+create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to unbounded;
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
new file mode 100644
index d1153f4..2718213
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -288,7 +288,7 @@ select tableoid::regclass, * from list_p
-- 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 unbounded 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 unbounded to (2);
+create table mcrparted1 partition of mcrparted for values from (2, 1) to (10, 5, 10);
+create table mcrparted2 partition of mcrparted for values from (10, 6) to (11);
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) to (30, 21);
+create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to unbounded;
-- routed to mcrparted0
insert into mcrparted values (0, 1, 1);
insert into mcrparted0 values (0, 1, 1);
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
new file mode 100644
index cb7aa5b..89ce126
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -477,18 +477,15 @@ CREATE TABLE range_parted (
-- trying to specify list for range partitioned table
CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES IN ('a');
--- each of start and end bounds must have same number of values as the
--- length of the partition key
+-- start and end bounds cannot have more values than the length of the partition key
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 ('z');
--- 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;
+-- cannot specify UNBOUNDED values in range bounds
+CREATE TABLE fail_part PARTITION OF range_parted FOR VALUES FROM (UNBOUNDED) TO ('z');
-- check if compatible with the specified parent
@@ -542,10 +539,10 @@ CREATE TABLE fail_part PARTITION OF rang
-- 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 unbounded TO (1);
+CREATE TABLE fail_part PARTITION OF range_parted2 FOR VALUES FROM unbounded 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 unbounded;
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 +554,18 @@ 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) TO (1);
+CREATE TABLE fail_part PARTITION OF range_parted3 FOR VALUES FROM (0) 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) 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 (2);
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) TO (2);
-- check schema propagation from parent
@@ -626,14 +623,14 @@ CREATE TABLE part_c_1_10 PARTITION OF pa
-- 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 UNBOUNDED TO UNBOUNDED;
\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 UNBOUNDED TO (2);
\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, 8);
\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) TO (10);
\d+ range_parted4_3
DROP TABLE range_parted4;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
new file mode 100644
index 70fe971..9149bda
--- 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
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 unbounded 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 unbounded to (1, 1, 1);
create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10);
create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10);
create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10);
create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20);
-create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (unbounded, unbounded, unbounded);
+create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to unbounded;
explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0
explain (costs off) select * from mcrparted where a = 10 and abs(b) < 5; -- scans mcrparted1
explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scans mcrparted1, mcrparted2
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
new file mode 100644
index 83c3ad8..2b8637d
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -169,7 +169,7 @@ select tableoid::regclass, * from list_p
-- 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 unbounded 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 unbounded to (2);
+create table mcrparted1 partition of mcrparted for values from (2, 1) to (10, 5, 10);
+create table mcrparted2 partition of mcrparted for values from (10, 6) to (11);
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) to (30, 21);
+create table mcrparted5 partition of mcrparted for values from (30, 21, 20) to unbounded;
-- routed to mcrparted0
insert into mcrparted values (0, 1, 1);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
new file mode 100644
index 23a4bbd..1bb6a8a
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1561,7 +1561,6 @@ PartitionElem
PartitionKey
PartitionListValue
PartitionRangeBound
-PartitionRangeDatum
PartitionSpec
PartitionedChildRelInfo
PasswordType
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers