commit 501cc0544c0317fa2718793f3e2d0aa35e7f936f
Author: Amit Khandekar <amit.khandekar@enterprisedb.com>
Date:   Thu Aug 16 15:08:52 2018 +0530

    Slotification of partition tuples.
    
    ConvertPartitionTupleSlot() and (in some cases) do_convert_tuple() both
    deal with tuples of partitions. At all such places, avoid fetching of
    tuples, and instead do the conversion rather using slot directly.
    
    There are other places where do_convert_tuple() deals with
    non-partition tuples. This will be worked on separately.

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 276a262..9084e9c 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,11 +2870,21 @@ 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. Also, we cannot call
+				 * ExecMaterializeSlot(), otherwise the tuple will get freed
+				 * while storing the next tuple.
+				 */
+				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 cd43af1..1742cd4 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;
 
@@ -1937,10 +1936,8 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo,
 		map = convert_tuples_by_name(old_tupdesc, tupdesc,
 									 gettext_noop("could not convert row type"));
 		/* Input slot might be of a partition, which has a fixed tupdesc. */
-		slot = MakeTupleTableSlot(tupdesc);
 		if (map != NULL)
-			tuple = do_convert_tuple(tuple, map);
-		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+			slot = ConvertTupleSlot(map, slot, MakeTupleTableSlot(tupdesc));
 	}
 
 	insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2006,7 +2003,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				 */
 				if (resultRelInfo->ri_PartitionRoot)
 				{
-					HeapTuple	tuple = ExecFetchSlotTuple(slot);
 					TupleConversionMap *map;
 
 					rel = resultRelInfo->ri_PartitionRoot;
@@ -2018,10 +2014,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 					 * Input slot might be partition-specific, which has a
 					 * fixed tupdesc.
 					 */
-					slot = MakeTupleTableSlot(tupdesc);
 					if (map != NULL)
-						tuple = do_convert_tuple(tuple, map);
-					ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+						slot = ConvertTupleSlot(map, slot,
+												MakeTupleTableSlot(tupdesc));
 				}
 
 				insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2055,7 +2050,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 			/* See the comment above. */
 			if (resultRelInfo->ri_PartitionRoot)
 			{
-				HeapTuple	tuple = ExecFetchSlotTuple(slot);
 				TupleDesc	old_tupdesc = RelationGetDescr(rel);
 				TupleConversionMap *map;
 
@@ -2068,10 +2062,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
 				 * Input slot might be partition-specific, which has a
 				 * fixed tupdesc.
 				 */
-				slot = MakeTupleTableSlot(tupdesc);
 				if (map != NULL)
-					tuple = do_convert_tuple(tuple, map);
-				ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+					slot = ConvertTupleSlot(map, slot,
+											MakeTupleTableSlot(tupdesc));
 			}
 
 			insertedCols = GetInsertedColumns(resultRelInfo, estate);
@@ -2163,7 +2156,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;
 
@@ -2176,10 +2168,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 						 * Input slot might be partition-specific, which has a
 						 * fixed tupdesc.
 						 */
-						slot = MakeTupleTableSlot(tupdesc);
 						if (map != NULL)
-							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 03779db..a57277f 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);
-			}
 		}
 	}
 
@@ -805,36 +789,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 9b4cd36..65cd53a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1162,13 +1162,9 @@ 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
 			 * into the root.
@@ -1801,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 72623d4..8469e2a 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -222,11 +222,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,
