From 9000faa6ccb3f5bbbd8944590c82826fa13404e9 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Fri, 25 Aug 2017 14:19:23 -0700
Subject: [PATCH 02/16] WIP: Allow tupleslots to have a fixed tupledesc, use in
 executor nodes.

The reason for doing so is that it will allow expression evaluation to
optimize based on the underlying tupledesc. In particular it allows
JITing tuple deforming together with the expression itself.

For that expression initialization needs to be moved after the
relevant slots are initialized - mostly unproblematic, except in the
case of nodeWorktablescan.c.
---
 src/backend/executor/execMain.c                |  2 +-
 src/backend/executor/execTuples.c              | 50 ++++++++++++-----
 src/backend/executor/execUtils.c               | 45 +--------------
 src/backend/executor/nodeAgg.c                 | 42 +++++++-------
 src/backend/executor/nodeAppend.c              | 17 +++---
 src/backend/executor/nodeBitmapAnd.c           | 14 ++---
 src/backend/executor/nodeBitmapHeapscan.c      | 43 +++++++-------
 src/backend/executor/nodeBitmapIndexscan.c     | 14 ++---
 src/backend/executor/nodeBitmapOr.c            | 14 ++---
 src/backend/executor/nodeCtescan.c             | 20 +++----
 src/backend/executor/nodeCustom.c              | 16 +++---
 src/backend/executor/nodeForeignscan.c         | 24 ++++----
 src/backend/executor/nodeFunctionscan.c        | 26 ++++-----
 src/backend/executor/nodeGather.c              |  3 +-
 src/backend/executor/nodeGatherMerge.c         |  3 +-
 src/backend/executor/nodeGroup.c               | 18 +++---
 src/backend/executor/nodeHash.c                | 13 ++---
 src/backend/executor/nodeHashjoin.c            | 25 ++++-----
 src/backend/executor/nodeIndexonlyscan.c       | 28 +++++-----
 src/backend/executor/nodeIndexscan.c           | 49 ++++++++--------
 src/backend/executor/nodeLimit.c               |  3 +-
 src/backend/executor/nodeLockRows.c            | 15 +++--
 src/backend/executor/nodeMaterial.c            | 20 +++----
 src/backend/executor/nodeMergeAppend.c         |  3 +-
 src/backend/executor/nodeMergejoin.c           | 25 ++++-----
 src/backend/executor/nodeModifyTable.c         |  6 +-
 src/backend/executor/nodeNamedtuplestorescan.c | 18 ++----
 src/backend/executor/nodeNestloop.c            | 21 ++++---
 src/backend/executor/nodeProjectSet.c          |  3 +-
 src/backend/executor/nodeRecursiveunion.c      | 40 ++++++-------
 src/backend/executor/nodeResult.c              | 27 +++++----
 src/backend/executor/nodeSamplescan.c          | 77 ++++++++++----------------
 src/backend/executor/nodeSeqscan.c             | 56 +++++++------------
 src/backend/executor/nodeSetOp.c               |  3 +-
 src/backend/executor/nodeSort.c                | 20 +++----
 src/backend/executor/nodeSubqueryscan.c        | 18 +++---
 src/backend/executor/nodeTableFuncscan.c       | 25 ++++-----
 src/backend/executor/nodeTidscan.c             | 49 ++++++++--------
 src/backend/executor/nodeUnique.c              |  3 +-
 src/backend/executor/nodeValuesscan.c          | 18 +++---
 src/backend/executor/nodeWindowAgg.c           |  6 +-
 src/backend/executor/nodeWorktablescan.c       | 13 ++---
 src/include/executor/executor.h                |  8 +--
 src/include/executor/tuptable.h                |  5 +-
 44 files changed, 422 insertions(+), 526 deletions(-)

diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 2946a0edee..89ef0fc8ee 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -3290,7 +3290,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
 	 * (such as ModifyTableState) and released when the node finishes
 	 * processing.
 	 */
-	*partition_tuple_slot = MakeTupleTableSlot();
+	*partition_tuple_slot = MakeTupleTableSlot(NULL);
 
 	leaf_part_rri = *partitions;
 	i = 0;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 31f814c0f0..8280b89f7f 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -58,7 +58,7 @@
  *		At ExecutorStart()
  *		----------------
  *		- ExecInitSeqScan() calls ExecInitScanTupleSlot() and
- *		  ExecInitResultTupleSlot() to construct TupleTableSlots
+ *		  ExecInitResultTupleSlotTL() to construct TupleTableSlots
  *		  for the tuples returned by the access methods and the
  *		  tuples resulting from performing target list projections.
  *
@@ -108,7 +108,7 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList,
  * --------------------------------
  */
 TupleTableSlot *
-MakeTupleTableSlot(void)
+MakeTupleTableSlot(TupleDesc tupleDesc)
 {
 	TupleTableSlot *slot = makeNode(TupleTableSlot);
 
@@ -116,6 +116,7 @@ MakeTupleTableSlot(void)
 	slot->tts_shouldFree = false;
 	slot->tts_shouldFreeMin = false;
 	slot->tts_tuple = NULL;
+	slot->tts_fixedTupleDescriptor = false;
 	slot->tts_tupleDescriptor = NULL;
 	slot->tts_mcxt = CurrentMemoryContext;
 	slot->tts_buffer = InvalidBuffer;
@@ -124,6 +125,13 @@ MakeTupleTableSlot(void)
 	slot->tts_isnull = NULL;
 	slot->tts_mintuple = NULL;
 
+	/* FIXME: instead allocate everything in one go */
+	if (tupleDesc != NULL)
+	{
+		ExecSetSlotDescriptor(slot, tupleDesc);
+		slot->tts_fixedTupleDescriptor = true;
+	}
+
 	return slot;
 }
 
@@ -134,9 +142,9 @@ MakeTupleTableSlot(void)
  * --------------------------------
  */
 TupleTableSlot *
