diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 0a1706c0d1..107a456210 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2725,30 +2725,25 @@ CopyFrom(CopyState cstate)
 			}
 			else
 			{
+				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.
+				 * Determine if we need to check any partition constaint for
+				 * this rel.
 				 */
-				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;
+				check_partition_constr =
+						ExecPartitionConstraintNeedsRecheck(resultRelInfo,
+												saved_resultRelInfo != NULL);
 
 				/*
-				 * 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, if required.
 				 */
 				if (resultRelInfo->ri_FdwRoutine == NULL &&
 					(resultRelInfo->ri_RelationDesc->rd_att->constr ||
 					 check_partition_constr))
-					ExecConstraints(resultRelInfo, slot, estate, true);
+					ExecConstraints(resultRelInfo, slot, estate,
+									check_partition_constr);
 
 				if (useHeapMultiInsert)
 				{
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index c83991c93c..4cee0f851d 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -700,6 +700,33 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
 	partRelInfo->ri_PartitionReadyForRouting = true;
 }
 
+/*
+ * ExecPartitionConstraintNeedsRecheck
+ *		Returns true if 'resultRelInfo' requires a partition bound re-check
+ *		for an inserted tuple.
+ */
+bool
+ExecPartitionConstraintNeedsRecheck(ResultRelInfo *resultRelInfo,
+									bool tupleRouting)
+{
+	/*
+	 * We must validate the partition constraint when inserting directly into
+	 * a partition, or when the tuple has been routed here and a BR trigger
+	 * exists.  Such a trigger could have changed the tuple so that it
+	 * violates the partition constraint.  If no such trigger exists then the
+	 * correct partition has already been selected, so we can skip the check.
+	 */
+	if (resultRelInfo->ri_PartitionCheck != NIL && !tupleRouting)
+		return true;
+
+	if (tupleRouting &&
+		resultRelInfo->ri_TrigDesc &&
+		resultRelInfo->ri_TrigDesc->trig_insert_before_row)
+		return true;
+
+	return false;
+}
+
 /*
  * ExecSetupChildParentMapForLeaf -- Initialize the per-leaf-partition
  * child-to-root tuple conversion map array.
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c4c841cdd7..39b859b903 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -367,15 +367,6 @@ ExecInsert(ModifyTableState *mtstate,
 		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
 		 * t_tableOid before evaluating them.
@@ -402,17 +393,19 @@ 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.
+		 * Determine if we need to check any partition constaint for this rel.
 		 */
-		if (resultRelInfo->ri_PartitionRoot != NULL &&
-			!(resultRelInfo->ri_TrigDesc &&
-			  resultRelInfo->ri_TrigDesc->trig_insert_before_row))
-			check_partition_constr = false;
+		check_partition_constr =
+				ExecPartitionConstraintNeedsRecheck(resultRelInfo,
+									resultRelInfo->ri_PartitionRoot != NULL);
 
-		/* Check the constraints of the tuple */
+		/*
+		 * Check the tuple is valid against all constraints, and the partition
+		 * constraint, if required.
+		 */
 		if (resultRelationDesc->rd_att->constr || check_partition_constr)
-			ExecConstraints(resultRelInfo, slot, estate, true);
+			ExecConstraints(resultRelInfo, slot, estate,
+							check_partition_constr);
 
 		if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
 		{
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index fc6e9574e3..2272217695 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -198,6 +198,8 @@ extern void ExecInitRoutingInfo(ModifyTableState *mtstate,
 					PartitionTupleRouting *proute,
 					ResultRelInfo *partRelInfo,
 					int partidx);
+extern bool ExecPartitionConstraintNeedsRecheck(ResultRelInfo *resultRelInfo,
+									bool tupleRouting);
 extern void ExecSetupChildParentMapForLeaf(PartitionTupleRouting *proute);
 extern TupleConversionMap *TupConvMapForLeaf(PartitionTupleRouting *proute,
 				  ResultRelInfo *rootRelInfo, int leaf_index);
