diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c
index 3bc67b8..fbcfa85 100644
--- a/src/backend/access/common/tupconvert.c
+++ b/src/backend/access/common/tupconvert.c
@@ -22,6 +22,7 @@
 
 #include "access/htup_details.h"
 #include "access/tupconvert.h"
+#include "executor/tuptable.h"
 #include "utils/builtins.h"
 
 
@@ -406,6 +407,60 @@ do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
 }
 
 /*
+ * Perform conversion of a tuple slot according to the map.
+ */
+TupleTableSlot *
+ConvertTupleSlot(TupleConversionMap *map,
+				 TupleTableSlot *in_slot, TupleTableSlot *out_slot)
+{
+	AttrNumber *attrMap = map->attrMap;
+	Datum	   *invalues;
+	bool	   *inisnull;
+	Datum	   *outvalues;
+	bool	   *outisnull;
+	int			outnatts = map->outdesc->natts;
+	int			i;
+
+	/* Sanity checks */
+	Assert(in_slot->tts_tupleDescriptor != NULL &&
+		   out_slot->tts_tupleDescriptor != NULL);
+	Assert(in_slot->tts_values != NULL && out_slot->tts_values != NULL);
+
+	/* Extract all the values of the in slot. */
+	slot_getallattrs(in_slot);
+
+	/* Before doing the mapping, clear any old contents from the out slot */
+	ExecClearTuple(out_slot);
+
+	invalues = in_slot->tts_values;
+	inisnull = in_slot->tts_isnull;
+	outvalues = out_slot->tts_values;
+	outisnull = out_slot->tts_isnull;
+
+	/* Transpose into proper fields of the out slot. */
+	for (i = 0; i < outnatts; i++)
+	{
+		int			j = attrMap[i] - 1;
+
+		/* attrMap[i] == 0 means it's a NULL datum. */
+		if (j == -1)
+		{
+			outvalues[i] = (Datum) 0;
+			outisnull[i] = true;
+		}
+		else
+		{
+			outvalues[i] = invalues[j];
+			outisnull[i] = inisnull[j];
+		}
+	}
+
+	ExecStoreVirtualTuple(out_slot);
+
+	return out_slot;
+}
+
+/*
  * Free a TupleConversionMap structure.
  */
 void
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f61db3e..e5e8e20 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -31,6 +31,7 @@
 #include "commands/trigger.h"
 #include "executor/execPartition.h"
 #include "executor/executor.h"
