diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 2854f21..39c2921 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -165,12 +165,9 @@ typedef struct CopyStateData
 	bool		volatile_defexprs;	/* is any of defexprs volatile? */
 	List	   *range_table;
 
-	PartitionDispatch *partition_dispatch_info;
-	int			num_dispatch;	/* Number of entries in the above array */
-	int			num_partitions; /* Number of members in the following arrays */
-	ResultRelInfo **partitions;	/* Per partition result relation pointers */
-	TupleConversionMap **partition_tupconv_maps;
-	TupleTableSlot *partition_tuple_slot;
+	PartitionTupleRouting *partition_tuple_routing; /* all tuple-routing info
+													 * for partitions.
+													 */
 	TransitionCaptureState *transition_capture;
 	TupleConversionMap **transition_tupconv_maps;
 
@@ -2471,30 +2468,16 @@ CopyFrom(CopyState cstate)
 	 */
 	if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
-		PartitionDispatch *partition_dispatch_info;
-		ResultRelInfo **partitions;
-		TupleConversionMap **partition_tupconv_maps;
-		TupleTableSlot *partition_tuple_slot;
-		int			num_parted,
-					num_partitions;
+		PartitionTupleRouting *ptr;
 
 		ExecSetupPartitionTupleRouting(cstate->rel,
 									   NULL,
 									   0,
 									   1,
 									   estate,
-									   &partition_dispatch_info,
-									   &partitions,
-									   &partition_tupconv_maps,
-									   NULL,
-									   &partition_tuple_slot,
-									   &num_parted, &num_partitions);
-		cstate->partition_dispatch_info = partition_dispatch_info;
-		cstate->num_dispatch = num_parted;
-		cstate->partitions = partitions;
-		cstate->num_partitions = num_partitions;
-		cstate->partition_tupconv_maps = partition_tupconv_maps;
-		cstate->partition_tuple_slot = partition_tuple_slot;
+									   &cstate->partition_tuple_routing);
+
+		ptr = cstate->partition_tuple_routing;
 
 		/*
 		 * If we are capturing transition tuples, they may need to be
@@ -2507,11 +2490,11 @@ CopyFrom(CopyState cstate)
 			int			i;
 
 			cstate->transition_tupconv_maps = (TupleConversionMap **)
-				palloc0(sizeof(TupleConversionMap *) * cstate->num_partitions);
-			for (i = 0; i < cstate->num_partitions; ++i)
+				palloc0(sizeof(TupleConversionMap *) * ptr->num_partitions);
+			for (i = 0; i < ptr->num_partitions; ++i)
 			{
 				cstate->transition_tupconv_maps[i] =
-					convert_tuples_by_name(RelationGetDescr(cstate->partitions[i]->ri_RelationDesc),
+					convert_tuples_by_name(RelationGetDescr(ptr->partitions[i]->ri_RelationDesc),
 										   RelationGetDescr(cstate->rel),
 										   gettext_noop("could not convert row type"));
 			}
@@ -2531,7 +2514,7 @@ CopyFrom(CopyState cstate)
 	if ((resultRelInfo->ri_TrigDesc != NULL &&
 		 (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
 		  resultRelInfo->ri_TrigDesc->trig_insert_instead_row)) ||
-		cstate->partition_dispatch_info != NULL ||
+		cstate->partition_tuple_routing != NULL ||
 		cstate->volatile_defexprs)
 	{
 		useHeapMultiInsert = false;
@@ -2606,10 +2589,11 @@ CopyFrom(CopyState cstate)
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
 		/* Determine the partition to heap_insert the tuple into */
-		if (cstate->partition_dispatch_info)
+		if (cstate->partition_tuple_routing)
 		{
 			int			leaf_part_index;
 			TupleConversionMap *map;
+			PartitionTupleRouting *ptr = cstate->partition_tuple_routing;
 
 			/*
 			 * Away we go ... If we end up not finding a partition after all,
@@ -2620,11 +2604,11 @@ CopyFrom(CopyState cstate)
 			 * partition, respectively.
 			 */
 			leaf_part_index = ExecFindPartition(resultRelInfo,
-												cstate->partition_dispatch_info,
+												ptr->partition_dispatch_info,
 												slot,
 												estate);
 			Assert(leaf_part_index >= 0 &&
-				   leaf_part_index < cstate->num_partitions);
+				   leaf_part_index < ptr->num_partitions);
 
 			/*
 			 * If this tuple is mapped to a partition that is not same as the
@@ -2642,7 +2626,7 @@ CopyFrom(CopyState cstate)
 			 * to the selected partition.
 			 */
 			saved_resultRelInfo = resultRelInfo;
