diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index d869ac5..04a24c6 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -170,7 +170,6 @@ typedef struct CopyStateData
 	PartitionTupleRouting *partition_tuple_routing;
 
 	TransitionCaptureState *transition_capture;
-	TupleConversionMap **transition_tupconv_maps;
 
 	/*
 	 * These variables are used to reduce overhead in textual COPY FROM.
@@ -2481,19 +2480,7 @@ CopyFrom(CopyState cstate)
 		 * tuple).
 		 */
 		if (cstate->transition_capture != NULL)
-		{
-			int			i;
-
-			cstate->transition_tupconv_maps = (TupleConversionMap **)
-				palloc0(sizeof(TupleConversionMap *) * proute->num_partitions);
-			for (i = 0; i < proute->num_partitions; ++i)
-			{
-				cstate->transition_tupconv_maps[i] =
-					convert_tuples_by_name(RelationGetDescr(proute->partitions[i]->ri_RelationDesc),
-										   RelationGetDescr(cstate->rel),
-										   gettext_noop("could not convert row type"));
-			}
-		}
+			ExecSetupChildParentMapForLeaf(proute);
 	}
 
 	/*
@@ -2650,7 +2637,8 @@ CopyFrom(CopyState cstate)
 					 */
 					cstate->transition_capture->tcs_original_insert_tuple = NULL;
 					cstate->transition_capture->tcs_map =
-						cstate->transition_tupconv_maps[leaf_part_index];
+						TupConvMapForLeaf(proute, saved_resultRelInfo,
+										  leaf_part_index);
 				}
 				else
 				{
@@ -2667,7 +2655,7 @@ CopyFrom(CopyState cstate)
 			 * We might need to convert from the parent rowtype to the
 			 * partition rowtype.
 			 */
-			tuple = ConvertPartitionTupleSlot(proute->partition_tupconv_maps[leaf_part_index],
+			tuple = ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[leaf_part_index],
 											  tuple,
 											  proute->partition_tuple_slot,
 											  &slot);
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 5100d82..9d3677f 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -73,7 +73,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
 	proute->num_partitions = list_length(leaf_parts);
 	proute->partitions = (ResultRelInfo **) palloc(proute->num_partitions *
 												   sizeof(ResultRelInfo *));
-	proute->partition_tupconv_maps =
+	proute->parent_child_tupconv_maps =
 		(TupleConversionMap **) palloc0(proute->num_partitions *
 										sizeof(TupleConversionMap *));
 
@@ -198,7 +198,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
 		 * Save a tuple conversion map to convert a tuple routed to this
 		 * partition from the parent's type to the partition's.
 		 */
-		proute->partition_tupconv_maps[i] =
+		proute->parent_child_tupconv_maps[i] =
 			convert_tuples_by_name(tupDesc, part_tupdesc,
 								   gettext_noop("could not convert row type"));
 
@@ -358,6 +358,69 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 }
 
 /*
+ * Initialize the per-leaf-partition child-to-root tuple conversion map array.
+ *
+ * This map is required for capturing transition tuples when the target table
+ * is a partitioned table. For tuple that is routed by INSERT or UPDATE, we
+ * need to convert from the leaf partition to the target table descriptor.
+ *
+ */
+void
+ExecSetupChildParentMapForLeaf(PartitionTupleRouting *proute)
+{
+	Assert(proute != NULL);
+
+	/*
+	 * These array elements gets filled up with maps on an on-demand basis.
+	 * Initially just set all of them to NULL.
+	 */
+	proute->child_parent_tupconv_maps =
+		palloc0(sizeof(TupleConversionMap *) * proute->num_partitions);
+
+	/* Same is the case for this array. */
+	proute->child_parent_tupconv_map_not_reqd =
+		(bool *) palloc0(sizeof(bool) * proute->num_partitions);
+}
+
+/*
+ * TupConvMapForLeaf -- For a given leaf partitions index, get the tuple
+ * conversion map.
+ */
+TupleConversionMap *
+TupConvMapForLeaf(PartitionTupleRouting *proute,
+					 ResultRelInfo *rootRelInfo, int leaf_index)
+{
+	Assert(leaf_index >= 0 && leaf_index < proute->num_partitions);
+	Assert(proute->child_parent_tupconv_maps != NULL);
+
+	/* If it is already determined that the map is not required, return NULL. */
+	if (proute->child_parent_tupconv_map_not_reqd[leaf_index])
+		return NULL;
+	else
+	{
+		ResultRelInfo **resultRelInfos = proute->partitions;
+		TupleConversionMap **map = proute->child_parent_tupconv_maps + leaf_index;
+
+		/*
+		 * Either the map is already allocated, or it is yet to be determined
+		 * if the map is required.
+		 */
+		if (!*map)
+		{
+			*map =
+				convert_tuples_by_name(RelationGetDescr(resultRelInfos[leaf_index]->ri_RelationDesc),
+									   RelationGetDescr(rootRelInfo->ri_RelationDesc),
+									   gettext_noop("could not convert row type"));
+
+			/* Update the array element with the new info */
+			proute->child_parent_tupconv_map_not_reqd[leaf_index] =
+				(*map == NULL);
+		}
+		return *map;
+	}
+}
+
+/*
  * ConvertPartitionTupleSlot -- convenience function for tuple conversion using
  * 'map'. The tuple, if converted, is stored in 'new_slot', and 'p_my_slot' is
  * updated with the 'new_slot'. 'new_slot' typically should be one of the
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 5f1c51f..0335339 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -64,7 +64,9 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
 					 EState *estate,
 					 bool canSetTag,
 					 TupleTableSlot **returning);
-static void ExecSetupChildParentMap(ModifyTableState *mtstate, bool perleaf);
+static ResultRelInfo *getASTriggerResultRelInfo(ModifyTableState *node);
+static void ExecSetupChildParentMapForTcs(ModifyTableState *mtstate);
+static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate);
 static inline TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
 													int whichplan);
 /*
@@ -336,9 +338,10 @@ ExecInsert(ModifyTableState *mtstate,
 				 */
 				mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
 