-ExecAllocTableSlot(List **tupleTable)
+ExecAllocTableSlot(List **tupleTable, TupleDesc desc)
 {
-	TupleTableSlot *slot = MakeTupleTableSlot();
+	TupleTableSlot *slot = MakeTupleTableSlot(desc);
 
 	*tupleTable = lappend(*tupleTable, slot);
 
@@ -198,9 +206,7 @@ ExecResetTupleTable(List *tupleTable,	/* tuple table */
 TupleTableSlot *
 MakeSingleTupleTableSlot(TupleDesc tupdesc)
 {
-	TupleTableSlot *slot = MakeTupleTableSlot();
-
-	ExecSetSlotDescriptor(slot, tupdesc);
+	TupleTableSlot *slot = MakeTupleTableSlot(tupdesc);
 
 	return slot;
 }
@@ -247,6 +253,8 @@ void
 ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
 					  TupleDesc tupdesc)	/* new tuple descriptor */
 {
+	Assert(!slot->tts_fixedTupleDescriptor);
+
 	/* For safety, make sure slot is empty before changing it */
 	ExecClearTuple(slot);
 
@@ -825,13 +833,28 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
  */
 
 /* ----------------
- *		ExecInitResultTupleSlot
+ *		ExecInitResultTupleSlotTL
  * ----------------
  */
 void
-ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
+ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
 {
-	planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
+	bool		hasoid;
+	TupleDesc	tupDesc;
+
+	if (ExecContextForcesOids(planstate, &hasoid))
+	{
+		/* context forces OID choice; hasoid is now set correctly */
+	}
+	else
+	{
+		/* given free choice, don't leave space for OIDs in result tuples */
+		hasoid = false;
+	}
+
+	tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
+
+	planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tupDesc);
 }
 
 /* ----------------
@@ -839,9 +862,10 @@ ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
  * ----------------
  */
 void
-ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc)
 {
-	scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
+	scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
+													 tupledesc);
 }
 
 /* ----------------
@@ -851,7 +875,7 @@ ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
 TupleTableSlot *
 ExecInitExtraTupleSlot(EState *estate)
 {
-	return ExecAllocTableSlot(&estate->es_tupleTable);
+	return ExecAllocTableSlot(&estate->es_tupleTable, NULL);
 }
 
 /* ----------------
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9528393976..5928c38f90 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -425,47 +425,6 @@ ExecAssignExprContext(EState *estate, PlanState *planstate)
 	planstate->ps_ExprContext = CreateExprContext(estate);
 }
 
-/* ----------------
- *		ExecAssignResultType
- * ----------------
- */
-void
-ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc)
-{
-	TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
-
-	ExecSetSlotDescriptor(slot, tupDesc);
-}
-
-/* ----------------
- *		ExecAssignResultTypeFromTL
- * ----------------
- */
-void
-ExecAssignResultTypeFromTL(PlanState *planstate)
-{
-	bool		hasoid;
-	TupleDesc	tupDesc;
-
-	if (ExecContextForcesOids(planstate, &hasoid))
-	{
-		/* context forces OID choice; hasoid is now set correctly */
-	}
-	else
-	{
-		/* given free choice, don't leave space for OIDs in result tuples */
-		hasoid = false;
-	}
-
-	/*
-	 * ExecTypeFromTL needs the parse-time representation of the tlist, not a
-	 * list of ExprStates.  This is good because some plan nodes don't bother
-	 * to set up planstate->targetlist ...
-	 */
-	tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
-	ExecAssignResultType(planstate, tupDesc);
-}
-
 /* ----------------
  *		ExecGetResultType
  * ----------------
@@ -554,7 +513,7 @@ ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc)
  * ----------------
  */
 void
-ExecAssignScanTypeFromOuterPlan(ScanState *scanstate)
+ExecCreateScanSlotForOuterPlan(EState *estate, ScanState *scanstate)
 {
 	PlanState  *outerPlan;
 	TupleDesc	tupDesc;
@@ -562,7 +521,7 @@ ExecAssignScanTypeFromOuterPlan(ScanState *scanstate)
 	outerPlan = outerPlanState(scanstate);
 	tupDesc = ExecGetResultType(outerPlan);
 
-	ExecAssignScanType(scanstate, tupDesc);
+	ExecInitScanTupleSlot(estate, scanstate, tupDesc);
 }
 
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0ae5873868..1783f38f14 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2795,10 +2795,28 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	 *
 	 * For hashtables, we create some additional slots below.
 	 */
-	ExecInitScanTupleSlot(estate, &aggstate->ss);
-	ExecInitResultTupleSlot(estate, &aggstate->ss.ps);
+	ExecInitResultTupleSlotTL(estate, &aggstate->ss.ps);
 	aggstate->sort_slot = ExecInitExtraTupleSlot(estate);
 
+	/*
+	 * Initialize child nodes.
+	 *
+	 * If we are doing a hashed aggregation then the child plan does not need
+	 * to handle REWIND efficiently; see ExecReScanAgg.
+	 */
+	if (node->aggstrategy == AGG_HASHED)
+		eflags &= ~EXEC_FLAG_REWIND;
+	outerPlan = outerPlan(node);
+	outerPlanState(aggstate) = ExecInitNode(outerPlan, estate, eflags);
+
+	/*
+	 * initialize source tuple type.
+	 */
+	ExecCreateScanSlotForOuterPlan(estate, &aggstate->ss);
+	if (node->chain)
+		ExecSetSlotDescriptor(aggstate->sort_slot,
+							  aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
+
 	/*
 	 * initialize child expressions
 	 *
@@ -2814,29 +2832,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.qual =
 		ExecInitQual(node->plan.qual, (PlanState *) aggstate);
 
-	/*
-	 * Initialize child nodes.
-	 *
-	 * If we are doing a hashed aggregation then the child plan does not need
-	 * to handle REWIND efficiently; see ExecReScanAgg.
-	 */
-	if (node->aggstrategy == AGG_HASHED)
-		eflags &= ~EXEC_FLAG_REWIND;
-	outerPlan = outerPlan(node);
-	outerPlanState(aggstate) = ExecInitNode(outerPlan, estate, eflags);
-
-	/*
-	 * initialize source tuple type.
-	 */
-	ExecAssignScanTypeFromOuterPlan(&aggstate->ss);
-	if (node->chain)
-		ExecSetSlotDescriptor(aggstate->sort_slot,
-							  aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&aggstate->ss.ps);
 	ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
 
 	/*
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index bed9bb8713..e67d0c36d3 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -152,18 +152,11 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	appendstate->appendplans = appendplanstates;
 	appendstate->as_nplans = nplans;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * Append plans don't have expression contexts because they never call
-	 * ExecQual or ExecProject.
-	 */
-
 	/*
 	 * append nodes still have Result slots, which hold pointers to tuples, so
 	 * we have to initialize them.
 	 */
-	ExecInitResultTupleSlot(estate, &appendstate->ps);
+	ExecInitResultTupleSlotTL(estate, &appendstate->ps);
 
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
@@ -181,9 +174,15 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	/*
 	 * initialize output tuple type
 	 */
-	ExecAssignResultTypeFromTL(&appendstate->ps);
 	appendstate->ps.ps_ProjInfo = NULL;
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * Append plans don't have expression contexts because they never call
+	 * ExecQual or ExecProject.
+	 */
+
 	/*
 	 * initialize to scan first subplan
 	 */
diff --git a/src/backend/executor/nodeBitmapAnd.c b/src/backend/executor/nodeBitmapAnd.c
index 1c5c312c95..b2b30842c6 100644
--- a/src/backend/executor/nodeBitmapAnd.c
+++ b/src/backend/executor/nodeBitmapAnd.c
@@ -80,13 +80,6 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags)
 	bitmapandstate->bitmapplans = bitmapplanstates;
 	bitmapandstate->nplans = nplans;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * BitmapAnd plans don't have expression contexts because they never call
