Commit 5b0c7e2f authored by Alvaro Herrera's avatar Alvaro Herrera

Don't needlessly check the partition contraint twice

Starting with commit f0e44751, ExecConstraints was in charge of
running the partition constraint; commit 19c47e7c modified that so
that caller could request to skip that checking depending on some
conditions, but that commit and 15ce775f together introduced a small
bug there which caused ExecInsert to request skipping the constraint
check but have this not be honored -- in effect doing the check twice.
This could have been fixed in a very small patch, but on further
analysis of the involved function and its callsites, it turns out to be
simpler to give the responsibility of checking the partition constraint
fully to the caller, and return ExecConstraints to its original
(pre-partitioning) shape where it only checked tuple descriptor-related
constraints.  Each caller must do partition constraint checking on its
own schedule, which is more convenient after commit 2f178441 anyway.

Reported-by: David Rowley
Author: David Rowley, Álvaro Herrera
Reviewed-by: Amit Langote, Amit Khandekar, Simon Riggs
Discussion: https://postgr.es/m/CAKJS1f8w8+awsxgea8wt7_UX8qzOQ=Tm1LD+U1fHqBAkXxkW2w@mail.gmail.com
parent 85dd744a
...@@ -2725,30 +2725,25 @@ CopyFrom(CopyState cstate) ...@@ -2725,30 +2725,25 @@ CopyFrom(CopyState cstate)
} }
else else
{ {
/*
* We always check the partition constraint, including when
* the tuple got here via tuple-routing. However we don't
* need to in the latter case if no BR trigger is defined on
* the partition. Note that a BR trigger might modify the
* tuple such that the partition constraint is no longer
* satisfied, so we need to check in that case.
*/
bool check_partition_constr =
(resultRelInfo->ri_PartitionCheck != NIL);
if (saved_resultRelInfo != NULL &&
!(resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row))
check_partition_constr = false;
/* /*
* If the target is a plain table, check the constraints of * If the target is a plain table, check the constraints of
* the tuple. * the tuple.
*/ */
if (resultRelInfo->ri_FdwRoutine == NULL && if (resultRelInfo->ri_FdwRoutine == NULL &&
(resultRelInfo->ri_RelationDesc->rd_att->constr || resultRelInfo->ri_RelationDesc->rd_att->constr)
check_partition_constr)) ExecConstraints(resultRelInfo, slot, estate);
ExecConstraints(resultRelInfo, slot, estate, true);
/*
* Also check the tuple against the partition constraint, if
* there is one; except that if we got here via tuple-routing,
* we don't need to if there's no BR trigger defined on the
* partition.
*/
if (resultRelInfo->ri_PartitionCheck &&
(saved_resultRelInfo == NULL ||
(resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
ExecPartitionCheck(resultRelInfo, slot, estate, true);
if (useHeapMultiInsert) if (useHeapMultiInsert)
{ {
......
...@@ -1856,14 +1856,16 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ...@@ -1856,14 +1856,16 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
/* /*
* ExecPartitionCheck --- check that tuple meets the partition constraint. * ExecPartitionCheck --- check that tuple meets the partition constraint.
* *
* Exported in executor.h for outside use. * Returns true if it meets the partition constraint. If the constraint
* Returns true if it meets the partition constraint, else returns false. * fails and we're asked to emit to error, do so and don't return; otherwise
* return false.
*/ */
bool bool
ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
EState *estate) EState *estate, bool emitError)
{ {
ExprContext *econtext; ExprContext *econtext;
bool success;
/* /*
* If first time through, build expression state tree for the partition * If first time through, build expression state tree for the partition
...@@ -1890,7 +1892,13 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, ...@@ -1890,7 +1892,13 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
* As in case of the catalogued constraints, we treat a NULL result as * As in case of the catalogued constraints, we treat a NULL result as
* success here, not a failure. * success here, not a failure.
*/ */
return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext); success = ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
/* if asked to emit error, don't actually return on failure */
if (!success && emitError)
ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
return success;
} }
/* /*
...@@ -1951,17 +1959,17 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, ...@@ -1951,17 +1959,17 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
/* /*
* ExecConstraints - check constraints of the tuple in 'slot' * ExecConstraints - check constraints of the tuple in 'slot'
* *
* This checks the traditional NOT NULL and check constraints, and if * This checks the traditional NOT NULL and check constraints.
* requested, checks the partition constraint. *
* The partition constraint is *NOT* checked.
* *
* Note: 'slot' contains the tuple to check the constraints of, which may * Note: 'slot' contains the tuple to check the constraints of, which may
* have been converted from the original input tuple after tuple routing. * have been converted from the original input tuple after tuple routing.
* 'resultRelInfo' is the original result relation, before tuple routing. * 'resultRelInfo' is the final result relation, after tuple routing.
*/ */
void void
ExecConstraints(ResultRelInfo *resultRelInfo, ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate, TupleTableSlot *slot, EState *estate)
bool check_partition_constraint)
{ {
Relation rel = resultRelInfo->ri_RelationDesc; Relation rel = resultRelInfo->ri_RelationDesc;
TupleDesc tupdesc = RelationGetDescr(rel); TupleDesc tupdesc = RelationGetDescr(rel);
...@@ -2076,13 +2084,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -2076,13 +2084,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
errtableconstraint(orig_rel, failed))); errtableconstraint(orig_rel, failed)));
} }
} }
if (check_partition_constraint && resultRelInfo->ri_PartitionCheck &&
!ExecPartitionCheck(resultRelInfo, slot, estate))
ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
} }
/* /*
* ExecWithCheckOptions -- check that tuple satisfies any WITH CHECK OPTIONs * ExecWithCheckOptions -- check that tuple satisfies any WITH CHECK OPTIONs
* of the specified kind. * of the specified kind.
......
...@@ -201,9 +201,8 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, ...@@ -201,9 +201,8 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
* First check the root table's partition constraint, if any. No point in * First check the root table's partition constraint, if any. No point in
* routing the tuple if it doesn't belong in the root table itself. * routing the tuple if it doesn't belong in the root table itself.
*/ */
if (resultRelInfo->ri_PartitionCheck && if (resultRelInfo->ri_PartitionCheck)
!ExecPartitionCheck(resultRelInfo, slot, estate)) ExecPartitionCheck(resultRelInfo, slot, estate, true);
ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
/* start with the root partitioned table */ /* start with the root partitioned table */
parent = pd[0]; parent = pd[0];
......
...@@ -413,7 +413,9 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) ...@@ -413,7 +413,9 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
/* Check the constraints of the tuple */ /* Check the constraints of the tuple */
if (rel->rd_att->constr) if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate, true); ExecConstraints(resultRelInfo, slot, estate);
if (resultRelInfo->ri_PartitionCheck)
ExecPartitionCheck(resultRelInfo, slot, estate, true);
/* Store the slot into tuple that we can inspect. */ /* Store the slot into tuple that we can inspect. */
tuple = ExecMaterializeSlot(slot); tuple = ExecMaterializeSlot(slot);
...@@ -478,7 +480,9 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, ...@@ -478,7 +480,9 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* Check the constraints of the tuple */ /* Check the constraints of the tuple */
if (rel->rd_att->constr) if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate, true); ExecConstraints(resultRelInfo, slot, estate);
if (resultRelInfo->ri_PartitionCheck)
ExecPartitionCheck(resultRelInfo, slot, estate, true);
/* Store the slot into tuple that we can write. */ /* Store the slot into tuple that we can write. */
tuple = ExecMaterializeSlot(slot); tuple = ExecMaterializeSlot(slot);
......
...@@ -365,16 +365,6 @@ ExecInsert(ModifyTableState *mtstate, ...@@ -365,16 +365,6 @@ ExecInsert(ModifyTableState *mtstate,
else else
{ {
WCOKind wco_kind; WCOKind wco_kind;
bool check_partition_constr;
/*
* We always check the partition constraint, including when the tuple
* got here via tuple-routing. However we don't need to in the latter
* case if no BR trigger is defined on the partition. Note that a BR
* trigger might modify the tuple such that the partition constraint
* is no longer satisfied, so we need to check in that case.
*/
check_partition_constr = (resultRelInfo->ri_PartitionCheck != NIL);
/* /*
* Constraints might reference the tableoid column, so initialize * Constraints might reference the tableoid column, so initialize
...@@ -402,17 +392,21 @@ ExecInsert(ModifyTableState *mtstate, ...@@ -402,17 +392,21 @@ ExecInsert(ModifyTableState *mtstate,
ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate); ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
/* /*
* No need though if the tuple has been routed, and a BR trigger * Check the constraints of the tuple.
* doesn't exist.
*/ */
if (resultRelInfo->ri_PartitionRoot != NULL && if (resultRelationDesc->rd_att->constr)
!(resultRelInfo->ri_TrigDesc && ExecConstraints(resultRelInfo, slot, estate);
resultRelInfo->ri_TrigDesc->trig_insert_before_row))
check_partition_constr = false;
/* Check the constraints of the tuple */ /*
if (resultRelationDesc->rd_att->constr || check_partition_constr) * Also check the tuple against the partition constraint, if there is
ExecConstraints(resultRelInfo, slot, estate, true); * one; except that if we got here via tuple-routing, we don't need to
* if there's no BR trigger defined on the partition.
*/
if (resultRelInfo->ri_PartitionCheck &&
(resultRelInfo->ri_PartitionRoot == NULL ||
(resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
ExecPartitionCheck(resultRelInfo, slot, estate, true);
if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0) if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
{ {
...@@ -1037,7 +1031,7 @@ lreplace:; ...@@ -1037,7 +1031,7 @@ lreplace:;
*/ */
partition_constraint_failed = partition_constraint_failed =
resultRelInfo->ri_PartitionCheck && resultRelInfo->ri_PartitionCheck &&
!ExecPartitionCheck(resultRelInfo, slot, estate); !ExecPartitionCheck(resultRelInfo, slot, estate, false);
if (!partition_constraint_failed && if (!partition_constraint_failed &&
resultRelInfo->ri_WithCheckOptions != NIL) resultRelInfo->ri_WithCheckOptions != NIL)
...@@ -1168,7 +1162,7 @@ lreplace:; ...@@ -1168,7 +1162,7 @@ lreplace:;
* have it validate all remaining checks. * have it validate all remaining checks.
*/ */
if (resultRelationDesc->rd_att->constr) if (resultRelationDesc->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate, false); ExecConstraints(resultRelInfo, slot, estate);
/* /*
* replace the heap tuple * replace the heap tuple
......
...@@ -180,10 +180,9 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); ...@@ -180,10 +180,9 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern void ExecCleanUpTriggerState(EState *estate); extern void ExecCleanUpTriggerState(EState *estate);
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo, extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate, TupleTableSlot *slot, EState *estate);
bool check_partition_constraint);
extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate, bool emitError);
extern void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, extern void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment