On 2018-Jun-09, David Rowley wrote:

> Of course, that could be fixed by adding something like "bool
> isinsert" to ExecConstraints(), so that it does not do the needless
> check on UPDATE,

Yeah, that was my actual suggestion rather than using Amit's patch
verbatim.

> but I'm strongly against the reduction in modularity.
> I quite like ExecConstraints() the way it is.

I see.

Maybe this business of sticking the partition constraint check in
ExecConstraints was a bad idea all along.  How about we rip it out and
move the responsibility of doing ExecPartitionCheck to the caller
instead?  See attached.  This whole business looks much cleaner to me
this way.


BTW, while working on this, I was a bit disturbed by the
execReplication.c changes (namely: if the partitioning is not identical
on both sides, things are likely to blow up pretty badly), but that's a
separate topic.  I didn't see any tests of logical replication with
partitioned tables ... Is the current understanding that we don't
promise those things to work terribly well together?

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From 5d57489c6d8c5e1b10413362005ae333acd6c243 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 6 Jun 2018 15:08:23 -0400
Subject: [PATCH] Fix situation with partition constraints

blah blah ... commits 15ce775faa4 19c47e7c820 ... blah blah
---
 src/backend/commands/copy.c            | 30 +++++++++------------------
 src/backend/executor/execMain.c        | 32 ++++++++++++++++-------------
 src/backend/executor/execPartition.c   |  5 ++---
 src/backend/executor/execReplication.c |  8 ++++++--
 src/backend/executor/nodeModifyTable.c | 37 +++++++++++-----------------------
 src/include/executor/executor.h        |  5 ++---
 6 files changed, 49 insertions(+), 68 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0a1706c0d1..5f4ffd2975 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2726,29 +2726,17 @@ CopyFrom(CopyState cstate)
                        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