-	 * ExecQual or ExecProject.  They don't need any tuple slots either.
-	 */
-
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
 	 * results into the array "bitmapplanstates".
@@ -99,6 +92,13 @@ ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags)
 		i++;
 	}
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * BitmapAnd plans don't have expression contexts because they never call
+	 * ExecQual or ExecProject.  They don't need any tuple slots either.
+	 */
+
 	return bitmapandstate;
 }
 
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index f7e55e0b45..021fe0e272 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -824,6 +824,27 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
+	/*
+	 * open the base relation and acquire appropriate lock on it.
+	 */
+	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+
+	/*
+	 * get the scan type from the relation descriptor.
+	 */
+	ExecInitScanTupleSlot(estate, &scanstate->ss,
+						  RelationGetDescr(currentRelation));
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignScanProjectionInfo(&scanstate->ss);
+
 	/*
 	 * initialize child expressions
 	 */
@@ -832,17 +853,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->bitmapqualorig =
 		ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
-	/*
-	 * open the base relation and acquire appropriate lock on it.
-	 */
-	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
-
 	/*
 	 * Determine the maximum for prefetch_target.  If the tablespace has a
 	 * specific IO concurrency set, use that to compute the corresponding
@@ -870,17 +880,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 														 0,
 														 NULL);
 
-	/*
-	 * get the scan type from the relation descriptor.
-	 */
-	ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
-
-	/*
-	 * Initialize result tuple type and projection info.
-	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
-	ExecAssignScanProjectionInfo(&scanstate->ss);
-
 	/*
 	 * initialize child nodes
 	 *
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 6feb70f4ae..1178dc82b2 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -226,13 +226,6 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
 	/* normally we don't make the result bitmap till runtime */
 	indexstate->biss_result = NULL;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * We do not need a standard exprcontext for this node, though we may
-	 * decide below to create a runtime-key exprcontext
-	 */
-
 	/*
 	 * initialize child expressions
 	 *
@@ -248,6 +241,13 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
 	 * the heap relation throughout the execution of the plan tree.
 	 */
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * We do not need a standard exprcontext for this node, though we may
+	 * decide below to create a runtime-key exprcontext
+	 */
+
 	indexstate->ss.ss_currentRelation = NULL;
 	indexstate->ss.ss_currentScanDesc = NULL;
 
diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c
index 66a7a89a8b..73c08e4652 100644
--- a/src/backend/executor/nodeBitmapOr.c
+++ b/src/backend/executor/nodeBitmapOr.c
@@ -81,13 +81,6 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags)
 	bitmaporstate->bitmapplans = bitmapplanstates;
 	bitmaporstate->nplans = nplans;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * BitmapOr plans don't have expression contexts because they never call
-	 * ExecQual or ExecProject.  They don't need any tuple slots either.
-	 */
-
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
 	 * results into the array "bitmapplanstates".
@@ -100,6 +93,13 @@ ExecInitBitmapOr(BitmapOr *node, EState *estate, int eflags)
 		i++;
 	}
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * BitmapOr plans don't have expression contexts because they never call
+	 * ExecQual or ExecProject.  They don't need any tuple slots either.
+	 */
+
 	return bitmaporstate;
 }
 
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 79676ca978..934885dce5 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,31 +242,29 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
-
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
 
 	/*
 	 * The scan tuple type (ie, the rowtype we expect to find in the work
 	 * table) is the same as the result rowtype of the CTE query.
 	 */
-	ExecAssignScanType(&scanstate->ss,
-					   ExecGetResultType(scanstate->cteplanstate));
+	ExecInitScanTupleSlot(estate, &scanstate->ss,
+						  ExecGetResultType(scanstate->cteplanstate));
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+
 	return scanstate;
 }
 
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 07dcabef55..3d9b69e550 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,13 +54,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	/* create expression context for node */
 	ExecAssignExprContext(estate, &css->ss.ps);
 
-	/* initialize child expressions */
-	css->ss.ps.qual =
-		ExecInitQual(cscan->scan.plan.qual, (PlanState *) css);
-
 	/* tuple table initialization */
-	ExecInitScanTupleSlot(estate, &css->ss);
-	ExecInitResultTupleSlot(estate, &css->ss.ps);
+	ExecInitResultTupleSlotTL(estate, &css->ss.ps);
 
 	/*
 	 * open the base relation, if any, and acquire an appropriate lock on it
@@ -81,13 +76,13 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 		TupleDesc	scan_tupdesc;
 
 		scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false);
-		ExecAssignScanType(&css->ss, scan_tupdesc);
+		ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc);
 		/* Node's targetlist will contain Vars with varno = INDEX_VAR */
 		tlistvarno = INDEX_VAR;
 	}
 	else
 	{
-		ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
+		ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel));
 		/* Node's targetlist will contain Vars with varno = scanrelid */
 		tlistvarno = scanrelid;
 	}
@@ -95,9 +90,12 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&css->ss.ps);
 	ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno);
 
