summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/tablecmds.c16
-rw-r--r--src/backend/parser/parse_utilcmd.c53
-rw-r--r--src/backend/partitioning/partbounds.c101
3 files changed, 116 insertions, 54 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eab570a8675..16285ad09fa 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -543,7 +543,8 @@ static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partPa
static void CreateInheritance(Relation child_rel, Relation parent_rel);
static void RemoveInheritance(Relation child_rel, Relation parent_rel);
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
- PartitionCmd *cmd);
+ PartitionCmd *cmd,
+ AlterTableUtilityContext *context);
static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
List *partConstraint,
@@ -1007,7 +1008,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Check first that the new partition's bound is valid and does not
* overlap with any of existing partitions of the parent.
*/
- check_new_partition_bound(relname, parent, bound);
+ check_new_partition_bound(relname, parent, bound, pstate);
/*
* If the default partition exists, its partition constraints will
@@ -4718,7 +4719,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
cur_pass, context);
Assert(cmd != NULL);
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
- ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
+ ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
+ context);
else
ATExecAttachPartitionIdx(wqueue, rel,
((PartitionCmd *) cmd->def)->name);
@@ -16280,7 +16282,8 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
* Return the address of the newly attached partition.
*/
static ObjectAddress
-ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
+ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
+ AlterTableUtilityContext *context)
{
Relation attachrel,
catalog;
@@ -16295,6 +16298,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
const char *trigger_name;
Oid defaultPartOid;
List *partBoundConstraint;
+ ParseState *pstate = make_parsestate(NULL);
+
+ pstate->p_sourcetext = context->queryString;
/*
* We must lock the default partition if one exists, because attaching a
@@ -16460,7 +16466,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
* error.
*/
check_new_partition_bound(RelationGetRelationName(attachrel), rel,
- cmd->bound);
+ cmd->bound, pstate);
/* OK to create inheritance. Rest of the checks performed there */
CreateInheritance(attachrel, rel);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ec944371dd3..164312d60e2 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -4164,7 +4164,7 @@ validateInfiniteBounds(ParseState *pstate, List *blist)
}
/*
- * Transform one constant in a partition bound spec
+ * Transform one entry in a partition bound spec, producing a constant.
*/
static Const *
transformPartitionBoundValue(ParseState *pstate, Node *val,
@@ -4177,6 +4177,13 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
value = transformExpr(pstate, val, EXPR_KIND_PARTITION_BOUND);
/*
+ * transformExpr() should have already rejected column references,
+ * subqueries, aggregates, window functions, and SRFs, based on the
+ * EXPR_KIND_ of a partition bound expression.
+ */
+ Assert(!contain_var_clause(value));
+
+ /*
* Check that the input expression's collation is compatible with one
* specified for the parent's partition key (partcollation). Don't throw
* an error if it's the default collation which we'll replace with the
@@ -4220,7 +4227,11 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
parser_errposition(pstate, exprLocation(value))));
}
- /* Coerce to correct type */
+ /*
+ * Coerce to the correct type. This might cause an explicit coercion step
+ * to be added on top of the expression, which must be evaluated before
+ * returning the result to the caller.
+ */
value = coerce_to_target_type(pstate,
value, exprType(value),
colType,
@@ -4236,25 +4247,35 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
format_type_be(colType), colName),
parser_errposition(pstate, exprLocation(val))));
- /* Simplify the expression, in case we had a coercion */
- if (!IsA(value, Const))
- value = (Node *) expression_planner((Expr *) value);
-
/*
- * transformExpr() should have already rejected column references,
- * subqueries, aggregates, window functions, and SRFs, based on the
- * EXPR_KIND_ for a default expression.
+ * Evaluate the expression, if needed, assigning the partition key's data
+ * type and collation to the resulting Const node.
*/
- Assert(!contain_var_clause(value));
+ if (!IsA(value, Const))
+ {
+ value = (Node *) expression_planner((Expr *) value);
+ value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
+ partCollation);
+ if (!IsA(value, Const))
+ elog(ERROR, "could not evaluate partition bound expression");
+ }
+ else
+ {
+ /*
+ * If the expression is already a Const, as is often the case, we can
+ * skip the rather expensive steps above. But we still have to insert
+ * the right collation, since coerce_to_target_type doesn't handle
+ * that.
+ */
+ ((Const *) value)->constcollid = partCollation;
+ }
/*
- * Evaluate the expression, assigning the partition key's collation to the
- * resulting Const expression.
+ * Attach original expression's parse location to the Const, so that
+ * that's what will be reported for any later errors related to this
+ * partition bound.
*/
- value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
- partCollation);
- if (!IsA(value, Const))
- elog(ERROR, "could not evaluate partition bound expression");
+ ((Const *) value)->location = exprLocation(val);
return (Const *) value;
}
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 58f9b46289a..66c42b58986 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -223,7 +223,7 @@ static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal);
+ PartitionRangeBound *probe, int32 *cmpval);
static int get_partition_bound_num_indexes(PartitionBoundInfo b);
static Expr *make_partition_op_expr(PartitionKey key, int keynum,
uint16 strategy, Expr *arg1, Expr *arg2);
@@ -2805,14 +2805,14 @@ partitions_are_ordered(PartitionBoundInfo boundinfo, int nparts)
*/
void
check_new_partition_bound(char *relname, Relation parent,
- PartitionBoundSpec *spec)
+ PartitionBoundSpec *spec, ParseState *pstate)
{
PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- ParseState *pstate = make_parsestate(NULL);
int with = -1;
bool overlap = false;
+ int overlap_location = -1;
if (spec->is_default)
{
@@ -2907,6 +2907,7 @@ check_new_partition_bound(char *relname, Relation parent,
if (boundinfo->indexes[remainder] != -1)
{
overlap = true;
+ overlap_location = spec->location;
with = boundinfo->indexes[remainder];
break;
}
@@ -2935,6 +2936,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
Const *val = castNode(Const, lfirst(cell));
+ overlap_location = val->location;
if (!val->constisnull)
{
int offset;
@@ -2968,6 +2970,7 @@ check_new_partition_bound(char *relname, Relation parent,
{
PartitionRangeBound *lower,
*upper;
+ int cmpval;
Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
lower = make_one_partition_rbound(key, -1, spec->lowerdatums, true);
@@ -2977,10 +2980,17 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
- key->partcollation, lower->datums,
- lower->kind, true, upper) >= 0)
+ cmpval = partition_rbound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation,
+ lower->datums, lower->kind,
+ true, upper);
+ if (cmpval >= 0)
{
+ /* Fetch the problematic key from the lower datums list. */
+ PartitionRangeDatum *datum = list_nth(spec->lowerdatums,
+ cmpval - 1);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("empty range bound specified for partition \"%s\"",
@@ -2988,13 +2998,12 @@ check_new_partition_bound(char *relname, Relation parent,
errdetail("Specified lower bound %s is greater than or equal to upper bound %s.",
get_range_partbound_string(spec->lowerdatums),
get_range_partbound_string(spec->upperdatums)),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, datum->location)));
}
if (partdesc->nparts > 0)
{
int offset;
- bool equal;
Assert(boundinfo &&
boundinfo->strategy == PARTITION_STRATEGY_RANGE &&
@@ -3020,7 +3029,7 @@ check_new_partition_bound(char *relname, Relation parent,
key->partsupfunc,
key->partcollation,
boundinfo, lower,
- &equal);
+ &cmpval);
if (boundinfo->indexes[offset + 1] < 0)
{
@@ -3032,7 +3041,6 @@ check_new_partition_bound(char *relname, Relation parent,
*/
if (offset + 1 < boundinfo->ndatums)
{
- int32 cmpval;
Datum *datums;
PartitionRangeDatumKind *kind;
bool is_lower;
@@ -3049,11 +3057,19 @@ check_new_partition_bound(char *relname, Relation parent,
if (cmpval < 0)
{
/*
+ * Fetch the problematic key from the upper
+ * datums list.
+ */
+ PartitionRangeDatum *datum =
+ list_nth(spec->upperdatums, -cmpval - 1);
+
+ /*
* The new partition overlaps with the
* existing partition between offset + 1 and
* offset + 2.
*/
overlap = true;
+ overlap_location = datum->location;
with = boundinfo->indexes[offset + 2];
}
}
@@ -3064,7 +3080,20 @@ check_new_partition_bound(char *relname, Relation parent,
* The new partition overlaps with the existing
* partition between offset and offset + 1.
*/
+ PartitionRangeDatum *datum;
+
+ /*
+ * Fetch the problematic key from the lower datums
+ * list. Given the way partition_range_bsearch()
+ * works, the new lower bound is certainly >= the
+ * bound at offset. If the bound matches exactly, we
+ * flag the 1st key.
+ */
+ Assert(cmpval >= 0);
+ datum = cmpval == 0 ? linitial(spec->lowerdatums) :
+ list_nth(spec->lowerdatums, cmpval - 1);
overlap = true;
+ overlap_location = datum->location;
with = boundinfo->indexes[offset + 1];
}
}
@@ -3084,7 +3113,7 @@ check_new_partition_bound(char *relname, Relation parent,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition \"%s\" would overlap partition \"%s\"",
relname, get_rel_name(partdesc->oids[with])),
- parser_errposition(pstate, spec->location)));
+ parser_errposition(pstate, overlap_location)));
}
}
@@ -3317,8 +3346,12 @@ make_one_partition_rbound(PartitionKey key, int index, List *datums, bool lower)
/*
* partition_rbound_cmp
*
- * Return for two range bounds whether the 1st one (specified in datums1,
- * kind1, and lower1) is <, =, or > the bound specified in *b2.
+ * For two range bounds this decides whether the 1st one (specified by
+ * datums1, kind1, and lower1) is <, =, or > the bound specified in *b2.
+ *
+ * 0 is returned if they are equal, otherwise a non-zero integer whose sign
+ * indicates the ordering, and whose absolute value gives the 1-based
+ * partition key number of the first mismatching column.
*
* partnatts, partsupfunc and partcollation give the number of attributes in the
* bounds to be compared, comparison function to be used and the collations of
@@ -3337,6 +3370,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
+ int32 colnum = 0;
int32 cmpval = 0; /* placate compiler */
int i;
Datum *datums2 = b2->datums;
@@ -3345,6 +3379,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
for (i = 0; i < partnatts; i++)
{
+ /* Track column number in case we need it for result */
+ colnum++;
+
/*
* First, handle cases where the column is unbounded, which should not
* invoke the comparison procedure, and should not consider any later
@@ -3352,9 +3389,9 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* compare the same way as the values they represent.
*/
if (kind1[i] < kind2[i])
- return -1;
+ return -colnum;
else if (kind1[i] > kind2[i])
- return 1;
+ return colnum;
else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE)
/*
@@ -3381,7 +3418,7 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
if (cmpval == 0 && lower1 != lower2)
cmpval = lower1 ? 1 : -1;
- return cmpval;
+ return cmpval == 0 ? 0 : (cmpval < 0 ? -colnum : colnum);
}
/*
@@ -3393,7 +3430,6 @@ partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
* n_tuple_datums, partsupfunc and partcollation give number of attributes in
* the bounds to be compared, comparison function to be used and the collations
* of attributes resp.
- *
*/
int32
partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
@@ -3486,14 +3522,17 @@ partition_list_bsearch(FmgrInfo *partsupfunc, Oid *partcollation,
* equal to the given range bound or -1 if all of the range bounds are
* greater
*
- * *is_equal is set to true if the range bound at the returned index is equal
- * to the input range bound
+ * Upon return from this function, *cmpval is set to 0 if the bound at the
+ * returned index matches the input range bound exactly, otherwise a
+ * non-zero integer whose sign indicates the ordering, and whose absolute
+ * value gives the 1-based partition key number of the first mismatching
+ * column.
*/
static int
partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
Oid *partcollation,
PartitionBoundInfo boundinfo,
- PartitionRangeBound *probe, bool *is_equal)
+ PartitionRangeBound *probe, int32 *cmpval)
{
int lo,
hi,
@@ -3503,21 +3542,17 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
hi = boundinfo->ndatums - 1;
while (lo < hi)
{
- int32 cmpval;
-
mid = (lo + hi + 1) / 2;
- cmpval = partition_rbound_cmp(partnatts, partsupfunc,
- partcollation,
- boundinfo->datums[mid],
- boundinfo->kind[mid],
- (boundinfo->indexes[mid] == -1),
- probe);
- if (cmpval <= 0)
+ *cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ partcollation,
+ boundinfo->datums[mid],
+ boundinfo->kind[mid],
+ (boundinfo->indexes[mid] == -1),
+ probe);
+ if (*cmpval <= 0)
{
lo = mid;
- *is_equal = (cmpval == 0);
-
- if (*is_equal)
+ if (*cmpval == 0)
break;
}
else
@@ -3528,7 +3563,7 @@ partition_range_bsearch(int partnatts, FmgrInfo *partsupfunc,
}
/*
- * partition_range_bsearch
+ * partition_range_datum_bsearch
* Returns the index of the greatest range bound that is less than or
* equal to the given tuple or -1 if all of the range bounds are greater
*