-                                * the tuple.
+                                * Check the tuple is valid against all 
constraints, and the
+                                * partition constraint, as required.
                                 */
                                if (resultRelInfo->ri_FdwRoutine == NULL &&
-                                       
(resultRelInfo->ri_RelationDesc->rd_att->constr ||
-                                        check_partition_constr))
-                                       ExecConstraints(resultRelInfo, slot, 
estate, true);
+                                       
resultRelInfo->ri_RelationDesc->rd_att->constr)
+                                       ExecConstraints(resultRelInfo, slot, 
estate);
+                               if (resultRelInfo->ri_PartitionCheck &&
+                                       (saved_resultRelInfo == NULL ||
+                                        (resultRelInfo->ri_TrigDesc &&
+                                         
resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
+                                       ExecPartitionCheck(resultRelInfo, slot, 
estate, true);
 
                                if (useHeapMultiInsert)
                                {
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 3d12f9c76f..c394b5dbed 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1856,14 +1856,17 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 /*
  * ExecPartitionCheck --- check that tuple meets the partition constraint.
  *
- * Exported in executor.h for outside use.
- * Returns true if it meets the partition constraint, else returns false.
+ * Returns true if it meets the partition constraint.  If the constraint
+ * fails and we're asked to emit to error, do so and don't return; otherwise
+ * return false.
+ *
  */
 bool
 ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
-                                  EState *estate)
+                                  EState *estate, bool emitError)
 {
        ExprContext *econtext;
+       bool    success;
 
        /*
         * If first time through, build expression state tree for the partition
@@ -1890,7 +1893,13 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, 
TupleTableSlot *slot,
         * As in case of the catalogued constraints, we treat a NULL result as
         * 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 */
+       if (!success && emitError)
+               ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
+
+       return success;
 }
 
 /*
@@ -1951,17 +1960,17 @@ ExecPartitionCheckEmitError(ResultRelInfo 
*resultRelInfo,
 /*
  * ExecConstraints - check constraints of the tuple in 'slot'
  *
- * This checks the traditional NOT NULL and check constraints, and if
- * requested, checks the partition constraint.
+ * This checks the traditional NOT NULL and check constraints.
+ *
+ * The partition constraint is *NOT* checked.
  *
  * Note: 'slot' contains the tuple to check the constraints of, which may
  * 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
 ExecConstraints(ResultRelInfo *resultRelInfo,
-                               TupleTableSlot *slot, EState *estate,
-                               bool check_partition_constraint)
+                               TupleTableSlot *slot, EState *estate)
 {
        Relation        rel = resultRelInfo->ri_RelationDesc;
        TupleDesc       tupdesc = RelationGetDescr(rel);
@@ -2076,13 +2085,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
                                         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
  * of the specified kind.
diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index c83991c93c..bb9bf5afa4 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -201,9 +201,8 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, 
PartitionDispatch *pd,
         * 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.
         */
-       if (resultRelInfo->ri_PartitionCheck &&
-               !ExecPartitionCheck(resultRelInfo, slot, estate))
-               ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
+       if (resultRelInfo->ri_PartitionCheck)
+               ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
        /* start with the root partitioned table */
        parent = pd[0];
diff --git a/src/backend/executor/execReplication.c 
b/src/backend/executor/execReplication.c
index 4fbdfc0a09..41e857e378 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -413,7 +413,9 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot 
*slot)
 
                /* Check the constraints of the tuple */
                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. */
                tuple = ExecMaterializeSlot(slot);
@@ -478,7 +480,9 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 
                /* Check the constraints of the tuple */
                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. */
                tuple = ExecMaterializeSlot(slot);
diff --git a/src/backend/executor/nodeModifyTable.c 
b/src/backend/executor/nodeModifyTable.c
index c4c841cdd7..c75d66ab83 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -365,16 +365,6 @@ ExecInsert(ModifyTableState *mtstate,
        else
        {
                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
@@ -402,17 +392,16 @@ ExecInsert(ModifyTableState *mtstate,
                        ExecWithCheckOptions(wco_kind, resultRelInfo, slot, 
estate);
 
                /*
-                * No need though if the tuple has been routed, and a BR trigger
-                * doesn't exist.
+                * Check the tuple is valid against all constraints, and the 
partition
+                * constraint, as required.
                 */
-               if (resultRelInfo->ri_PartitionRoot != NULL &&
-                       !(resultRelInfo->ri_TrigDesc &&
-                         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)
-                       ExecConstraints(resultRelInfo, slot, estate, true);
+               if (resultRelationDesc->rd_att->constr)
+                       ExecConstraints(resultRelInfo, slot, estate);
+               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)
                {
@@ -1037,7 +1026,7 @@ lreplace:;
                 */
                partition_constraint_failed =
                        resultRelInfo->ri_PartitionCheck &&
-                       !ExecPartitionCheck(resultRelInfo, slot, estate);
+                       !ExecPartitionCheck(resultRelInfo, slot, estate, false);
 
                if (!partition_constraint_failed &&
                        resultRelInfo->ri_WithCheckOptions != NIL)
@@ -1166,12 +1155,10 @@ lreplace:;
                 * slot for the orig_slot argument, because unlike 
ExecInsert(), no
                 * tuple-routing is performed here, hence the slot remains 
unchanged.
                 * We've already checked the partition constraint above; 
however, we
-                * must still ensure the tuple passes all other constraints, so 
we
-                * will call ExecConstraints() and have it validate all 
remaining
-                * checks.
+                * must still ensure the tuple passes all other constraints.
                 */
                if (resultRelationDesc->rd_att->constr)
-                       ExecConstraints(resultRelInfo, slot, estate, false);
+                       ExecConstraints(resultRelInfo, slot, estate);
 
                /*
                 * replace the heap tuple
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index a7ea3c7d10..f82b51667f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -180,10 +180,9 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState 
*estate, Oid relid);
 extern void ExecCleanUpTriggerState(EState *estate);
 extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
-                               TupleTableSlot *slot, EState *estate,
-                               bool check_partition_constraint);
+                               TupleTableSlot *slot, EState *estate);
 extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo,
-                                  TupleTableSlot *slot, EState *estate);
+                                  TupleTableSlot *slot, EState *estate, bool 
emitError);
 extern void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
                                                        TupleTableSlot *slot, 
EState *estate);
 extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
-- 
2.11.0

Reply via email to