+	/* initialize child expressions */
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual, (PlanState *) css);
+
 	/*
 	 * The callback of custom-scan provider applies the final initialization
 	 * of the custom-scan-state node according to its logic.
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 20892d6d5f..fb67a53d1e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -155,19 +155,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals =
-		ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
-
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
 
 	/*
 	 * open the base relation, if any, and acquire an appropriate lock on it;
@@ -194,13 +185,13 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 		TupleDesc	scan_tupdesc;
 
 		scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
-		ExecAssignScanType(&scanstate->ss, scan_tupdesc);
+		ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc);
 		/* Node's targetlist will contain Vars with varno = INDEX_VAR */
 		tlistvarno = INDEX_VAR;
 	}
 	else
 	{
-		ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+		ExecInitScanTupleSlot(estate, &scanstate->ss, RelationGetDescr(currentRelation));
 		/* Node's targetlist will contain Vars with varno = scanrelid */
 		tlistvarno = scanrelid;
 	}
@@ -208,9 +199,16 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
 
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
+
 	/*
 	 * Initialize FDW-related state.
 	 */
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 9f87a7e5cd..8d834820c1 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -334,18 +334,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
-
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
 	natts = 0;
@@ -491,14 +479,24 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 		Assert(attno == natts);
 	}
 
-	ExecAssignScanType(&scanstate->ss, scan_tupdesc);
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc);
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+
+
 	/*
 	 * Create a memory context that ExecMakeTableFunctionResult can use to
 	 * evaluate function arguments in.  We can't use the per-tuple context for
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index d93fbacdf9..7656aaaee2 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -93,7 +93,7 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	 * tuple table initialization
 	 */
 	gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate);
-	ExecInitResultTupleSlot(estate, &gatherstate->ps);
+	ExecInitResultTupleSlotTL(estate, &gatherstate->ps);
 
 	/*
 	 * now initialize outer plan
@@ -104,7 +104,6 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&gatherstate->ps);
 	ExecAssignProjectionInfo(&gatherstate->ps, NULL);
 
 	/*
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index b8bb4f8eb0..b940fb3cb5 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -106,7 +106,7 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &gm_state->ps);
+	ExecInitResultTupleSlotTL(estate, &gm_state->ps);
 
 	/*
 	 * now initialize outer plan
@@ -117,7 +117,6 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&gm_state->ps);
 	ExecAssignProjectionInfo(&gm_state->ps, NULL);
 
 	/*
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index ab4ae24a6b..acd679fa14 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -187,14 +187,7 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitScanTupleSlot(estate, &grpstate->ss);
-	ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
-
-	/*
-	 * initialize child expressions
-	 */
-	grpstate->ss.ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) grpstate);
+	ExecInitResultTupleSlotTL(estate, &grpstate->ss.ps);
 
 	/*
 	 * initialize child nodes
@@ -204,14 +197,19 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	/*
 	 * initialize tuple type.
 	 */
-	ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
+	ExecCreateScanSlotForOuterPlan(estate, &grpstate->ss);
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&grpstate->ss.ps);
 	ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
 
+	/*
+	 * initialize child expressions
+	 */
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) grpstate);
+
 	/*
 	 * Precompute fmgr lookup data for inner loop
 	 */
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index d10d94ccc2..c551f319ac 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -186,7 +186,12 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	/*
 	 * initialize our result slot
 	 */
-	ExecInitResultTupleSlot(estate, &hashstate->ps);
+	ExecInitResultTupleSlotTL(estate, &hashstate->ps);
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlanState(hashstate) = ExecInitNode(outerPlan(node), estate, eflags);
 
 	/*
 	 * initialize child expressions
@@ -194,16 +199,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.qual =
 		ExecInitQual(node->plan.qual, (PlanState *) hashstate);
 
-	/*
-	 * initialize child nodes
-	 */
-	outerPlanState(hashstate) = ExecInitNode(outerPlan(node), estate, eflags);
-
 	/*
 	 * initialize tuple type. no need to initialize projection info because
 	 * this node doesn't do projections
 	 */
-	ExecAssignResultTypeFromTL(&hashstate->ps);
 	hashstate->ps.ps_ProjInfo = NULL;
 
 	return hashstate;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index ab1632cc13..f35538924a 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -401,6 +401,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.plan = (Plan *) node;
 	hjstate->js.ps.state = estate;
 	hjstate->js.ps.ExecProcNode = ExecHashJoin;
+	hjstate->js.jointype = node->join.jointype;
 
 	/*
 	 * Miscellaneous initialization
@@ -409,17 +410,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &hjstate->js.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	hjstate->js.ps.qual =
-		ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
-	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual =
-		ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
-	hjstate->hashclauses =
-		ExecInitQual(node->hashclauses, (PlanState *) hjstate);
-
 	/*
 	 * initialize child nodes
 	 *
@@ -436,7 +426,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &hjstate->js.ps);
+	ExecInitResultTupleSlotTL(estate, &hjstate->js.ps);
 	hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
 
 	/*
@@ -492,12 +482,21 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL(&hjstate->js.ps);
 	ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
 
 	ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
 						  ExecGetResultType(outerPlanState(hjstate)));
 
+	/*
+	 * initialize child expressions
+	 */
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses, (PlanState *) hjstate);
+
 	/*
 	 * initialize hash-specific info
 	 */
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 5351cb8981..479d6b3e0a 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -474,22 +474,10 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &indexstate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 *
-	 * Note: we don't initialize all of the indexorderby expression, only the
-	 * sub-parts corresponding to runtime keys (see below).
-	 */
-	indexstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
-	indexstate->indexqual =
-		ExecInitQual(node->indexqual, (PlanState *) indexstate);
-
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &indexstate->ss);
+	ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps);
 
 	/*
 	 * open the base relation and acquire appropriate lock on it.
@@ -507,16 +495,26 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	 * suitable data anyway.)
 	 */
 	tupDesc = ExecTypeFromTL(node->indextlist, false);
-	ExecAssignScanType(&indexstate->ss, tupDesc);
+	ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc);
 
 	/*
 	 * Initialize result tuple type and projection info.  The node's
 	 * targetlist will contain Vars with varno = INDEX_VAR, referencing the
 	 * scan tuple.
 	 */
-	ExecAssignResultTypeFromTL(&indexstate->ss.ps);
 	ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
 