-				Assert(mtstate->mt_is_tupconv_perpart);
 				mtstate->mt_transition_capture->tcs_map =
-					mtstate->mt_childparent_tupconv_maps[leaf_part_index];
+					TupConvMapForLeaf(proute,
+									  getASTriggerResultRelInfo(mtstate),
+									  leaf_part_index);
 			}
 			else
 			{
@@ -352,16 +355,16 @@ ExecInsert(ModifyTableState *mtstate,
 		}
 		if (mtstate->mt_oc_transition_capture != NULL)
 		{
-			Assert(mtstate->mt_is_tupconv_perpart);
 			mtstate->mt_oc_transition_capture->tcs_map =
-				mtstate->mt_childparent_tupconv_maps[leaf_part_index];
+				TupConvMapForLeaf(proute, getASTriggerResultRelInfo(mtstate),
+								  leaf_part_index);
 		}
 
 		/*
 		 * We might need to convert from the parent rowtype to the partition
 		 * rowtype.
 		 */
-		tuple = ConvertPartitionTupleSlot(proute->partition_tupconv_maps[leaf_part_index],
+		tuple = ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[leaf_part_index],
 										  tuple,
 										  proute->partition_tuple_slot,
 										  &slot);
@@ -1682,8 +1685,7 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
 	if (mtstate->mt_transition_capture != NULL ||
 		mtstate->mt_oc_transition_capture != NULL)
 	{
-		ExecSetupChildParentMap(mtstate,
-								(mtstate->mt_partition_tuple_routing != NULL));
+		ExecSetupChildParentMapForTcs(mtstate);
 
 		/*
 		 * Install the conversion map for the first plan for UPDATE and DELETE
@@ -1698,55 +1700,32 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
 }
 
 /*
- * Initialize the child-to-root tuple conversion map array.
+ * Initialize the child-to-root tuple conversion map array for UPDATE subplans.
  *
  * This map array is required for two purposes:
  * 1. For update-tuple-routing. We need to convert the tuple from the subplan
  *    result rel to the root partitioned table descriptor.
- * 2. For capturing transition tuples when the target table is a partitioned
- *    table. For updates, we need to convert the tuple from the subplan result
- *    rel to the target table descriptor, and for inserts, we need to convert
- *    the inserted tuple from the leaf partition to the target table
- *    descriptor.
- *
- * The caller can request either a per-subplan map or per-leaf-partition map.
+ * 2. For capturing transition tuples For updates, we need to convert the tuple
+ *    from the subplan result rel to the target table descriptor.
  */
-static void
-ExecSetupChildParentMap(ModifyTableState *mtstate, bool perleaf)
+void
+ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
 {
 	ResultRelInfo *rootRelInfo = getASTriggerResultRelInfo(mtstate);
-	PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
+	ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
 	TupleDesc	outdesc;
-	int			numResultRelInfos;
+	int			numResultRelInfos = mtstate->mt_nplans;
 	int			i;
 
-	/* First check if there is already one */
-	if (mtstate->mt_childparent_tupconv_maps)
-	{
-		/*
-		 * If per-leaf map is required and the map is already created, that map
-		 * 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
-		 * 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
-		 * called with perleaf=true so that the map created is per-leaf, not
-		 * per-subplan.
-		 */
-		Assert(!(perleaf && !mtstate->mt_is_tupconv_perpart));
+	/*
+	 * First check if there is already one. Even if there is already a per-leaf
+	 * map, we won't require a per-subplan one, since we will use the subplan
+	 * offset array to convert the subplan index to per-leaf index.
+	 */
+	if (mtstate->mt_per_subplan_tupconv_maps ||
+		(mtstate->mt_partition_tuple_routing &&
+		mtstate->mt_partition_tuple_routing->child_parent_tupconv_maps))
 		return;
-	}
-
-	/* If perleaf is true, partition tuple routing info has to be present */
-	Assert(!perleaf || proute != NULL);
-
-	numResultRelInfos = (perleaf ? proute->num_partitions :
-								   mtstate->mt_nplans);
-
-	/* Get tuple descriptor of the root partitioned table. */
-	outdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
 
 	/*
 	 * Build array of conversion maps from each child's TupleDesc to the
@@ -1754,48 +1733,55 @@ ExecSetupChildParentMap(ModifyTableState *mtstate, bool perleaf)
 	 * conversion is necessary, which is hopefully a common case for
 	 * partitions.
 	 */
-	mtstate->mt_childparent_tupconv_maps = (TupleConversionMap **)
+
+	/* Get tuple descriptor of the root partitioned table. */
+	outdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
+
+	mtstate->mt_per_subplan_tupconv_maps =
 		palloc(sizeof(TupleConversionMap *) * numResultRelInfos);
 
-	/* Choose the right set of partitions */
-	if (perleaf)
+	for (i = 0; i < numResultRelInfos; ++i)
 	{
-		/*
-		 * For tuple routing among partitions, we need TupleDescs based on the
-		 * partition routing table.
-		 */
-		ResultRelInfo **resultRelInfos;
-
-		resultRelInfos = proute->partitions;
+		mtstate->mt_per_subplan_tupconv_maps[i] =
+			convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
+								   outdesc,
+								   gettext_noop("could not convert row type"));
+	}
+}
 
-		for (i = 0; i < numResultRelInfos; ++i)
-		{
-			mtstate->mt_childparent_tupconv_maps[i] =
-				convert_tuples_by_name(RelationGetDescr(resultRelInfos[i]->ri_RelationDesc),
-									   outdesc,
-									   gettext_noop("could not convert row type"));
-		}
+/*
+ * Initialize the child-to-root tuple conversion map array required for
+ * capturing transition tuples.
+ *
+ * For updates, a per-subplan map is required, and additionally a
+ * per-leaf-partition map is required when tuples are routed in case of updates
+ * or inserts.
+ */
+static void
+ExecSetupChildParentMapForTcs(ModifyTableState *mtstate)
+{
+	PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
 
+	/*
+	 * For transition tables, we need a subplan-indexed access, and where
+	 * tuple-routing is present, we also require a leaf-indexed access.
+	 */
+	if (proute)
+	{
 		/*
-		 * Save the info that the tuple conversion map is per-leaf, not
-		 * per-subplan
+		 * If per-leaf map is to be created, the subplan map has to be NULL.
+		 * If the subplan map is already created, we won't be able to access
+		 * the map leaf-partition-wise.  But if the map is per-leaf, we will be
+		 * able to access the maps subplan-wise using the
+		 * subplan_partition_offsets map using function
+		 * tupconv_map_for_subplan().
 		 */
-		mtstate->mt_is_tupconv_perpart = true;
-	}
-	else
-	{
-		/* Otherwise we need the ResultRelInfo for each subplan. */
-		ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
+		Assert(mtstate->mt_per_subplan_tupconv_maps == NULL);
 
-		for (i = 0; i < numResultRelInfos; ++i)
-		{
-			mtstate->mt_childparent_tupconv_maps[i] =
-				convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
-									   outdesc,
-									   gettext_noop("could not convert row type"));
-		}
+		ExecSetupChildParentMapForLeaf(proute);
 	}
-
+	else
+		ExecSetupChildParentMapForSubplan(mtstate);
 }
 
 /*
@@ -1804,13 +1790,11 @@ ExecSetupChildParentMap(ModifyTableState *mtstate, bool perleaf)
 static inline TupleConversionMap *
 tupconv_map_for_subplan(ModifyTableState *mtstate, int whichplan)
 {
-	Assert(mtstate->mt_childparent_tupconv_maps != NULL);
-
 	/*
 	 * If the tuple conversion map array is per-partition, we need to first get
 	 * the index into the partition array.
 	 */
-	if (mtstate->mt_is_tupconv_perpart)
+	if (mtstate->mt_per_subplan_tupconv_maps == NULL)
 	{
 		int leaf_index;
 		PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
@@ -1818,13 +1802,13 @@ tupconv_map_for_subplan(ModifyTableState *mtstate, int whichplan)
 		Assert(proute && proute->subplan_partition_offsets != NULL);
 		leaf_index = proute->subplan_partition_offsets[whichplan];
 
-		Assert(leaf_index >= 0 && leaf_index < proute->num_partitions);
-		return mtstate->mt_childparent_tupconv_maps[leaf_index];
+		return TupConvMapForLeaf(proute, getASTriggerResultRelInfo(mtstate),
+								 leaf_index);
 	}
 	else
 	{
 		Assert(whichplan >= 0 && whichplan < mtstate->mt_nplans);
-		return mtstate->mt_childparent_tupconv_maps[whichplan];
+		return mtstate->mt_per_subplan_tupconv_maps[whichplan];
 	}
 }
 
@@ -2270,7 +2254,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 * from the root.  Skip this setup if it's not a partition key update.
 	 */
 	if (update_tuple_routing_needed)
-		ExecSetupChildParentMap(mtstate, false);
+		ExecSetupChildParentMapForSubplan(mtstate);
 
 	/*
 	 * Initialize any WITH CHECK OPTION constraints if needed.
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 0afa41e..06e6edd 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -62,11 +62,22 @@ typedef struct PartitionDispatchData *PartitionDispatch;
  *								for every leaf partition in the partition tree.
  * num_partitions				Number of leaf partitions in the partition tree
  *								(= 'partitions' array length)
- * partition_tupconv_maps		Array of TupleConversionMap objects with one
+ * parent_child_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)
+ * child_parent_tupconv_maps	Array of TupleConversionMap objects with one
+ *								entry for every leaf partition (required to
+ *								convert input tuple based on the leaf
+ *								partition's rowtype to the root table's rowtype
+ *								after tuple routing is done)
+ * child_parent_tupconv_map_not_reqd
+ *								Array of bool. True value means that a map is
+ *								determined to be not required for the given
+ *								partition. False means either we haven't yet
+ *								checked if a map is required, or it was
+ *								determined to be required.
  * subplan_partition_offsets	int array ordered by UPDATE subplans. Each
  *								element of this array has the index into the
  *								corresponding partition in 'partitions' array.
@@ -82,7 +93,9 @@ typedef struct PartitionTupleRouting
 	int			num_dispatch;
 	ResultRelInfo **partitions;
 	int			num_partitions;
-	TupleConversionMap **partition_tupconv_maps;
+	TupleConversionMap **parent_child_tupconv_maps;
+	TupleConversionMap **child_parent_tupconv_maps;
+	bool	   *child_parent_tupconv_map_not_reqd;
 	int		   *subplan_partition_offsets;
 	TupleTableSlot *partition_tuple_slot;
 	TupleTableSlot *root_tuple_slot;
@@ -95,6 +108,9 @@ extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
 				  PartitionDispatch *pd,
 				  TupleTableSlot *slot,
 				  EState *estate);
+extern void ExecSetupChildParentMapForLeaf(PartitionTupleRouting *proute);
+extern TupleConversionMap *TupConvMapForLeaf(PartitionTupleRouting *proute,
+					 ResultRelInfo *rootRelInfo, int leaf_index);
 extern HeapTuple ConvertPartitionTupleSlot(TupleConversionMap *map,
 						  HeapTuple tuple,
 						  TupleTableSlot *new_slot,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 8b5391d..defd5cd 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -991,9 +991,8 @@ typedef struct ModifyTableState
 	/* controls transition table population for specified operation */
 	struct TransitionCaptureState *mt_oc_transition_capture;
 	/* controls transition table population for INSERT...ON CONFLICT UPDATE */
-	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 ? */
+	TupleConversionMap **mt_per_subplan_tupconv_maps;
+	/* Per plan map for tuple conversion from child to root */
 } ModifyTableState;
 
 /* ----------------
