diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index db7d24a..2047557 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -3236,7 +3236,7 @@ CopyFrom(CopyState cstate)
 				 * we don't need to if there's no BR trigger defined on the
 				 * partition.
 				 */
-				if (resultRelInfo->ri_PartitionCheck &&
+				if (resultRelInfo->ri_RelationDesc->rd_rel->relispartition &&
 					(proute == NULL || has_before_insert_row_trig))
 					ExecPartitionCheck(resultRelInfo, myslot, estate, true);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4fdffad..7341ed2 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -53,6 +53,7 @@
 #include "jit/jit.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "parser/parsetree.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
@@ -1280,8 +1281,6 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 				  Relation partition_root,
 				  int instrument_options)
 {
-	List	   *partition_check = NIL;
-
 	MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
 	resultRelInfo->type = T_ResultRelInfo;
 	resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
@@ -1325,23 +1324,6 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 	resultRelInfo->ri_ReturningSlot = NULL;
 	resultRelInfo->ri_TrigOldSlot = NULL;
 	resultRelInfo->ri_TrigNewSlot = NULL;
-
-	/*
-	 * Partition constraint, which also includes the partition constraint of
-	 * all the ancestors that are partitions.  Note that it will be checked
-	 * even in the case of tuple-routing where this table is the target leaf
-	 * partition, if there any BR triggers defined on the table.  Although
-	 * tuple-routing implicitly preserves the partition constraint of the
-	 * target partition for a given row, the BR triggers may change the row
-	 * such that the constraint is no longer satisfied, which we must fail for
-	 * by checking it explicitly.
-	 *
-	 * If this is a partitioned table, the partition constraint (if any) of a
-	 * given row will be checked just before performing tuple-routing.
-	 */
-	partition_check = RelationGetPartitionQual(resultRelationDesc);
-
-	resultRelInfo->ri_PartitionCheck = partition_check;
 	resultRelInfo->ri_PartitionRoot = partition_root;
 	resultRelInfo->ri_PartitionInfo = NULL; /* may be set later */
 	resultRelInfo->ri_CopyMultiInsertBuffer = NULL;
@@ -1793,9 +1775,26 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	 */
 	if (resultRelInfo->ri_PartitionCheckExpr == NULL)
 	{
-		List	   *qual = resultRelInfo->ri_PartitionCheck;
+		/*
+		 * Switch to query-lifespan context before copying the partition
+		 * constraint expression from relcache.
+		 */
+		MemoryContext	oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
+		List   *qual = RelationGetPartitionQual(resultRelInfo->ri_RelationDesc);
+
+		/*
+		 * An empty partition constraint is only possible if the partition is
+		 * the default one and there are no other defined partitions.  In such
+		 * a case, no row inserted into this partition would violate the
+		 * partition constraint.  We represent that here with a constant-true
+		 * expression instead of just returning true here, so that subsequent
+		 * rows don't have to go back to relcache.
+		 */
+		if (qual == NIL)
+			qual = list_make1(makeBoolConst(true, false));
 
 		resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
+		MemoryContextSwitchTo(oldcxt);
 	}
 
 	/*
@@ -1904,7 +1903,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 	Bitmapset  *insertedCols;
 	Bitmapset  *updatedCols;
 
-	Assert(constr || resultRelInfo->ri_PartitionCheck);
+	Assert(constr);
 
 	if (constr && constr->has_not_null)
 	{
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index bd2ea25..33d2c6f 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -299,7 +299,7 @@ ExecFindPartition(ModifyTableState *mtstate,
 	 * 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 (rootResultRelInfo->ri_PartitionCheck)
+	if (rootResultRelInfo->ri_RelationDesc->rd_rel->relispartition)
 		ExecPartitionCheck(rootResultRelInfo, slot, estate, true);
 
 	/* start with the root partitioned table */
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 8f474fa..b29db7b 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -435,7 +435,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
 		/* Check the constraints of the tuple */
 		if (rel->rd_att->constr)
 			ExecConstraints(resultRelInfo, slot, estate);
-		if (resultRelInfo->ri_PartitionCheck)
+		if (rel->rd_rel->relispartition)
 			ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
 		/* OK, store the tuple and create index entries for it */
@@ -501,7 +501,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 		/* Check the constraints of the tuple */
 		if (rel->rd_att->constr)
 			ExecConstraints(resultRelInfo, slot, estate);
-		if (resultRelInfo->ri_PartitionCheck)
+		if (rel->rd_rel->relispartition)
 			ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
 		simple_table_tuple_update(rel, tid, slot, estate->es_snapshot,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 20a4c47..9812089 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -491,7 +491,7 @@ ExecInsert(ModifyTableState *mtstate,
 		 * 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 &&
+		if (resultRelationDesc->rd_rel->relispartition &&
 			(resultRelInfo->ri_PartitionRoot == NULL ||
 			 (resultRelInfo->ri_TrigDesc &&
 			  resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
@@ -1181,7 +1181,7 @@ lreplace:;
 		 * row.  So skip the WCO checks if the partition constraint fails.
 		 */
 		partition_constraint_failed =
-			resultRelInfo->ri_PartitionCheck &&
+			resultRelationDesc->rd_rel->relispartition &&
 			!ExecPartitionCheck(resultRelInfo, slot, estate, false);
 
 		if (!partition_constraint_failed &&
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index c37aafe..d239d28 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -1676,7 +1676,7 @@ apply_handle_tuple_routing(ResultRelInfo *relinfo,
 				 * Does the updated tuple still satisfy the current
 				 * partition's constraint?
 				 */
-				if (partrelinfo->ri_PartitionCheck == NULL ||
+				if (!partrel->rd_rel->relispartition ||
 					ExecPartitionCheck(partrelinfo, remoteslot_part, estate,
 									   false))
 				{
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 0b42dd6..62c6d70 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -477,9 +477,6 @@ typedef struct ResultRelInfo
 	/* ON CONFLICT evaluation state */
 	OnConflictSetState *ri_onConflict;
 
-	/* partition check expression */
-	List	   *ri_PartitionCheck;
-
 	/* partition check expression state */
 	ExprState  *ri_PartitionCheckExpr;
 