+	/*
+	 * initialize child expressions
+	 *
+	 * Note: we don't initialize all of the indexorderby expression, only the
+	 * sub-parts corresponding to runtime keys (see below).
+	 */
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual, (PlanState *) indexstate);
+
 	/*
 	 * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
 	 * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 638b17b07c..998a39418c 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -900,6 +900,30 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &indexstate->ss.ps);
 
+	/*
+	 * open the base relation and acquire appropriate lock on it.
+	 */
+	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
+
+	indexstate->ss.ss_currentRelation = currentRelation;
+	indexstate->ss.ss_currentScanDesc = NULL;	/* no heap scan here */
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps);
+
+	/*
+	 * get the scan type from the relation descriptor.
+	 */
+	ExecInitScanTupleSlot(estate, &indexstate->ss,
+						  RelationGetDescr(currentRelation));
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignScanProjectionInfo(&indexstate->ss);
+
 	/*
 	 * initialize child expressions
 	 *
@@ -917,31 +941,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->indexorderbyorig =
 		ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &indexstate->ss);
-
-	/*
-	 * open the base relation and acquire appropriate lock on it.
-	 */
-	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
-
-	indexstate->ss.ss_currentRelation = currentRelation;
-	indexstate->ss.ss_currentScanDesc = NULL;	/* no heap scan here */
-
-	/*
-	 * get the scan type from the relation descriptor.
-	 */
-	ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation));
-
-	/*
-	 * Initialize result tuple type and projection info.
-	 */
-	ExecAssignResultTypeFromTL(&indexstate->ss.ps);
-	ExecAssignScanProjectionInfo(&indexstate->ss);
-
 	/*
 	 * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
 	 * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 883f46ce7c..0eed8f74b1 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -364,7 +364,7 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
 	/*
 	 * Tuple table initialization (XXX not actually used...)
 	 */
-	ExecInitResultTupleSlot(estate, &limitstate->ps);
+	ExecInitResultTupleSlotTL(estate, &limitstate->ps);
 
 	/*
 	 * then initialize outer plan
@@ -376,7 +376,6 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
 	 * limit nodes do no projections, so initialize projection info for this
 	 * node appropriately
 	 */
-	ExecAssignResultTypeFromTL(&limitstate->ps);
 	limitstate->ps.ps_ProjInfo = NULL;
 
 	return limitstate;
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 93895600a5..9dfb2f4524 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -367,16 +367,10 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
 	lrstate->ps.state = estate;
 	lrstate->ps.ExecProcNode = ExecLockRows;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * LockRows nodes never call ExecQual or ExecProject.
-	 */
-
 	/*
 	 * Tuple table initialization (XXX not actually used...)
 	 */
-	ExecInitResultTupleSlot(estate, &lrstate->ps);
+	ExecInitResultTupleSlotTL(estate, &lrstate->ps);
 
 	/*
 	 * then initialize outer plan
@@ -387,9 +381,14 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
 	 * LockRows nodes do no projections, so initialize projection info for
 	 * this node appropriately
 	 */
-	ExecAssignResultTypeFromTL(&lrstate->ps);
 	lrstate->ps.ps_ProjInfo = NULL;
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * LockRows nodes never call ExecQual or ExecProject.
+	 */
+
 	/*
 	 * Create workspace in which we can remember per-RTE locked tuples
 	 */
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 91178f1019..470d3ed717 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -199,20 +199,12 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
 	matstate->eof_underlying = false;
 	matstate->tuplestorestate = NULL;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * Materialization nodes don't need ExprContexts because they never call
-	 * ExecQual or ExecProject.
-	 */
-
 	/*
 	 * tuple table initialization
 	 *
 	 * material nodes only return tuples from their materialized relation.
 	 */
-	ExecInitResultTupleSlot(estate, &matstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &matstate->ss);
+	ExecInitResultTupleSlotTL(estate, &matstate->ss.ps);
 
 	/*
 	 * initialize child nodes
@@ -229,10 +221,16 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
 	 * initialize tuple type.  no need to initialize projection info because
 	 * this node doesn't do projections.
 	 */
-	ExecAssignResultTypeFromTL(&matstate->ss.ps);
-	ExecAssignScanTypeFromOuterPlan(&matstate->ss);
+	ExecCreateScanSlotForOuterPlan(estate, &matstate->ss);
 	matstate->ss.ps.ps_ProjInfo = NULL;
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * Materialization nodes don't need ExprContexts because they never call
+	 * ExecQual or ExecProject.
+	 */
+
 	return matstate;
 }
 
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 6bf490bd70..f50464fd7d 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -109,7 +109,7 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 	 * MergeAppend nodes do have Result slots, which hold pointers to tuples,
 	 * so we have to initialize them.
 	 */
-	ExecInitResultTupleSlot(estate, &mergestate->ps);
+	ExecInitResultTupleSlotTL(estate, &mergestate->ps);
 
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
@@ -127,7 +127,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 	/*
 	 * initialize output tuple type
 	 */
-	ExecAssignResultTypeFromTL(&mergestate->ps);
 	mergestate->ps.ps_ProjInfo = NULL;
 
 	/*
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 925b4cf553..b11af73aaa 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1450,6 +1450,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.plan = (Plan *) node;
 	mergestate->js.ps.state = estate;
 	mergestate->js.ps.ExecProcNode = ExecMergeJoin;
+	mergestate->js.jointype = node->join.jointype;
+	mergestate->mj_ConstFalseJoin = false;
 
 	/*
 	 * Miscellaneous initialization
@@ -1466,17 +1468,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->mj_OuterEContext = CreateExprContext(estate);
 	mergestate->mj_InnerEContext = CreateExprContext(estate);
 
-	/*
-	 * initialize child expressions
-	 */
-	mergestate->js.ps.qual =
-		ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
-	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual =
-		ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
-	mergestate->mj_ConstFalseJoin = false;
-	/* mergeclauses are handled below */
-
 	/*
 	 * initialize child nodes
 	 *
@@ -1513,12 +1504,21 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &mergestate->js.ps);
+	ExecInitResultTupleSlotTL(estate, &mergestate->js.ps);
 
 	mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
 	ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
 						  ExecGetResultType(innerPlanState(mergestate)));
 
+	/*
+	 * initialize child expressions
+	 */
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
+	/* mergeclauses are handled below */
+
 	/*
 	 * detect whether we need only consider the first matching inner tuple
 	 */
