diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 523eb2f995..dee32e827e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2323,7 +2323,7 @@ CopyFrom(CopyState cstate)
 	TupleTableSlot *myslot;
 	MemoryContext oldcontext = CurrentMemoryContext;
 
-	PartitionTupleRouting *proute = NULL;
+	struct PartitionTupleRouting *proute = NULL;
 	ExprContext *secondaryExprContext = NULL;
 	ErrorContextCallback errcallback;
 	CommandId	mycid = GetCurrentCommandId(true);
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 930349ac47..0e0f4e8294 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -33,6 +33,67 @@
 
 #define PARTITION_ROUTING_INITSIZE	8
 
+ /*-----------------------
+  * PartitionTupleRouting - Encapsulates all information required to
+  * route a tuple inserted into a partitioned table to one of its leaf
+  * partitions
+  *
+  * partition_root			The partitioned table that's the target of the
+  *							command.
+  *
+  * partition_dispatch_info	Array of 'dispatch_allocsize' elements containing
+  *							a pointer to a PartitionDispatch objects for every
+  *							partitioned table touched by tuple routing.  The
+  *							entry for the target partitioned table is *always*
+  *							present in the 0th element of this array.  See
+  *							comment for PartitionDispatchData->indexes for
+  *							details on how this array is indexed.
+  *
+  * num_dispatch				The current number of items stored in the
+  *							'partition_dispatch_info' array.  Also serves as
+  *							the index of the next free array element for new
+  *							PartitionDispatch which need to be stored.
+  *
+  * dispatch_allocsize		The current allocated size of the
+  *							'partition_dispatch_info' array.
+  *
+  * partitions				Array of 'partitions_allocsize' elements
+  *							containing pointers to a ResultRelInfos of all
+  *							leaf partitions touched by tuple routing.  Some of
+  *							these are pointers to ResultRelInfos which are
+  *							borrowed out of 'subplan_resultrel_hash'.  The
+  *							remainder have been built especially for tuple
+  *							routing.  See comment for
+  *							PartitionDispatchData->indexes for details on how
+  *							this array is indexed.
+  *
+  * num_partitions			The current number of items stored in the
+  *							'partitions' array.  Also serves as the index of
+  *							the next free array element for new ResultRelInfos
+  *							which need to be stored.
+  *
+  * partitions_allocsize		The current allocated size of the 'partitions'
+  *							array.
+  *
+  * subplan_resultrel_hash	Hash table to store subplan ResultRelInfos by Oid.
+  *							This is used to cache ResultRelInfos from subplans
+  *							of an UPDATE ModifyTable node.  Some of these may
+  *							be useful for tuple routing to save having to build
+  *							duplicates.
+  *-----------------------
+  */
+typedef struct PartitionTupleRouting
+{
+	Relation	partition_root;
+	PartitionDispatch *partition_dispatch_info;
+	int			num_dispatch;
+	int			dispatch_allocsize;
+	ResultRelInfo **partitions;
+	int			num_partitions;
+	int			partitions_allocsize;
+	HTAB	   *subplan_resultrel_hash;
+} PartitionTupleRouting;
+
 /*-----------------------
  * PartitionDispatch - information about one partitioned table in a partition
  * hierarchy required to route a tuple to any of its partitions.  A
@@ -54,8 +115,8 @@
  *				partitioned table then we store the index into the
  *				encapsulating PartitionTupleRouting's
  *				'partition_dispatch_info' array.  An index of -1 means we've
- *				not yet allocated anything in PartitionTupleRouting for the
- *				partition.
+ *				not yet allocated anything in PartitionTupleRouting for
+ *				the partition.
  *-----------------------
  */
 typedef struct PartitionDispatchData
@@ -134,7 +195,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
 	 * More space can be allocated later if we end up routing tuples to more
 	 * than that many partitions.
 	 *
-	 * Initially we must only setup 1 PartitionDispatch object; the one for
+	 * Initially we must only set up 1 PartitionDispatch object; the one for
 	 * the partitioned table that's the target of the command.  If we must
 	 * route a tuple via some sub-partitioned table, then its
 	 * PartitionDispatch is only built the first time it's required.
@@ -168,20 +229,11 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
 	 * Every time a tuple is routed to a partition that we've yet to set the
 	 * ResultRelInfo for, before we go to the trouble of making one, we check
 	 * for a pre-made one in the hash table.
-	 *
-	 * Also, we'll need a slot that will transiently store the tuple being
-	 * routed using the root parent's rowtype.
 	 */
 	if (node && node->operation == CMD_UPDATE)