-			resultRelInfo = cstate->partitions[leaf_part_index];
+			resultRelInfo = ptr->partitions[leaf_part_index];
 
 			/* We do not yet have a way to insert into a foreign partition */
 			if (resultRelInfo->ri_FdwRoutine)
@@ -2689,7 +2673,7 @@ CopyFrom(CopyState cstate)
 			 * We might need to convert from the parent rowtype to the
 			 * partition rowtype.
 			 */
-			map = cstate->partition_tupconv_maps[leaf_part_index];
+			map = ptr->parentchild_tupconv_maps[leaf_part_index];
 			if (map)
 			{
 				Relation	partrel = resultRelInfo->ri_RelationDesc;
@@ -2701,7 +2685,7 @@ CopyFrom(CopyState cstate)
 				 * point on.  Use a dedicated slot from this point on until
 				 * we're finished dealing with the partition.
 				 */
-				slot = cstate->partition_tuple_slot;
+				slot = ptr->partition_tuple_slot;
 				Assert(slot != NULL);
 				ExecSetSlotDescriptor(slot, RelationGetDescr(partrel));
 				ExecStoreTuple(tuple, slot, InvalidBuffer, true);
@@ -2853,8 +2837,9 @@ CopyFrom(CopyState cstate)
 	ExecCloseIndices(resultRelInfo);
 
 	/* Close all the partitioned tables, leaf partitions, and their indices */
-	if (cstate->partition_dispatch_info)
+	if (cstate->partition_tuple_routing)
 	{
+		PartitionTupleRouting *ptr = cstate->partition_tuple_routing;
 		int			i;
 
 		/*
@@ -2863,23 +2848,23 @@ CopyFrom(CopyState cstate)
 		 * the main target table of COPY that will be closed eventually by
 		 * DoCopy().  Also, tupslot is NULL for the root partitioned table.
 		 */
-		for (i = 1; i < cstate->num_dispatch; i++)
+		for (i = 1; i < ptr->num_dispatch; i++)
 		{
-			PartitionDispatch pd = cstate->partition_dispatch_info[i];
+			PartitionDispatch pd = ptr->partition_dispatch_info[i];
 
 			heap_close(pd->reldesc, NoLock);
 			ExecDropSingleTupleTableSlot(pd->tupslot);
 		}
-		for (i = 0; i < cstate->num_partitions; i++)
+		for (i = 0; i < ptr->num_partitions; i++)
 		{
-			ResultRelInfo *resultRelInfo = cstate->partitions[i];
+			ResultRelInfo *resultRelInfo = ptr->partitions[i];
 
 			ExecCloseIndices(resultRelInfo);
 			heap_close(resultRelInfo->ri_RelationDesc, NoLock);
 		}
 
 		/* Release the standalone partition tuple descriptor */
-		ExecDropSingleTupleTableSlot(cstate->partition_tuple_slot);
+		ExecDropSingleTupleTableSlot(ptr->partition_tuple_slot);
 	}
 
 	/* Close any trigger target relations */
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 2ac7484..3b72547 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -49,22 +49,9 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
  *      INSERT.
  *
  * Output arguments:
- * 'pd' receives an array of PartitionDispatch objects with one entry for
- *		every partitioned table in the partition tree
- * 'partitions' receives an array of ResultRelInfo* objects with one entry for
- *		every leaf partition in the partition tree
- * 'tup_conv_maps' receives an array of TupleConversionMap objects with one
- *		entry for every leaf partition (required to convert input tuple based
- *		on the root table's rowtype to a leaf partition's rowtype after tuple
- *		routing is done)
- * 'partition_tuple_slot' receives a standalone TupleTableSlot to be used
- *		to manipulate any given leaf partition's rowtype after that partition
- *		is chosen by tuple-routing.
- * 'num_parted' receives the number of partitioned tables in the partition
- *		tree (= the number of entries in the 'pd' output array)
- * 'num_partitions' receives the number of leaf partitions in the partition
- *		tree (= the number of entries in the 'partitions' and 'tup_conv_maps'
- *		output arrays
+ *
+ * 'partition_tuple_routing' encapsulates all the partition related information
+ *		required to do tuple routing.
  *
  * Note that all the relations in the partition tree are locked using the
  * RowExclusiveLock mode upon return from this function.
@@ -75,12 +62,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 							   int num_update_rri,
 							   Index resultRTindex,
 							   EState *estate,
-							   PartitionDispatch **pd,
-							   ResultRelInfo ***partitions,
-							   TupleConversionMap ***tup_conv_maps,
-							   int **subplan_leaf_map,
-							   TupleTableSlot **partition_tuple_slot,
-							   int *num_parted, int *num_partitions)
+							   PartitionTupleRouting **partition_tuple_routing)
 {
 	TupleDesc	tupDesc = RelationGetDescr(rel);
 	List	   *leaf_parts;
@@ -89,20 +71,23 @@ ExecSetupPartitionTupleRouting(Relation rel,
 	ResultRelInfo *leaf_part_arr = NULL;
 	int			update_rri_index = 0;
 	bool		is_update = (num_update_rri > 0);
+	PartitionTupleRouting *ptr;
 
 	/*
 	 * Get the information about the partition tree after locking all the
 	 * partitions.
 	 */
 	(void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
-	*pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts);
-	*num_partitions = list_length(leaf_parts);
-	if (subplan_leaf_map)
-		*subplan_leaf_map = NULL;
-	*partitions = (ResultRelInfo **) palloc(*num_partitions *
+	ptr = *partition_tuple_routing =
+		(PartitionTupleRouting *) palloc0(sizeof(PartitionTupleRouting));
+	ptr->partition_dispatch_info =
+		RelationGetPartitionDispatchInfo(rel, &ptr->num_dispatch, &leaf_parts);
+	ptr->num_partitions = list_length(leaf_parts);
+	ptr->partitions = (ResultRelInfo **) palloc(ptr->num_partitions *
 											sizeof(ResultRelInfo *));
-	*tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions *
-													 sizeof(TupleConversionMap *));
+	ptr->parentchild_tupconv_maps =
+		(TupleConversionMap **) palloc0(ptr->num_partitions *
+										sizeof(TupleConversionMap *));
 
 	if (is_update)
 	{
@@ -123,7 +108,13 @@ ExecSetupPartitionTupleRouting(Relation rel,
 		 * Prepare for generating the mapping from subplan result rels to leaf
 		 * partition position.
 		 */
-		*subplan_leaf_map = palloc(num_update_rri * sizeof(int));
+		ptr->subplan_partition_offsets = palloc(num_update_rri * sizeof(int));
+
+		/*
+		 * For UPDATEs, we require an additional tuple slot for storing
+		 * transient tuples that are converted to the root table descriptor.
+		 */
+		ptr->root_tuple_slot = MakeTupleTableSlot();
 	}
 	else
 	{
@@ -132,7 +123,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 		 * repeated pallocs by allocating memory for all the result rels in
 		 * bulk.
 		 */
-		leaf_part_arr = (ResultRelInfo *) palloc0(*num_partitions *
+		leaf_part_arr = (ResultRelInfo *) palloc0(ptr->num_partitions *
 												  sizeof(ResultRelInfo));
 	}
 
@@ -142,7 +133,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 	 * (such as ModifyTableState) and released when the node finishes
 	 * processing.
 	 */
-	*partition_tuple_slot = MakeTupleTableSlot();
+	ptr->partition_tuple_slot = MakeTupleTableSlot();
 
 	i = 0;
 	foreach(cell, leaf_parts)
@@ -173,7 +164,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 				 * Save the position of this update rel in the leaf partitions
 				 * array
 				 */
-				(*subplan_leaf_map)[update_rri_index] = i;
+				ptr->subplan_partition_offsets[update_rri_index] = i;
 
 				update_rri_index++;
 			}
@@ -211,7 +202,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 		 * Save a tuple conversion map to convert a tuple routed to this
 		 * partition from the parent's type to the partition's.
 		 */
-		(*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
+		ptr->parentchild_tupconv_maps[i] = convert_tuples_by_name(tupDesc, part_tupdesc,
 													 gettext_noop("could not convert row type"));
 
 		/*
@@ -233,7 +224,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 		estate->es_leaf_result_relations =
 			lappend(estate->es_leaf_result_relations, leaf_part_rri);
 
-		(*partitions)[i] = leaf_part_rri;
+		ptr->partitions[i] = leaf_part_rri;
 		i++;
 	}
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index a0d8259..75269bf 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -324,10 +324,11 @@ ExecInsert(ModifyTableState *mtstate,
 	resultRelInfo = estate->es_result_relation_info;
 
 	/* Determine the partition to heap_insert the tuple into */
-	if (mtstate->mt_partition_dispatch_info)
+	if (mtstate->mt_partition_tuple_routing)
 	{
 		int			leaf_part_index;
 		ResultRelInfo *rootResultRelInfo;
+		PartitionTupleRouting *ptr = mtstate->mt_partition_tuple_routing;
 
 		/*
 		 * For UPDATE, the resultRelInfo is not the root partitioned table, so
@@ -353,7 +354,7 @@ ExecInsert(ModifyTableState *mtstate,
 			tuple = ConvertPartitionTupleSlot(mtstate,
 											  tupconv_map,
 											  tuple,
-											  mtstate->mt_root_tuple_slot,
+											  ptr->root_tuple_slot,
 											  &slot);
 		}
 		else
@@ -363,23 +364,23 @@ ExecInsert(ModifyTableState *mtstate,
 		 * Away we go ... If we end up not finding a partition after all,
 		 * ExecFindPartition() does not return and errors out instead.
 		 * Otherwise, the returned value is to be used as an index into arrays
-		 * mt_partitions[] and mt_parentchild_tupconv_maps[] that will get us
-		 * the ResultRelInfo and TupleConversionMap for the partition,
+		 * ptr->partitions[] and ptr->parentchild_tupconv_maps[] that will get
+		 * us the ResultRelInfo and TupleConversionMap for the partition,
 		 * respectively.
 		 */
 		leaf_part_index = ExecFindPartition(rootResultRelInfo,
-											mtstate->mt_partition_dispatch_info,
+											ptr->partition_dispatch_info,
 											slot,
 											estate);
 		Assert(leaf_part_index >= 0 &&
-			   leaf_part_index < mtstate->mt_num_partitions);
+			   leaf_part_index < ptr->num_partitions);
 
 		/*
 		 * Save the old ResultRelInfo and switch to the one corresponding to
 		 * the selected partition.
 		 */
 		saved_resultRelInfo = resultRelInfo;
-		resultRelInfo = mtstate->mt_partitions[leaf_part_index];
+		resultRelInfo = ptr->partitions[leaf_part_index];
 
 		/* We do not yet have a way to insert into a foreign partition */
 		if (resultRelInfo->ri_FdwRoutine)
@@ -433,9 +434,9 @@ ExecInsert(ModifyTableState *mtstate,
 		 * rowtype.
 		 */
 		tuple = ConvertPartitionTupleSlot(mtstate,
-										  mtstate->mt_parentchild_tupconv_maps[leaf_part_index],
+										  ptr->parentchild_tupconv_maps[leaf_part_index],
 										  tuple,
-										  mtstate->mt_partition_tuple_slot,
+										  ptr->partition_tuple_slot,
 										  &slot);
 	}
 
@@ -1184,7 +1185,7 @@ lreplace:;
 			 * partition tuple routing setup. In that case, fail with
 			 * partition constraint violation error.
 			 */
-			if (mtstate->mt_partition_dispatch_info == NULL)
+			if (mtstate->mt_partition_tuple_routing == NULL)
 				ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
 
 			/* Do the row movement. */
@@ -1702,13 +1703,14 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
 		mtstate->mt_oc_transition_capture != NULL)
 	{
 		int			numResultRelInfos;
+		PartitionTupleRouting *ptr = mtstate->mt_partition_tuple_routing;
 
-		numResultRelInfos = (mtstate->mt_partition_tuple_slot != NULL ?
-							 mtstate->mt_num_partitions :
+		numResultRelInfos = (ptr != NULL ?
+							 ptr->num_partitions :
 							 mtstate->mt_nplans);
 
 		ExecSetupChildParentMap(mtstate, targetRelInfo, numResultRelInfos,
-								(mtstate->mt_partition_dispatch_info != NULL));
+								(ptr != NULL));
 
 		/*
 		 * Install the conversion map for the first plan for UPDATE and DELETE
@@ -1751,7 +1753,7 @@ ExecSetupChildParentMap(ModifyTableState *mtstate,
 		 * has to be per-leaf. If that map is per-subplan, we won't be able to
 		 * access the maps leaf-partition-wise. But if the map is per-leaf, we
 		 * will be able to access the maps subplan-wise using the
-		 * mt_subplan_partition_offsets map using function
+		 * subplan_partition_offsets map using function
 		 * tupconv_map_for_subplan().  So if the callers might need to access
 		 * the map both leaf-partition-wise and subplan-wise, they should make
 		 * sure that the first time this function is called, it should be
@@ -1781,7 +1783,10 @@ ExecSetupChildParentMap(ModifyTableState *mtstate,
 		 * For tuple routing among partitions, we need TupleDescs based
 		 * on the partition routing table.
 		 */
-		ResultRelInfo **resultRelInfos = mtstate->mt_partitions;
+		ResultRelInfo **resultRelInfos;
+
+		Assert(mtstate->mt_partition_tuple_routing != NULL);
+		resultRelInfos = mtstate->mt_partition_tuple_routing->partitions;
 
 		for (i = 0; i < numResultRelInfos; ++i)
 		{
@@ -1828,11 +1833,12 @@ tupconv_map_for_subplan(ModifyTableState *mtstate, int whichplan)
 	if (mtstate->mt_is_tupconv_perpart)
 	{
 		int leaf_index;
+		PartitionTupleRouting *ptr = mtstate->mt_partition_tuple_routing;
 
-		Assert(mtstate->mt_subplan_partition_offsets != NULL);
-		leaf_index = mtstate->mt_subplan_partition_offsets[whichplan];
+		Assert(ptr && ptr->subplan_partition_offsets != NULL);
+		leaf_index = ptr->subplan_partition_offsets[whichplan];
 
-		Assert(leaf_index >= 0 && leaf_index < mtstate->mt_num_partitions);
+		Assert(leaf_index >= 0 && leaf_index < ptr->num_partitions);
 		return mtstate->mt_childparent_tupconv_maps[leaf_index];
 	}
 	else
@@ -2119,6 +2125,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	int			i;
 	Relation	rel;
 	bool		update_tuple_routing_needed = node->partKeyUpdated;
+	PartitionTupleRouting *ptr = NULL;
+	int			num_partitions = 0;
 
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -2252,33 +2260,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
 		(operation == CMD_INSERT || update_tuple_routing_needed))
 	{
-		PartitionDispatch *partition_dispatch_info;
-		ResultRelInfo **partitions;
-		TupleConversionMap **partition_tupconv_maps;
-		int *subplan_leaf_map;
-		TupleTableSlot *partition_tuple_slot;
-		int			num_parted,
-					num_partitions;
-
 		ExecSetupPartitionTupleRouting(rel,
 									   mtstate->resultRelInfo,
 									   (operation == CMD_UPDATE ? nplans : 0),
 									   node->nominalRelation,
 									   estate,
-									   &partition_dispatch_info,
-									   &partitions,
-									   &partition_tupconv_maps,
-									   &subplan_leaf_map,
-									   &partition_tuple_slot,
-									   &num_parted, &num_partitions);
-		mtstate->mt_partition_dispatch_info = partition_dispatch_info;
-		mtstate->mt_num_dispatch = num_parted;
-		mtstate->mt_partitions = partitions;
-		mtstate->mt_num_partitions = num_partitions;
-		mtstate->mt_parentchild_tupconv_maps = partition_tupconv_maps;
-		mtstate->mt_subplan_partition_offsets = subplan_leaf_map;
-		mtstate->mt_partition_tuple_slot = partition_tuple_slot;
-		mtstate->mt_root_tuple_slot = MakeTupleTableSlot();
+									   &mtstate->mt_partition_tuple_routing);
+
+		ptr = mtstate->mt_partition_tuple_routing;
+		num_partitions = ptr->num_partitions;
 
 		/*
 		 * Below are required as reference objects for mapping partition
@@ -2340,7 +2330,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 * will suffice.  This only occurs for the INSERT case or for UPDATE row
 	 * movement. DELETEs and local UPDATEs are handled above.
 	 */
-	if (node->withCheckOptionLists != NIL && mtstate->mt_num_partitions > 0)
+	if (node->withCheckOptionLists != NIL && num_partitions > 0)
 	{
 		List	   *first_wcoList;
 
@@ -2360,14 +2350,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 				mtstate->mt_nplans == 1));
 
 		first_wcoList = linitial(node->withCheckOptionLists);
-		for (i = 0; i < mtstate->mt_num_partitions; i++)
+		for (i = 0; i < num_partitions; i++)
 		{
 			Relation	partrel;
 			List	   *mapped_wcoList;
 			List	   *wcoExprs = NIL;
 			ListCell   *ll;
 
-			resultRelInfo = mtstate->mt_partitions[i];
+			resultRelInfo = ptr->partitions[i];
 
 			/*
 			 * If we are referring to a resultRelInfo from one of the update
@@ -2445,12 +2435,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		 * row movement. DELETEs and local UPDATEs are handled above.
 		 */
 		firstReturningList = linitial(node->returningLists);
-		for (i = 0; i < mtstate->mt_num_partitions; i++)
+		for (i = 0; i < num_partitions; i++)
 		{
 			Relation	partrel;
 			List	   *rlist;
 
-			resultRelInfo = mtstate->mt_partitions[i];
+			resultRelInfo = ptr->partitions[i];
 
 			/*
 			 * If we are referring to a resultRelInfo from one of the update
@@ -2733,41 +2723,46 @@ ExecEndModifyTable(ModifyTableState *node)
 	/*
 	 * Close all the partitioned tables, leaf partitions, and their indices
 	 *
-	 * Remember node->mt_partition_dispatch_info[0] corresponds to the root
+	 * Remember ptr->partition_dispatch_info[0] corresponds to the root
 	 * partitioned table, which we must not try to close, because it is the
 	 * main target table of the query that will be closed by ExecEndPlan().
 	 * Also, tupslot is NULL for the root partitioned table.
 	 */
-	for (i = 1; i < node->mt_num_dispatch; i++)
+	if (node->mt_partition_tuple_routing)
 	{
-		PartitionDispatch pd = node->mt_partition_dispatch_info[i];
+		PartitionTupleRouting *ptr = node->mt_partition_tuple_routing;
 
-		heap_close(pd->reldesc, NoLock);
-		ExecDropSingleTupleTableSlot(pd->tupslot);
-	}
-	for (i = 0; i < node->mt_num_partitions; i++)
-	{
-		ResultRelInfo *resultRelInfo = node->mt_partitions[i];
+		for (i = 1; i < ptr->num_dispatch; i++)
+		{
+			PartitionDispatch pd = ptr->partition_dispatch_info[i];
 
-		/*
-		 * If this result rel is one of the subplan result rels, let
-		 * ExecEndPlan() close it. For INSERTs, this does not apply because
-		 * leaf partition result rels are always newly allocated.
-		 */
-		if (operation == CMD_UPDATE &&
-			resultRelInfo >= node->resultRelInfo &&
-			resultRelInfo < node->resultRelInfo + node->mt_nplans)
-			continue;
+			heap_close(pd->reldesc, NoLock);
+			ExecDropSingleTupleTableSlot(pd->tupslot);
+		}
+		for (i = 0; i < ptr->num_partitions; i++)
+		{
+			ResultRelInfo *resultRelInfo = ptr->partitions[i];
 
-		ExecCloseIndices(resultRelInfo);
-		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
-	}
+			/*
+			 * If this result rel is one of the subplan result rels, let
+			 * ExecEndPlan() close it. For INSERTs, this does not apply because
+			 * leaf partition result rels are always newly allocated.
+			 */
+			if (operation == CMD_UPDATE &&
+				resultRelInfo >= node->resultRelInfo &&
+				resultRelInfo < node->resultRelInfo + node->mt_nplans)
+				continue;
+
+			ExecCloseIndices(resultRelInfo);
+			heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+		}
 
-	/* Release the standalone partition tuple descriptors, if any */
-	if (node->mt_root_tuple_slot)
-		ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
-	if (node->mt_partition_tuple_slot)
-		ExecDropSingleTupleTableSlot(node->mt_partition_tuple_slot);
+		/* Release the standalone partition tuple descriptors, if any */
+		if (ptr->root_tuple_slot)
+			ExecDropSingleTupleTableSlot(ptr->root_tuple_slot);
+		if (ptr->partition_tuple_slot)
+			ExecDropSingleTupleTableSlot(ptr->partition_tuple_slot);
+	}
 
 	/*
 	 * Free the exprcontext
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 41be2cf..7e69c48 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -49,17 +49,51 @@ typedef struct PartitionDispatchData
 
 typedef struct PartitionDispatchData *PartitionDispatch;
 
+/*-----------------------
+ * PartitionTupleRouting - Encapsulates all information required to execute
+ * tuple-routing between partitions.
+ *
+ * partition_dispatch_info		Array of PartitionDispatch objects with one
+ *								entry for every partitioned table in the
+ *								partition tree.
+ * num_dispatch					number of partitioned tables in the partition
+ *								tree (= length of partition_dispatch_info[])
+ * partitions					Array of ResultRelInfo* objects with one entry
+ *								for every leaf partition in the partition tree.
+ * num_partitions				Number of leaf partitions in the partition tree
+ *								(= 'partitions' array length)
+ * parentchild_tupconv_maps		Array of TupleConversionMap objects with one
+ *								entry for every leaf partition (required to
+ *								convert input tuple based on the root table's
+ *								rowtype to a leaf partition's rowtype after
+ *								tuple routing is done)
+ * subplan_partition_offsets	int Array ordered by UPDATE subplans. Each
+ *								element of this array has the index into the
+ *								corresponding partition in 'partitions' array.
+ * partition_tuple_slot			TupleTableSlot to be used to manipulate any
+ *								given leaf partition's rowtype after that
+ *								partition is chosen for insertion by
+ *								tuple-routing.
+ *-----------------------
+ */
+typedef struct PartitionTupleRouting
+{
+	PartitionDispatch *partition_dispatch_info;
+	int			num_dispatch;
+	ResultRelInfo **partitions;
+	int			num_partitions;
+	TupleConversionMap **parentchild_tupconv_maps;
+	int		   *subplan_partition_offsets;
+	TupleTableSlot *partition_tuple_slot;
+	TupleTableSlot *root_tuple_slot;
+} PartitionTupleRouting;
+
 extern void ExecSetupPartitionTupleRouting(Relation rel,
 							   ResultRelInfo *update_rri,
 							   int num_update_rri,
 							   Index resultRTindex,
 							   EState *estate,
-							   PartitionDispatch **pd,
-							   ResultRelInfo ***partitions,
-							   TupleConversionMap ***tup_conv_maps,
-							   int **subplan_leaf_map,
-							   TupleTableSlot **partition_tuple_slot,
-							   int *num_parted, int *num_partitions);
+							   PartitionTupleRouting **partition_tuple_routing);
 extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
 				  PartitionDispatch *pd,
 				  TupleTableSlot *slot,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d2e8060..64cf3dd 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -976,24 +976,14 @@ 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 */
-	struct PartitionDispatchData **mt_partition_dispatch_info;
-	/* Tuple-routing support info */
-	int			mt_num_dispatch;	/* Number of entries in the above array */
-	int			mt_num_partitions;	/* Number of members in the following
-									 * arrays */
-	ResultRelInfo **mt_partitions;	/* Per partition result relation pointers */
-	TupleTableSlot *mt_partition_tuple_slot;
-	TupleTableSlot *mt_root_tuple_slot;
+	struct PartitionTupleRouting *mt_partition_tuple_routing; /* Tuple-routing support info */
 	struct TransitionCaptureState *mt_transition_capture;
 	/* controls transition table population for specified operation */
 	struct TransitionCaptureState *mt_oc_transition_capture;
 	/* controls transition table population for INSERT...ON CONFLICT UPDATE */
-	TupleConversionMap **mt_parentchild_tupconv_maps;
-	/* Per partition map for tuple conversion from root to leaf */
 	TupleConversionMap **mt_childparent_tupconv_maps;
 	/* Per plan/partition map for tuple conversion from child to root */
 	bool		mt_is_tupconv_perpart;	/* Is the above map per-partition ? */
-	int		*mt_subplan_partition_offsets;
 	/* Stores position of update result rels in leaf partitions */
 } ModifyTableState;
 