@@ -1586,7 +1586,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL(&mergestate->js.ps);
 	ExecAssignProjectionInfo(&mergestate->js.ps, NULL);
 
 	/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 57946e1591..1e76ef7dd4 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2029,8 +2029,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
 
 		/* Set up a slot for the output of the RETURNING projection(s) */
-		ExecInitResultTupleSlot(estate, &mtstate->ps);
-		ExecAssignResultTypeFromTL(&mtstate->ps);
+		ExecInitResultTupleSlotTL(estate, &mtstate->ps);
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
@@ -2083,8 +2082,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		 * expects one (maybe should change that?).
 		 */
 		mtstate->ps.plan->targetlist = NIL;
-		ExecInitResultTupleSlot(estate, &mtstate->ps);
-		ExecAssignResultTypeFromTL(&mtstate->ps);
+		ExecInitResultTupleSlotTL(estate, &mtstate->ps);
 
 		mtstate->ps.ps_ExprContext = NULL;
 	}
diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c
index 3a65b9f5dc..28036d5076 100644
--- a/src/backend/executor/nodeNamedtuplestorescan.c
+++ b/src/backend/executor/nodeNamedtuplestorescan.c
@@ -132,27 +132,21 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss, scanstate->tupdesc);
+
 	/*
 	 * initialize child expressions
 	 */
 	scanstate->ss.ps.qual =
 		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
-	/*
-	 * The scan tuple type is specified for the tuplestore.
-	 */
-	ExecAssignScanType(&scanstate->ss, scanstate->tupdesc);
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
 	return scanstate;
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 4447b7c051..32c1544f4f 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -285,15 +285,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &nlstate->js.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	nlstate->js.ps.qual =
-		ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
-	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual =
-		ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
-
 	/*
 	 * initialize child nodes
 	 *
@@ -313,7 +304,16 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &nlstate->js.ps);
+	ExecInitResultTupleSlotTL(estate, &nlstate->js.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
+	nlstate->js.jointype = node->join.jointype;
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
 
 	/*
 	 * detect whether we need only consider the first matching inner tuple
@@ -341,7 +341,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL(&nlstate->js.ps);
 	ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
 
 	/*
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index d93462c542..c55a5fded0 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -230,7 +230,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &state->ps);
+	ExecInitResultTupleSlotTL(estate, &state->ps);
 
 	/* We don't support any qual on ProjectSet nodes */
 	Assert(node->plan.qual == NIL);
@@ -248,7 +248,6 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL(&state->ps);
 
 	/* Create workspace for per-tlist-entry expr state & SRF-is-done state */
 	state->nelems = list_length(node->plan.targetlist);
diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index a64dd1397a..3c1f9ac7c6 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -214,6 +214,26 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
 	prmdata->value = PointerGetDatum(rustate);
 	prmdata->isnull = false;
 
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
+	innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
+
+	/*
+	 * RecursiveUnion nodes still have Result slots, which hold pointers to
+	 * tuples, so we have to initialize them.
+	 */
+	ExecInitResultTupleSlotTL(estate, &rustate->ps);
+
+	/*
+	 * Initialize result tuple type and projection info.  (Note: we have to
+	 * set up the result type before initializing child nodes, because
+	 * nodeWorktablescan.c expects it to be valid.)
+	 */
+	rustate->ps.ps_ProjInfo = NULL;
+
 	/*
 	 * Miscellaneous initialization
 	 *
@@ -222,26 +242,6 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
 	 */
 	Assert(node->plan.qual == NIL);
 
-	/*
-	 * RecursiveUnion nodes still have Result slots, which hold pointers to
-	 * tuples, so we have to initialize them.
-	 */
-	ExecInitResultTupleSlot(estate, &rustate->ps);
-
-	/*
-	 * Initialize result tuple type and projection info.  (Note: we have to
-	 * set up the result type before initializing child nodes, because
-	 * nodeWorktablescan.c expects it to be valid.)
-	 */
-	ExecAssignResultTypeFromTL(&rustate->ps);
-	rustate->ps.ps_ProjInfo = NULL;
-
-	/*
-	 * initialize child nodes
-	 */
-	outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
-	innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
-
 	/*
 	 * If hashing, precompute fmgr lookup data for inner loop, and create the
 	 * hash table.
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4c879d8765..7767c7426f 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -204,19 +204,6 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &resstate->ps);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &resstate->ps);
-
-	/*
-	 * initialize child expressions
-	 */
-	resstate->ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) resstate);
-	resstate->resconstantqual =
-		ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
-
 	/*
 	 * initialize child nodes
 	 */
@@ -227,12 +214,24 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	 */
 	Assert(innerPlan(node) == NULL);
 
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &resstate->ps);
+
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL(&resstate->ps);
 	ExecAssignProjectionInfo(&resstate->ps, NULL);
 
+	/*
+	 * initialize child expressions
+	 */
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) resstate);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
+
 	return resstate;
 }
 
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9c74a836e4..89f2a78d14 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -26,7 +26,6 @@
 #include "utils/rel.h"
 #include "utils/tqual.h"
 
-static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
 static TupleTableSlot *SampleNext(SampleScanState *node);
 static void tablesample_init(SampleScanState *scanstate);
 static HeapTuple tablesample_getnext(SampleScanState *scanstate);
@@ -106,35 +105,6 @@ ExecSampleScan(PlanState *pstate)
 					(ExecScanRecheckMtd) SampleRecheck);
 }
 