-	{
 		ExecHashSubPlanResultRelsByOid(mtstate, proute);
-		proute->root_tuple_slot = MakeTupleTableSlot(RelationGetDescr(rel));
-	}
 	else
-	{
 		proute->subplan_resultrel_hash = NULL;
-		proute->root_tuple_slot = NULL;
-	}
 
 	return proute;
 }
@@ -1060,10 +1112,6 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate,
 		ExecCloseIndices(resultRelInfo);
 		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 	}
-
-	/* Release the standalone partition tuple descriptors, if any */
-	if (proute->root_tuple_slot)
-		ExecDropSingleTupleTableSlot(proute->root_tuple_slot);
 }
 
 /* ----------------
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index aafeea3a8c..0f704308be 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -64,7 +64,7 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
 					 TupleTableSlot **returning);
 static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
 						EState *estate,
-						PartitionTupleRouting *proute,
+						struct PartitionTupleRouting *proute,
 						ResultRelInfo *targetRelInfo,
 						TupleTableSlot *slot);
 static ResultRelInfo *getTargetResultRelInfo(ModifyTableState *node);
@@ -1068,7 +1068,7 @@ lreplace:;
 			bool		tuple_deleted;
 			TupleTableSlot *ret_slot;
 			TupleTableSlot *epqslot = NULL;
-			PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
+			struct PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
 			int			map_index;
 			TupleConversionMap *tupconv_map;
 
@@ -1162,7 +1162,8 @@ lreplace:;
 			tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
 			if (tupconv_map != NULL)
 				slot = execute_attr_map_slot(tupconv_map->attrMap,
-											 slot, proute->root_tuple_slot);
+											 slot,
+											 mtstate->mt_root_tuple_slot);
 
 			/*
 			 * Prepare for tuple routing, making it look like we're inserting
@@ -1692,7 +1693,7 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
 static TupleTableSlot *
 ExecPrepareTupleRouting(ModifyTableState *mtstate,
 						EState *estate,
-						PartitionTupleRouting *proute,
+						struct PartitionTupleRouting *proute,
 						ResultRelInfo *targetRelInfo,
 						TupleTableSlot *slot)
 {
@@ -1845,7 +1846,7 @@ static TupleTableSlot *
 ExecModifyTable(PlanState *pstate)
 {
 	ModifyTableState *node = castNode(ModifyTableState, pstate);
-	PartitionTupleRouting *proute = node->mt_partition_tuple_routing;
+	struct PartitionTupleRouting *proute = node->mt_partition_tuple_routing;
 	EState	   *estate = node->ps.state;
 	CmdType		operation = node->operation;
 	ResultRelInfo *saved_resultRelInfo;
@@ -2254,10 +2255,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 * descriptor of a source partition does not match the root partitioned
 	 * table descriptor.  In such a case we need to convert tuples to the root
 	 * tuple descriptor, because the search for destination partition starts
-	 * from the root.  Skip this setup if it's not a partition key update.
+	 * from the root.  We'll also need a slot to store these converted tuples.
+	 * We can skip this setup if it's not a partition key update.
 	 */
 	if (update_tuple_routing_needed)
+	{
 		ExecSetupChildParentMapForSubplan(mtstate);
+		mtstate->mt_root_tuple_slot = MakeTupleTableSlot(RelationGetDescr(rel));
+	}
 
 	/*
 	 * Initialize any WITH CHECK OPTION constraints if needed.
@@ -2597,9 +2602,16 @@ ExecEndModifyTable(ModifyTableState *node)
 														   resultRelInfo);
 	}
 
-	/* Close all the partitioned tables, leaf partitions, and their indices */
+	/*
+	 * Close all the partitioned tables, leaf partitions, and their indices
+	 * and release the slot used for tuple routing, if set.
+	 */
 	if (node->mt_partition_tuple_routing)