+#include "executor/tuptable.h"
 #include "foreign/fdwapi.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -2869,12 +2870,20 @@ CopyFrom(CopyState cstate)
 			if (map != NULL)
 			{
 				TupleTableSlot *new_slot;
+				MemoryContext oldcontext;
 
 				Assert(proute->partition_tuple_slots != NULL &&
 					   proute->partition_tuple_slots[leaf_part_index] != NULL);
 				new_slot = proute->partition_tuple_slots[leaf_part_index];
-				tuple = ConvertPartitionTupleSlot(map, tuple, new_slot, &slot,
-												  false);
+				slot = ConvertTupleSlot(map, slot, new_slot);
+
+				/*
+				 * Get the tuple in the per-tuple context, so that it will be
+				 * freed after each batch insert.
+				 */
+				oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
+				tuple = ExecCopySlotTuple(slot);
+				MemoryContextSwitchTo(oldcontext);
 			}
 
 			tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 415670e..ecdf7f2 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1927,7 +1927,6 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
 	 */
 	if (resultRelInfo->ri_PartitionRoot)
 	{
-		HeapTuple	tuple = ExecFetchSlotTuple(slot);
 		TupleDesc	old_tupdesc = RelationGetDescr(rel);
 		TupleConversionMap *map;
 
@@ -1936,16 +1935,12 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
 		/* a reverse map */
 		map = convert_tuples_by_name(old_tupdesc, tupdesc,
 									 gettext_noop("could not convert row type"));
+		/*
+		 * Partition-specific slot's tupdesc can't be changed, so allocate a
+		 * new one.
+		 */
 		if (map != NULL)
-		{
-			/*
-			 * Partition-specific slot's tupdesc can't be changed, so allocate
-			 * a new one.
-			 */
-			slot = MakeTupleTableSlot(tupdesc);
-			tuple = do_convert_tuple(tuple, map);
-			ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-		}
+			slot = ConvertTupleSlot(map, slot, MakeTupleTableSlot(tupdesc));
 	}
 
 	insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2011,7 +2006,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				 */
 				if (resultRelInfo->ri_PartitionRoot)
 				{
-					HeapTuple	tuple = ExecFetchSlotTuple(slot);
 					TupleConversionMap *map;
 
 					rel = resultRelInfo->ri_PartitionRoot;
@@ -2019,16 +2013,13 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 					/* a reverse map */
 					map = convert_tuples_by_name(orig_tupdesc, tupdesc,
 												 gettext_noop("could not convert row type"));
+					/*
+					 * Partition-specific slot's tupdesc can't be changed, so
+					 * allocate a new one.
+					 */
 					if (map != NULL)
-					{
-						/*
-						 * Partition-specific slot's tupdesc can't be changed,
-						 * so allocate a new one.
-						 */
-						slot = MakeTupleTableSlot(tupdesc);
-						tuple = do_convert_tuple(tuple, map);
-						ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-					}
+						slot = ConvertTupleSlot(map, slot,
+												MakeTupleTableSlot(tupdesc));
 				}
 
 				insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2062,7 +2053,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 			/* See the comment above. */
 			if (resultRelInfo->ri_PartitionRoot)
 			{
-				HeapTuple	tuple = ExecFetchSlotTuple(slot);
 				TupleDesc	old_tupdesc = RelationGetDescr(rel);
 				TupleConversionMap *map;
 
@@ -2071,16 +2061,13 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				/* a reverse map */
 				map = convert_tuples_by_name(old_tupdesc, tupdesc,
 											 gettext_noop("could not convert row type"));
+				/*
+				 * Partition-specific slot's tupdesc can't be changed, so
+				 * allocate a new one.
+				 */
 				if (map != NULL)
-				{
-					/*
-					 * Partition-specific slot's tupdesc can't be changed,
-					 * so allocate a new one.
-					 */
-					slot = MakeTupleTableSlot(tupdesc);
-					tuple = do_convert_tuple(tuple, map);
-					ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-				}
+					slot = ConvertTupleSlot(map, slot,
+											MakeTupleTableSlot(tupdesc));
 			}
 
 			insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2172,7 +2159,6 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 					/* See the comment in ExecConstraints(). */
 					if (resultRelInfo->ri_PartitionRoot)
 					{
-						HeapTuple	tuple = ExecFetchSlotTuple(slot);
 						TupleDesc	old_tupdesc = RelationGetDescr(rel);
 						TupleConversionMap *map;
 
@@ -2181,16 +2167,13 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 						/* a reverse map */
 						map = convert_tuples_by_name(old_tupdesc, tupdesc,
 													 gettext_noop("could not convert row type"));
+						/*
+						 * Partition-specific slot's tupdesc can't be changed,
+						 * so allocate a new one.
+						 */
 						if (map != NULL)
-						{
-							/*
-							 * Partition-specific slot's tupdesc can't be
-							 * changed, so allocate a new one.
-							 */
-							slot = MakeTupleTableSlot(tupdesc);
-							tuple = do_convert_tuple(tuple, map);
-							ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-						}
+							slot = ConvertTupleSlot(map, slot,
+													MakeTupleTableSlot(tupdesc));
 					}
 
 					insertedCols = GetInsertedColumns(resultRelInfo, estate);
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 2af7599..6c9b8f9 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -190,7 +190,6 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 	TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple;
 	TupleTableSlot *myslot = NULL;
 	MemoryContext	oldcxt;
-	HeapTuple		tuple;
 
 	/* use per-tuple context here to avoid leaking memory */
 	oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
@@ -203,7 +202,6 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 		ExecPartitionCheck(resultRelInfo, slot, estate, true);
 
 	/* start with the root partitioned table */