-/* ----------------------------------------------------------------
- *		InitScanRelation
- *
- *		Set up to access the scan relation.
- * ----------------------------------------------------------------
- */
-static void
-InitScanRelation(SampleScanState *node, EState *estate, int eflags)
-{
-	Relation	currentRelation;
-
-	/*
-	 * get the relation object id from the relid'th entry in the range table,
-	 * open that relation and acquire appropriate lock on it.
-	 */
-	currentRelation = ExecOpenScanRelation(estate,
-										   ((SampleScan *) node->ss.ps.plan)->scan.scanrelid,
-										   eflags);
-
-	node->ss.ss_currentRelation = currentRelation;
-
-	/* we won't set up the HeapScanDesc till later */
-	node->ss.ss_currentScanDesc = NULL;
-
-	/* and report the scan tuple slot's rowtype */
-	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
-}
-
-
 /* ----------------------------------------------------------------
  *		ExecInitSampleScan
  * ----------------------------------------------------------------
@@ -164,6 +134,36 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+
+	/*
+	 * initialize scan relation
+	 */
+
+	/*
+	 * get the relation object id from the relid'th entry in the range table,
+	 * open that relation and acquire appropriate lock on it.
+	 */
+	scanstate->ss.ss_currentRelation =
+		ExecOpenScanRelation(estate,
+							 node->scan.scanrelid,
+							 eflags);
+
+	/* we won't set up the HeapScanDesc till later */
+	scanstate->ss.ss_currentScanDesc = NULL;
+
+	/* and create slot with appropriate rowtype */
+	ExecInitScanTupleSlot(estate, &scanstate->ss,
+						  RelationGetDescr(scanstate->ss.ss_currentRelation));
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignScanProjectionInfo(&scanstate->ss);
+
 	/*
 	 * initialize child expressions
 	 */
@@ -174,23 +174,6 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->repeatable =
 		ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
-	/*
-	 * initialize scan relation
-	 */
-	InitScanRelation(scanstate, estate, eflags);
-
-	/*
-	 * Initialize result tuple type and projection info.
-	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
-	ExecAssignScanProjectionInfo(&scanstate->ss);
-
 	/*
 	 * If we don't have a REPEATABLE clause, select a random seed.  We want to
 	 * do this just once, since the seed shouldn't change over rescans.
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index d4ac939c9b..da2bb69583 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -32,7 +32,6 @@
 #include "executor/nodeSeqscan.h"
 #include "utils/rel.h"
 
-static void InitScanRelation(SeqScanState *node, EState *estate, int eflags);
 static TupleTableSlot *SeqNext(SeqScanState *node);
 
 /* ----------------------------------------------------------------
@@ -132,31 +131,6 @@ ExecSeqScan(PlanState *pstate)
 					(ExecScanRecheckMtd) SeqRecheck);
 }
 
-/* ----------------------------------------------------------------
- *		InitScanRelation
- *
- *		Set up to access the scan relation.
- * ----------------------------------------------------------------
- */
-static void
-InitScanRelation(SeqScanState *node, EState *estate, int eflags)
-{
-	Relation	currentRelation;
-
-	/*
-	 * get the relation object id from the relid'th entry in the range table,
-	 * open that relation and acquire appropriate lock on it.
-	 */
-	currentRelation = ExecOpenScanRelation(estate,
-										   ((SeqScan *) node->ss.ps.plan)->scanrelid,
-										   eflags);
-
-	node->ss.ss_currentRelation = currentRelation;
-
-	/* and report the scan tuple slot's rowtype */
-	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
-}
-
 
 /* ----------------------------------------------------------------
  *		ExecInitSeqScan
@@ -189,29 +163,39 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
-
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
 
 	/*
 	 * initialize scan relation
 	 */
-	InitScanRelation(scanstate, estate, eflags);
+
+	/*
+	 * get the relation object id from the relid'th entry in the range table,
+	 * open that relation and acquire appropriate lock on it.
+	 */
+	scanstate->ss.ss_currentRelation =
+		ExecOpenScanRelation(estate,
+							 node->scanrelid,
+							 eflags);
+
+	/* and create slot with the appropriate rowtype */
+	ExecInitScanTupleSlot(estate, &scanstate->ss,
+						  RelationGetDescr(scanstate->ss.ss_currentRelation));
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual, (PlanState *) scanstate);
+
 	return scanstate;
 }
 
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 571cbf86b1..126e65d4c7 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -523,7 +523,7 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
 	/*
 	 * Tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &setopstate->ps);
+	ExecInitResultTupleSlotTL(estate, &setopstate->ps);
 
 	/*
 	 * initialize child nodes
@@ -539,7 +539,6 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
 	 * setop nodes do no projections, so initialize projection info for this
 	 * node appropriately
 	 */
-	ExecAssignResultTypeFromTL(&setopstate->ps);
 	setopstate->ps.ps_ProjInfo = NULL;
 
 	/*
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 98bcaeb66f..de4733bb19 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -191,20 +191,12 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	sortstate->sort_Done = false;
 	sortstate->tuplesortstate = NULL;
 
-	/*
-	 * Miscellaneous initialization
-	 *
-	 * Sort nodes don't initialize their ExprContexts because they never call
-	 * ExecQual or ExecProject.
-	 */
-
 	/*
 	 * tuple table initialization
 	 *
 	 * sort nodes only return scan tuples from their sorted relation.
 	 */
-	ExecInitResultTupleSlot(estate, &sortstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &sortstate->ss);
+	ExecInitResultTupleSlotTL(estate, &sortstate->ss.ps);
 
 	/*
 	 * initialize child nodes
@@ -220,10 +212,16 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
 	 * initialize tuple type.  no need to initialize projection info because
 	 * this node doesn't do projections.
 	 */
-	ExecAssignResultTypeFromTL(&sortstate->ss.ps);
-	ExecAssignScanTypeFromOuterPlan(&sortstate->ss);
+	ExecCreateScanSlotForOuterPlan(estate, &sortstate->ss);
 	sortstate->ss.ps.ps_ProjInfo = NULL;
 
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * Sort nodes don't initialize their ExprContexts because they never call
+	 * ExecQual or ExecProject.
+	 */
+
 	SO1_printf("ExecInitSort: %s\n",
 			   "sort node initialized");
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 088c92992e..93f582eb02 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -120,17 +120,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &subquerystate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	subquerystate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
-
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &subquerystate->ss.ps);
-	ExecInitScanTupleSlot(estate, &subquerystate->ss);
+	ExecInitResultTupleSlotTL(estate, &subquerystate->ss.ps);
 
 	/*
 	 * initialize subquery
@@ -140,15 +133,20 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	/*
 	 * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo)
 	 */
-	ExecAssignScanType(&subquerystate->ss,
+	ExecInitScanTupleSlot(estate, &subquerystate->ss,
 					   ExecGetResultType(subquerystate->subplan));
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&subquerystate->ss.ps);
 	ExecAssignScanProjectionInfo(&subquerystate->ss);
 