+	{
 		ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
+		if (node->mt_root_tuple_slot)
+			ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
+	}
 
 	/*
 	 * Free the exprcontext
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 78b9ac85c2..0123a38b59 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -48,76 +48,6 @@ typedef struct PartitionRoutingInfo
 	TupleTableSlot		   *pi_PartitionTupleSlot;
 } PartitionRoutingInfo;
 
-/*-----------------------
- * PartitionTupleRouting - Encapsulates all information required to
- * route a tuple inserted into a partitioned table to one of its leaf
- * partitions
- *
- * partition_root			The partitioned table that's the target of the
- *							command.
- *
- * partition_dispatch_info	Array of 'dispatch_allocsize' elements containing
- *							a pointer to a PartitionDispatch objects for every
- *							partitioned table touched by tuple routing.  The
- *							entry for the target partitioned table is *always*
- *							present in the 0th element of this array.  See
- *							comment for PartitionDispatchData->indexes for
- *							details on how this array is indexed.
- *
- * num_dispatch				The current number of items stored in the
- *							'partition_dispatch_info' array.  Also serves as
- *							the index of the next free array element for new
- *							PartitionDispatch which need to be stored.
- *
- * dispatch_allocsize		The current allocated size of the
- *							'partition_dispatch_info' array.
- *
- * partitions				Array of 'partitions_allocsize' elements
- *							containing pointers to a ResultRelInfos of all
- *							leaf partitions touched by tuple routing.  Some of
- *							these are pointers to ResultRelInfos which are
- *							borrowed out of 'subplan_resultrel_hash'.  The
- *							remainder have been built especially for tuple
- *							routing.  See comment for
- *							PartitionDispatchData->indexes for details on how
- *							this array is indexed.
- *
- * num_partitions			The current number of items stored in the
- *							'partitions' array.  Also serves as the index of
- *							the next free array element for new ResultRelInfos
- *							which need to be stored.
- *
- * partitions_allocsize		The current allocated size of the 'partitions'
- *							array.
- * Note: The following fields are used only when UPDATE ends up needing to
- * do tuple routing.
- *
- * subplan_resultrel_hash	Hash table to store subplan ResultRelInfos by Oid.
- *							This is used to cache ResultRelInfos from subplans
- *							of a ModifyTable node.  Some of these may be
- *							useful for tuple routing to save having to build
- *							duplicates.
- *
- * root_tuple_slot			During UPDATE tuple routing, this tuple slot is
- *							used to transiently store a tuple using the root
- *							table's rowtype after converting it from the
- *							tuple's source leaf partition's rowtype.  That is,
- *							if leaf partition's rowtype is different.
- *-----------------------
- */
-typedef struct PartitionTupleRouting
-{
-	Relation	partition_root;
-	PartitionDispatch *partition_dispatch_info;
-	int			num_dispatch;
-	int			dispatch_allocsize;
-	ResultRelInfo **partitions;
-	int			num_partitions;
-	int			partitions_allocsize;
-	TupleTableSlot *root_tuple_slot;
-	HTAB	   *subplan_resultrel_hash;
-} PartitionTupleRouting;
-
 /*
  * PartitionedRelPruningData - Per-partitioned-table data for run-time pruning
  * of partitions.  For a multilevel partitioned table, we have one of these
@@ -204,15 +134,15 @@ typedef struct PartitionPruneState
 	PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER];
 } PartitionPruneState;
 
-extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
+extern struct PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
 							   Relation rel);
 extern ResultRelInfo *ExecFindPartition(ModifyTableState *mtstate,
 				  ResultRelInfo *rootResultRelInfo,
-				  PartitionTupleRouting *proute,
+				  struct PartitionTupleRouting *proute,
 				  TupleTableSlot *slot,
 				  EState *estate);
 extern void ExecCleanupTupleRouting(ModifyTableState *mtstate,
-						PartitionTupleRouting *proute);
+						struct PartitionTupleRouting *proute);
 extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate,
 							  PartitionPruneInfo *partitionpruneinfo);
 extern Bitmapset *ExecFindMatchingSubPlans(PartitionPruneState *prunestate);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 8efc80f710..55c5e700b5 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -34,6 +34,7 @@
 
 struct PlanState;				/* forward references in this file */
 struct PartitionRoutingInfo;
+struct PartitionTupleRouting;
 struct ParallelHashJoinState;
 struct ExecRowMark;
 struct ExprState;
@@ -1073,6 +1074,12 @@ typedef struct ModifyTableState
 	TupleTableSlot *mt_existing;	/* slot to store existing target tuple in */
 	List	   *mt_excludedtlist;	/* the excluded pseudo relation's tlist  */
 	TupleTableSlot *mt_conflproj;	/* CONFLICT ... SET ... projection target */
+	
+	/*
+	 * Slot for storing tuples in the root partitioned table's rowtype during
+	 * an UPDATE of a partitioned table.
+	 */
+	TupleTableSlot *mt_root_tuple_slot;
 
 	/* Tuple-routing support info */
 	struct PartitionTupleRouting *mt_partition_tuple_routing;