-	tuple = ExecFetchSlotTuple(slot);
 	dispatch = pd[0];
 	while (true)
 	{
@@ -218,11 +216,7 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 		 */
 		myslot = dispatch->tupslot;
 		if (myslot != NULL && map != NULL)
-		{
-			tuple = do_convert_tuple(tuple, map);
-			ExecStoreTuple(tuple, myslot, InvalidBuffer, true);
-			slot = myslot;
-		}
+			slot = ConvertTupleSlot(map, slot, myslot);
 
 		/*
 		 * Extract partition key from tuple. Expression evaluation machinery
@@ -267,16 +261,6 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 		{
 			/* move down one level */
 			dispatch = pd[-dispatch->indexes[cur_index]];
-
-			/*
-			 * Release the dedicated slot, if it was used.  Create a copy of
-			 * the tuple first, for the next iteration.
-			 */
-			if (slot == myslot)
-			{
-				tuple = ExecCopySlotTuple(myslot);
-				ExecClearTuple(myslot);
-			}
 		}
 	}
 
@@ -807,36 +791,6 @@ TupConvMapForLeaf(PartitionTupleRouting *proute,
 }
 
 /*
- * ConvertPartitionTupleSlot -- convenience function for tuple conversion.
- * The tuple, if converted, is stored in new_slot, and *p_my_slot is
- * updated to point to it.  new_slot typically should be one of the
- * dedicated partition tuple slots. If map is NULL, *p_my_slot is not changed.
- *
- * Returns the converted tuple, unless map is NULL, in which case original
- * tuple is returned unmodified.
- */
-HeapTuple
-ConvertPartitionTupleSlot(TupleConversionMap *map,
-						  HeapTuple tuple,
-						  TupleTableSlot *new_slot,
-						  TupleTableSlot **p_my_slot,
-						  bool shouldFree)
-{
-	Assert(map != NULL && new_slot != NULL);
-
-	tuple = do_convert_tuple(tuple, map);
-
-	/*
-	 * Change the partition tuple slot descriptor, as per converted tuple.
-	 */
-	*p_my_slot = new_slot;
-	Assert(new_slot != NULL);
-	ExecStoreTuple(tuple, new_slot, InvalidBuffer, shouldFree);
-
-	return tuple;
-}
-
-/*
  * ExecCleanupTupleRouting -- Clean up objects allocated for partition tuple
  * routing.
  *
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e4b2d37..65cd53a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1162,11 +1162,8 @@ lreplace:;
 			Assert(map_index >= 0 && map_index < mtstate->mt_nplans);
 			tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
 			if (tupconv_map != NULL)
-				tuple = ConvertPartitionTupleSlot(tupconv_map,
-												  tuple,
-												  proute->root_tuple_slot,
-												  &slot,
-												  true);
+				slot = ConvertTupleSlot(tupconv_map,
+										slot, proute->root_tuple_slot);
 
 			/*
 			 * Prepare for tuple routing, making it look like we're inserting
@@ -1800,7 +1797,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
 		Assert(proute->partition_tuple_slots != NULL &&
 			   proute->partition_tuple_slots[partidx] != NULL);
 		new_slot = proute->partition_tuple_slots[partidx];
-		ConvertPartitionTupleSlot(map, tuple, new_slot, &slot, true);
+		slot = ConvertTupleSlot(map, slot, new_slot);
 	}
 
 	/* Initialize information needed to handle ON CONFLICT DO UPDATE. */
diff --git a/src/include/access/tupconvert.h b/src/include/access/tupconvert.h
index 66c0ed0..7a453fe 100644
--- a/src/include/access/tupconvert.h
+++ b/src/include/access/tupconvert.h
@@ -16,6 +16,7 @@
 
 #include "access/htup.h"
 #include "access/tupdesc.h"
+#include "executor/tuptable.h"
 
 
 typedef struct TupleConversionMap
@@ -44,6 +45,9 @@ extern AttrNumber *convert_tuples_by_name_map(TupleDesc indesc,
 
 extern HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map);
 
+extern TupleTableSlot *ConvertTupleSlot(TupleConversionMap *map,
+				 TupleTableSlot *in_slot, TupleTableSlot *out_slot);
+
 extern void free_conversion_map(TupleConversionMap *map);
 
 #endif							/* TUPCONVERT_H */
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index c2a2dc5..13a213e 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -219,11 +219,6 @@ extern void ExecInitRoutingInfo(ModifyTableState *mtstate,
 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,
-						  TupleTableSlot **p_my_slot,
-						  bool shouldFree);
 extern void ExecCleanupTupleRouting(ModifyTableState *mtstate,
 						PartitionTupleRouting *proute);
 extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate,