+	/*
+	 * initialize child expressions
+	 */
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
+
 	return subquerystate;
 }
 
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index 165fae8c83..d10d8b8097 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -139,18 +139,6 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
-
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
 	/*
 	 * initialize source tuple type
 	 */
@@ -159,14 +147,23 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
 								 tf->coltypmods,
 								 tf->colcollations);
 
-	ExecAssignScanType(&scanstate->ss, tupdesc);
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc);
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
+
 	/* Only XMLTABLE is supported currently */
 	scanstate->routine = &XmlTableRoutine;
 
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0ee76e7d25..8d7ea412c2 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -530,6 +530,30 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &tidstate->ss.ps);
 
+	/*
+	 * open the base relation and acquire appropriate lock on it.
+	 */
+	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
+
+	tidstate->ss.ss_currentRelation = currentRelation;
+	tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &tidstate->ss.ps);
+
+	/*
+	 * get the scan type from the relation descriptor.
+	 */
+	ExecInitScanTupleSlot(estate, &tidstate->ss,
+						  RelationGetDescr(currentRelation));
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignScanProjectionInfo(&tidstate->ss);
+
 	/*
 	 * initialize child expressions
 	 */
@@ -538,12 +562,6 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 
 	TidExprListCreate(tidstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &tidstate->ss);
-
 	/*
 	 * mark tid list as not computed yet
 	 */
@@ -551,25 +569,6 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->tss_NumTids = 0;
 	tidstate->tss_TidPtr = -1;
 
-	/*
-	 * open the base relation and acquire appropriate lock on it.
-	 */
-	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
-
-	tidstate->ss.ss_currentRelation = currentRelation;
-	tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
-
-	/*
-	 * get the scan type from the relation descriptor.
-	 */
-	ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation));
-
-	/*
-	 * Initialize result tuple type and projection info.
-	 */
-	ExecAssignResultTypeFromTL(&tidstate->ss.ps);
-	ExecAssignScanProjectionInfo(&tidstate->ss);
-
 	/*
 	 * all done.
 	 */
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 621fdd9b9c..b86f1e549a 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -143,7 +143,7 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
 	/*
 	 * Tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &uniquestate->ps);
+	ExecInitResultTupleSlotTL(estate, &uniquestate->ps);
 
 	/*
 	 * then initialize outer plan
@@ -154,7 +154,6 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
 	 * unique nodes do no projections, so initialize projection info for this
 	 * node appropriately
 	 */
-	ExecAssignResultTypeFromTL(&uniquestate->ps);
 	uniquestate->ps.ps_ProjInfo = NULL;
 
 	/*
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 1a72bfe160..232234cfd8 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -239,21 +239,20 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
-	/*
-	 * initialize child expressions
-	 */
-	scanstate->ss.ps.qual =
-		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
 
 	/*
 	 * get info about values list
 	 */
 	tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists));
 
-	ExecAssignScanType(&scanstate->ss, tupdesc);
+	ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc);
+
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
 	/*
 	 * Other node-specific setup
@@ -273,7 +272,6 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
 	return scanstate;
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 80be46029f..1b3d075b3a 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1823,8 +1823,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitScanTupleSlot(estate, &winstate->ss);
-	ExecInitResultTupleSlot(estate, &winstate->ss.ps);
+	ExecInitResultTupleSlotTL(estate, &winstate->ss.ps);
 	winstate->first_part_slot = ExecInitExtraTupleSlot(estate);
 	winstate->agg_row_slot = ExecInitExtraTupleSlot(estate);
 	winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
@@ -1847,7 +1846,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * initialize source tuple type (which is also the tuple type that we'll
 	 * store in the tuplestore and use in all our working slots).
 	 */
-	ExecAssignScanTypeFromOuterPlan(&winstate->ss);
+	ExecCreateScanSlotForOuterPlan(estate, &winstate->ss);
 
 	ExecSetSlotDescriptor(winstate->first_part_slot,
 						  winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
@@ -1861,7 +1860,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&winstate->ss.ps);
 	ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
 
 	/* Set up data for comparing tuples */
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index d5ffadda3e..f7ec95ba67 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -156,22 +156,21 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss, NULL);
+
 	/*
 	 * initialize child expressions
 	 */
 	scanstate->ss.ps.qual =
 		ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
 
-	/*
-	 * tuple table initialization
-	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
-	ExecInitScanTupleSlot(estate, &scanstate->ss);
-
 	/*
 	 * Initialize result tuple type, but not yet projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 
 	return scanstate;
 }
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index f48a603dae..f0601cb870 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -418,8 +418,8 @@ extern void ExecScanReScan(ScanState *node);
 /*
  * prototypes from functions in execTuples.c
  */
-extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate);
-extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
+extern void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate);
+extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupleDesc);
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
 extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
 					  TupleDesc tupType);
@@ -489,14 +489,12 @@ extern ExprContext *MakePerTupleExprContext(EState *estate);
 	} while (0)
 
 extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
-extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
-extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 extern TupleDesc ExecGetResultType(PlanState *planstate);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
 extern void ExecFreeExprContext(PlanState *planstate);
 extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
-extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);
+extern void ExecCreateScanSlotForOuterPlan(EState *estate, ScanState *scanstate);
 
 extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
 
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 55f4cce4ee..6c24fd334d 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -127,6 +127,7 @@ typedef struct TupleTableSlot
 	MinimalTuple tts_mintuple;	/* minimal tuple, or NULL if none */
 	HeapTupleData tts_minhdr;	/* workspace for minimal-tuple-only case */
 	long		tts_off;		/* saved state for slot_deform_tuple */
+	bool		tts_fixedTupleDescriptor;
 } TupleTableSlot;
 
 #define TTS_HAS_PHYSICAL_TUPLE(slot)  \
@@ -139,8 +140,8 @@ typedef struct TupleTableSlot
 	((slot) == NULL || (slot)->tts_isempty)
 
 /* in executor/execTuples.c */
-extern TupleTableSlot *MakeTupleTableSlot(void);
-extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable);
+extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);
+extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc);
 extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
 extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
 extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
-- 
2.14.1.2.g4274c698f4.dirty

