From 0d54d6ac44444c05f7c0f5058d3d3f32cc188b48 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 29 Jan 2001 00:39:20 +0000
Subject: [PATCH] Clean up handling of tuple descriptors so that result-tuple
 descriptors allocated by plan nodes are not leaked at end of query.  This
 doesn't really matter for normal queries, but it sure does for queries
 invoked repetitively inside SQL functions.  Clean up some other grotty code
 associated with tupdescs, and fix a few other memory leaks exposed by tests
 with simple SQL functions.

---
 src/backend/access/gist/gist.c          |   4 +-
 src/backend/access/hash/hash.c          |   4 +-
 src/backend/access/nbtree/nbtree.c      |   4 +-
 src/backend/access/rtree/rtree.c        |   4 +-
 src/backend/catalog/index.c             |   4 +-
 src/backend/commands/command.c          |  26 +-
 src/backend/commands/copy.c             |   4 +-
 src/backend/executor/execJunk.c         |  65 ++--
 src/backend/executor/execMain.c         |  37 +--
 src/backend/executor/execProcnode.c     | 187 +++++++++++-
 src/backend/executor/execQual.c         |  16 +-
 src/backend/executor/execTuples.c       | 388 +++---------------------
 src/backend/executor/execUtils.c        | 242 +--------------
 src/backend/executor/functions.c        |  63 ++--
 src/backend/executor/nodeHashjoin.c     |   5 +-
 src/backend/executor/nodeIndexscan.c    |  12 +-
 src/backend/executor/nodeMergejoin.c    |   5 +-
 src/backend/executor/nodeSeqscan.c      |   9 +-
 src/backend/executor/nodeSort.c         |  17 +-
 src/backend/executor/nodeSubqueryscan.c |   7 +-
 src/backend/executor/nodeTidscan.c      |   9 +-
 src/backend/utils/sort/tuplesort.c      |   3 +-
 src/include/executor/executor.h         |  15 +-
 src/include/executor/tuptable.h         |  26 +-
 src/include/nodes/execnodes.h           |  15 +-
 25 files changed, 423 insertions(+), 748 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 969b3309f76..c631f385037 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.68 2001/01/12 00:12:58 scrappy Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.69 2001/01/29 00:39:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -154,7 +154,7 @@ gistbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		ExecSetSlotDescriptor(slot, htupdesc);
+		ExecSetSlotDescriptor(slot, htupdesc, false);
 	}
 	else
 	{
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 1fcc54575de..2f4448e107e 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.47 2001/01/24 19:42:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.48 2001/01/29 00:39:13 tgl Exp $
  *
  * NOTES
  *	  This file contains only the public interface routines.
@@ -92,7 +92,7 @@ hashbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		ExecSetSlotDescriptor(slot, htupdesc);
+		ExecSetSlotDescriptor(slot, htupdesc, false);
 	}
 	else
 	{
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 8685975edf5..89720f8e2a1 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.76 2001/01/26 01:24:31 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.77 2001/01/29 00:39:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -128,7 +128,7 @@ btbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		ExecSetSlotDescriptor(slot, htupdesc);
+		ExecSetSlotDescriptor(slot, htupdesc, false);
 
 		/*
 		 * we never want to use sort/build if we are extending an existing
diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c
index 17f03e390c9..9f3948657c9 100644
--- a/src/backend/access/rtree/rtree.c
+++ b/src/backend/access/rtree/rtree.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.58 2001/01/24 19:42:50 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.59 2001/01/29 00:39:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,7 +125,7 @@ rtbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		ExecSetSlotDescriptor(slot, htupdesc);
+		ExecSetSlotDescriptor(slot, htupdesc, false);
 	}
 	else
 	{
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 9f1cf041495..1c2d6324cfc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.139 2001/01/24 19:42:51 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.140 2001/01/29 00:39:16 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1817,7 +1817,7 @@ DefaultBuild(Relation heapRelation,
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		ExecSetSlotDescriptor(slot, heapDescriptor);
+		ExecSetSlotDescriptor(slot, heapDescriptor, false);
 	}
 	else
 	{
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 618322645f3..30695a7a90b 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.119 2001/01/24 19:42:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.120 2001/01/29 00:39:20 tgl Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -1102,7 +1102,7 @@ AlterTableAddConstraint(char *relationName,
 					bool successful = true;
 					HeapScanDesc scan;
 					ExprContext *econtext;
-					TupleTableSlot *slot = makeNode(TupleTableSlot);
+					TupleTableSlot *slot;
 					HeapTuple tuple;
 					RangeTblEntry *rte;
 					List       *qual;
@@ -1169,28 +1169,28 @@ AlterTableAddConstraint(char *relationName,
 
 					qual = makeList1(expr);
 
+					/* Make tuple slot to hold tuples */
+					slot = MakeTupleTableSlot();
+					ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
+					/* Make an expression context for ExecQual */
+					econtext = MakeExprContext(slot, CurrentMemoryContext);
+
 					/*
-					 * Scan through the rows now, making the necessary things
-					 * for ExecQual, and then call it to evaluate the
-					 * expression.
+					 * Scan through the rows now, checking the expression
+					 * at each row.
 					 */
 					while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
 					{
-						slot->val = tuple;
-						slot->ttc_shouldFree = false;
-						slot->ttc_descIsNew = true;
-						slot->ttc_tupleDescriptor = rel->rd_att;
-						slot->ttc_buffer = InvalidBuffer;
-
-						econtext = MakeExprContext(slot, CurrentMemoryContext);
+						ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 						if (!ExecQual(qual, econtext, true))
 						{
 							successful=false;
 							break;
 						}
-						FreeExprContext(econtext);
+						ResetExprContext(econtext);
 					}
 
+					FreeExprContext(econtext);
 					pfree(slot);
 
 					heap_endscan(scan);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f992ac865b4..7d0352506ca 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.132 2001/01/24 19:42:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.133 2001/01/29 00:39:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -638,7 +638,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	/* Set up a dummy tuple table too */
 	tupleTable = ExecCreateTupleTable(1);
 	slot = ExecAllocTableSlot(tupleTable);
-	ExecSetSlotDescriptor(slot, tupDesc);
+	ExecSetSlotDescriptor(slot, tupDesc, false);
 
 	if (!binary)
 	{
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index e9556900ca4..f23ba273462 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -8,12 +8,10 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.24 2001/01/24 19:42:53 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.25 2001/01/29 00:39:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-
 #include "postgres.h"
 
 #include "access/heapam.h"
@@ -37,7 +35,7 @@
  * called 'resjunk'. If the value of this attribute is true then the
  * corresponding attribute is a "junk" attribute.
  *
- * When we initialize a plan  we call 'ExecInitJunkFilter' to create
+ * When we initialize a plan we call 'ExecInitJunkFilter' to create
  * and store the appropriate information in the 'es_junkFilter' attribute of
  * EState.
  *
@@ -63,6 +61,8 @@
 JunkFilter *
 ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 {
+	MemoryContext oldContext;
+	MemoryContext junkContext;
 	JunkFilter *junkfilter;
 	List	   *cleanTargetList;
 	int			len,
@@ -75,9 +75,21 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 	bool		resjunk;
 	AttrNumber	cleanResno;
 	AttrNumber *cleanMap;
-	Size		size;
 	Node	   *expr;
 
+	/*
+	 * Make a memory context that will hold the JunkFilter as well as all
+	 * the subsidiary structures we are about to create.  We use smaller-
+	 * than-default sizing parameters since we don't expect a very large
+	 * volume of stuff here.
+	 */
+	junkContext = AllocSetContextCreate(CurrentMemoryContext,
+										"JunkFilterContext",
+										1024,
+										1024,
+										ALLOCSET_DEFAULT_MAXSIZE);
+	oldContext = MemoryContextSwitchTo(junkContext);
+
 	/* ---------------------
 	 * First find the "clean" target list, i.e. all the entries
 	 * in the original target list which have a false 'resjunk'
@@ -166,7 +178,7 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 	cleanLength = ExecTargetListLength(cleanTargetList);
 
 	/* ---------------------
-	 * Now calculate the "map" between the original tuples attributes
+	 * Now calculate the "map" between the original tuple's attributes
 	 * and the "clean" tuple's attributes.
 	 *
 	 * The "map" is an array of "cleanLength" attribute numbers, i.e.
@@ -177,8 +189,7 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 	 */
 	if (cleanLength > 0)
 	{
-		size = cleanLength * sizeof(AttrNumber);
-		cleanMap = (AttrNumber *) palloc(size);
+		cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber));
 		cleanResno = 1;
 		foreach(t, targetList)
 		{
@@ -226,7 +237,7 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 		cleanMap = NULL;
 
 	/* ---------------------
-	 * Finally create and initialize the JunkFilter.
+	 * Finally create and initialize the JunkFilter struct.
 	 * ---------------------
 	 */
 	junkfilter = makeNode(JunkFilter);
@@ -238,20 +249,36 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
 	junkfilter->jf_cleanLength = cleanLength;
 	junkfilter->jf_cleanTupType = cleanTupType;
 	junkfilter->jf_cleanMap = cleanMap;
+	junkfilter->jf_junkContext = junkContext;
+
+	MemoryContextSwitchTo(oldContext);
 
 	return junkfilter;
+}
 
+/*-------------------------------------------------------------------------
+ * ExecFreeJunkFilter
+ *
+ * Release the data structures created by ExecInitJunkFilter.
+ *-------------------------------------------------------------------------
+ */
+void
+ExecFreeJunkFilter(JunkFilter *junkfilter)
+{
+	/*
+	 * Since the junkfilter is inside its own context, we just have to
+	 * delete the context and we're set.
+	 */
+	MemoryContextDelete(junkfilter->jf_junkContext);
 }
 
 /*-------------------------------------------------------------------------
  * ExecGetJunkAttribute
  *
  * Given a tuple (slot), the junk filter and a junk attribute's name,
- * extract & return the value of this attribute.
+ * extract & return the value and isNull flag of this attribute.
  *
  * It returns false iff no junk attribute with such name was found.
- *
- * NOTE: isNull might be NULL !
  *-------------------------------------------------------------------------
  */
 bool
@@ -304,7 +331,7 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
 	 * ---------------------
 	 */
 	tuple = slot->val;
-	tupType = (TupleDesc) junkfilter->jf_tupType;
+	tupType = junkfilter->jf_tupType;
 
 	*value = heap_getattr(tuple, resno, tupType, isNull);
 
@@ -328,7 +355,6 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
 	int			cleanLength;
 	bool		isNull;
 	int			i;
-	Size		size;
 	Datum	   *values;
 	char	   *nulls;
 	Datum		values_array[64];
@@ -340,8 +366,8 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
 	 */
 	tuple = slot->val;
 
-	tupType = (TupleDesc) junkfilter->jf_tupType;
-	cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
+	tupType = junkfilter->jf_tupType;
+	cleanTupType = junkfilter->jf_cleanTupType;
 	cleanLength = junkfilter->jf_cleanLength;
 	cleanMap = junkfilter->jf_cleanMap;
 
@@ -363,11 +389,8 @@ ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
 	 */
 	if (cleanLength > 64)
 	{
-		size = cleanLength * sizeof(Datum);
-		values = (Datum *) palloc(size);
-
-		size = cleanLength * sizeof(char);
-		nulls = (char *) palloc(size);
+		values = (Datum *) palloc(cleanLength * sizeof(Datum));
+		nulls = (char *) palloc(cleanLength * sizeof(char));
 	}
 	else
 	{
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 77af5e7ecce..929134209ba 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.137 2001/01/27 05:16:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.138 2001/01/29 00:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -252,12 +252,8 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, long count)
 /* ----------------------------------------------------------------
  *		ExecutorEnd
  *
- *		This routine must be called at the end of any execution of any
+ *		This routine must be called at the end of execution of any
  *		query plan
- *
- *		returns (AttrInfo*) which describes the attributes of the tuples to
- *		be returned by the query.
- *
  * ----------------------------------------------------------------
  */
 void
@@ -268,23 +264,15 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
 
 	EndPlan(queryDesc->plantree, estate);
 
-	/* XXX - clean up some more from ExecutorStart() - er1p */
-	if (NULL == estate->es_snapshot)
-	{
-		/* nothing to free */
-	}
-	else
+	if (estate->es_snapshot != NULL)
 	{
 		if (estate->es_snapshot->xcnt > 0)
 			pfree(estate->es_snapshot->xip);
 		pfree(estate->es_snapshot);
+		estate->es_snapshot = NULL;
 	}
 
-	if (NULL == estate->es_param_exec_vals)
-	{
-		/* nothing to free */
-	}
-	else
+	if (estate->es_param_exec_vals != NULL)
 	{
 		pfree(estate->es_param_exec_vals);
 		estate->es_param_exec_vals = NULL;
@@ -870,7 +858,7 @@ EndPlan(Plan *plan, EState *estate)
 
 	/*
 	 * close the result relation(s) if any, but hold locks
-	 * until xact commit.
+	 * until xact commit.  Also clean up junkfilters if present.
 	 */
 	resultRelInfo = estate->es_result_relations;
 	for (i = estate->es_num_result_relations; i > 0; i--)
@@ -878,6 +866,9 @@ EndPlan(Plan *plan, EState *estate)
 		/* Close indices and then the relation itself */
 		ExecCloseIndices(resultRelInfo);
 		heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+		/* Delete the junkfilter if any */
+		if (resultRelInfo->ri_junkFilter != NULL)
+			ExecFreeJunkFilter(resultRelInfo->ri_junkFilter);
 		resultRelInfo++;
 	}
 
@@ -887,6 +878,16 @@ EndPlan(Plan *plan, EState *estate)
 	if (estate->es_into_relation_descriptor != NULL)
 		heap_close(estate->es_into_relation_descriptor, NoLock);
 
+	/*
+	 * There might be a junkfilter without a result relation.
+	 */
+	if (estate->es_num_result_relations == 0 &&
+		estate->es_junkFilter != NULL)
+	{
+		ExecFreeJunkFilter(estate->es_junkFilter);
+		estate->es_junkFilter = NULL;
+	}
+
 	/*
 	 * close any relations selected FOR UPDATE, again keeping locks
 	 */
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 549f612d7eb..bc1b6d0b2f7 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.24 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.25 2001/01/29 00:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,8 @@
  *		ExecInitNode	-		initialize a plan node and its subplans
  *		ExecProcNode	-		get a tuple by executing the plan node
  *		ExecEndNode		-		shut down a plan node and its subplans
+ *		ExecCountSlotsNode -	count tuple slots needed by plan tree
+ *		ExecGetTupType	-		get result tuple type of a plan node
  *
  *	 NOTES
  *		This used to be three files.  It is now all combined into
@@ -218,7 +220,8 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
 			break;
 
 		default:
-			elog(ERROR, "ExecInitNode: node %d unsupported", nodeTag(node));
+			elog(ERROR, "ExecInitNode: node type %d unsupported",
+				 (int) nodeTag(node));
 			result = FALSE;
 	}
 
@@ -347,7 +350,8 @@ ExecProcNode(Plan *node, Plan *parent)
 			break;
 
 		default:
-			elog(ERROR, "ExecProcNode: node %d unsupported", nodeTag(node));
+			elog(ERROR, "ExecProcNode: node type %d unsupported",
+				 (int) nodeTag(node));
 			result = NULL;
 	}
 
@@ -430,8 +434,8 @@ ExecCountSlotsNode(Plan *node)
 			return ExecCountSlotsAgg((Agg *) node);
 
 		default:
-			elog(ERROR, "ExecCountSlotsNode: node not yet supported: %d",
-				 nodeTag(node));
+			elog(ERROR, "ExecCountSlotsNode: node type %d unsupported",
+				 (int) nodeTag(node));
 			break;
 	}
 	return 0;
@@ -558,7 +562,178 @@ ExecEndNode(Plan *node, Plan *parent)
 			break;
 
 		default:
-			elog(ERROR, "ExecEndNode: node %d unsupported", nodeTag(node));
+			elog(ERROR, "ExecEndNode: node type %d unsupported",
+				 (int) nodeTag(node));
 			break;
 	}
 }
+
+
+/* ----------------------------------------------------------------
+ *		ExecGetTupType
+ *
+ *		this gives you the tuple descriptor for tuples returned
+ *		by this node.  I really wish I could ditch this routine,
+ *		but since not all nodes store their type info in the same
+ *		place, we have to do something special for each node type.
+ *
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecGetTupType(Plan *node)
+{
+	TupleTableSlot *slot;
+
+	if (node == NULL)
+		return NULL;
+
+	switch (nodeTag(node))
+	{
+		case T_Result:
+			{
+				ResultState *resstate = ((Result *) node)->resstate;
+
+				slot = resstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_SeqScan:
+			{
+				CommonScanState *scanstate = ((SeqScan *) node)->scanstate;
+
+				slot = scanstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_NestLoop:
+			{
+				NestLoopState *nlstate = ((NestLoop *) node)->nlstate;
+
+				slot = nlstate->jstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Append:
+			{
+				AppendState *appendstate = ((Append *) node)->appendstate;
+
+				slot = appendstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_IndexScan:
+			{
+				CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
+
+				slot = scanstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_TidScan:
+			{
+				CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
+
+				slot = scanstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_SubqueryScan:
+			{
+				CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
+
+				slot = scanstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Material:
+			{
+				MaterialState *matstate = ((Material *) node)->matstate;
+
+				slot = matstate->csstate.css_ScanTupleSlot;
+			}
+			break;
+
+		case T_Sort:
+			{
+				SortState  *sortstate = ((Sort *) node)->sortstate;
+
+				slot = sortstate->csstate.css_ScanTupleSlot;
+			}
+			break;
+
+		case T_Agg:
+			{
+				AggState   *aggstate = ((Agg *) node)->aggstate;
+
+				slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Group:
+			{
+				GroupState *grpstate = ((Group *) node)->grpstate;
+
+				slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Hash:
+			{
+				HashState  *hashstate = ((Hash *) node)->hashstate;
+
+				slot = hashstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Unique:
+			{
+				UniqueState *uniquestate = ((Unique *) node)->uniquestate;
+
+				slot = uniquestate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_SetOp:
+			{
+				SetOpState *setopstate = ((SetOp *) node)->setopstate;
+
+				slot = setopstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_Limit:
+			{
+				LimitState *limitstate = ((Limit *) node)->limitstate;
+
+				slot = limitstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_MergeJoin:
+			{
+				MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
+
+				slot = mergestate->jstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_HashJoin:
+			{
+				HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;
+
+				slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		default:
+			/* ----------------
+			 *	  should never get here
+			 * ----------------
+			 */
+			elog(ERROR, "ExecGetTupType: node type %d unsupported",
+				 (int) nodeTag(node));
+			return NULL;
+	}
+
+	return slot->ttc_tupleDescriptor;
+}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index f0721f57bd5..bab2851df9d 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.82 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.83 2001/01/29 00:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -328,25 +328,19 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 	/*
 	 * If the attribute number is invalid, then we are supposed to return
 	 * the entire tuple, we give back a whole slot so that callers know
-	 * what the tuple looks like.
+	 * what the tuple looks like.  XXX why copy?  Couldn't we just give
+	 * back the existing slot?
 	 */
 	if (attnum == InvalidAttrNumber)
 	{
-		TupleTableSlot *tempSlot;
+		TupleTableSlot *tempSlot = MakeTupleTableSlot();
 		TupleDesc	td;
 		HeapTuple	tup;
 
-		tempSlot = makeNode(TupleTableSlot);
-		tempSlot->ttc_shouldFree = false;
-		tempSlot->ttc_descIsNew = true;
-		tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
-		tempSlot->ttc_buffer = InvalidBuffer;
-
 		tup = heap_copytuple(heapTuple);
 		td = CreateTupleDescCopy(tuple_type);
 
-		ExecSetSlotDescriptor(tempSlot, td);
-
+		ExecSetSlotDescriptor(tempSlot, td, true);
 		ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
 		return PointerGetDatum(tempSlot);
 	}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index ecb8ae48b50..e5f1a269d81 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.45 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.46 2001/01/29 00:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,26 +24,20 @@
  *
  *	 TABLE CREATE/DELETE
  *		ExecCreateTupleTable	- create a new tuple table
- *		ExecDropTupleTable	- destroy a table
+ *		ExecDropTupleTable		- destroy a table
  *
- *	 SLOT RESERVERATION
+ *	 SLOT RESERVATION
  *		ExecAllocTableSlot		- find an available slot in the table
  *
  *	 SLOT ACCESSORS
  *		ExecStoreTuple			- store a tuple in the table
  *		ExecFetchTuple			- fetch a tuple from the table
  *		ExecClearTuple			- clear contents of a table slot
- *		ExecSlotPolicy			- return slot's tuple pfree policy
- *		ExecSetSlotPolicy		- diddle the slot policy
- *		ExecSlotDescriptor		- type of tuple in a slot
  *		ExecSetSlotDescriptor	- set a slot's tuple descriptor
  *		ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
- *		ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
  *
  *	 SLOT STATUS PREDICATES
  *		TupIsNull				- true when slot contains no tuple(Macro)
- *		ExecSlotDescriptorIsNew - true if we're now storing a different
- *								  type of tuple in a slot
  *
  *	 CONVENIENCE INITIALIZATION ROUTINES
  *		ExecInitResultTupleSlot	   \	convenience routines to initialize
@@ -51,8 +45,7 @@
  *		ExecInitExtraTupleSlot		/	which store copies of tuples.
  *		ExecInitNullTupleSlot	   /
  *
- *	 old routines:
- *		ExecGetTupType			- get type of tuple returned by this node
+ *	 Routines that probably belong somewhere else:
  *		ExecTypeFromTL			- form a TupleDesc from a target list
  *
  *	 EXAMPLE OF HOW TABLE ROUTINES WORK
@@ -112,16 +105,11 @@
  *		and the TupleTableSlot node in execnodes.h.
  *
  */
-
 #include "postgres.h"
-#include "executor/executor.h"
-
-#undef ExecStoreTuple
 
-#include "catalog/pg_type.h"
 #include "access/heapam.h"
-
-static TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
 
 
 /* ----------------------------------------------------------------
@@ -212,23 +200,17 @@ ExecDropTupleTable(TupleTable table,	/* tuple table */
 	 *	and drop refcounts of any referenced buffers,
 	 *	if that's what the caller wants.  (There is probably
 	 *	no good reason for the caller ever not to want it!)
-	 *
-	 *	Note: we do nothing about the Tuple Descriptor's
-	 *	we store in the slots.	This may have to change (ex: we should
-	 *	probably worry about pfreeing tuple descs too) -cim 3/14/91
-	 *
-	 *	Right now, the handling of tuple pointers and buffer refcounts
-	 *	is clean, but the handling of tuple descriptors is NOT; they
-	 *	are copied around with wild abandon.  It would take some work
-	 *	to make tuple descs pfree'able.  Fortunately, since they're
-	 *	normally only made once per scan, it's probably not worth
-	 *	worrying about...  tgl 9/21/99
 	 * ----------------
 	 */
 	if (shouldFree)
 	{
 		for (i = 0; i < next; i++)
+		{
 			ExecClearTuple(&array[i]);
+			if (array[i].ttc_shouldFreeDesc &&
+				array[i].ttc_tupleDescriptor != NULL)
+				FreeTupleDesc(array[i].ttc_tupleDescriptor);
+		}
 	}
 
 	/* ----------------
@@ -301,6 +283,32 @@ ExecAllocTableSlot(TupleTable table)
 	slot->val = (HeapTuple) NULL;
 	slot->ttc_shouldFree = true;
 	slot->ttc_descIsNew = true;
+	slot->ttc_shouldFreeDesc = true;
+	slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+	slot->ttc_buffer = InvalidBuffer;
+
+	return slot;
+}
+
+/* --------------------------------
+ *		MakeTupleTableSlot
+ *
+ *		This routine makes an empty standalone TupleTableSlot.
+ *		It really shouldn't exist, but there are a few places
+ *		that do this, so we may as well centralize the knowledge
+ *		of what's in one ...
+ * --------------------------------
+ */
+TupleTableSlot *
+MakeTupleTableSlot(void)
+{
+	TupleTableSlot *slot = makeNode(TupleTableSlot);
+
+	/* This should match ExecAllocTableSlot() */
+	slot->val = (HeapTuple) NULL;
+	slot->ttc_shouldFree = true;
+	slot->ttc_descIsNew = true;
+	slot->ttc_shouldFreeDesc = true;
 	slot->ttc_tupleDescriptor = (TupleDesc) NULL;
 	slot->ttc_buffer = InvalidBuffer;
 
@@ -384,6 +392,8 @@ ExecStoreTuple(HeapTuple tuple,
  *		ExecClearTuple
  *
  *		This function is used to clear out a slot in the tuple table.
+ *
+ *		NB: only the tuple is cleared, not the tuple descriptor (if any).
  * --------------------------------
  */
 TupleTableSlot *				/* return: slot passed */
@@ -426,57 +436,6 @@ ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 	return slot;
 }
 
-
-/* --------------------------------
- *		ExecSlotPolicy
- *
- *		This function is used to get the call/don't call pfree
- *		setting of a slot.	Most executor routines don't need this.
- *		It's only when you do tricky things like marking tuples for
- *		merge joins that you need to diddle the slot policy.
- * --------------------------------
- */
-#ifdef NOT_USED
-bool							/* return: slot policy */
-ExecSlotPolicy(TupleTableSlot *slot)	/* slot to inspect */
-{
-	return slot->ttc_shouldFree;
-}
-
-
-/* --------------------------------
- *		ExecSetSlotPolicy
- *
- *		This function is used to change the call/don't call pfree
- *		setting of a slot.	Most executor routines don't need this.
- *		It's only when you do tricky things like marking tuples for
- *		merge joins that you need to diddle the slot policy.
- * --------------------------------
- */
-bool							/* return: old slot policy */
-ExecSetSlotPolicy(TupleTableSlot *slot, /* slot to change */
-				  bool shouldFree)		/* true if we call pfree() when we
-										 * gc. */
-{
-	bool		old_shouldFree = slot->ttc_shouldFree;
-
-	slot->ttc_shouldFree = shouldFree;
-
-	return old_shouldFree;
-}
-
-#endif
-
-/* --------------------------------
- *		ExecSlotDescriptor
- *
- *		This function is used to get the tuple descriptor associated
- *		with the slot's tuple.
- *
- * Now a macro in tuptable.h  -mer 5 March 1992
- * --------------------------------
- */
-
 /* --------------------------------
  *		ExecSetSlotDescriptor
  *
@@ -484,14 +443,17 @@ ExecSetSlotPolicy(TupleTableSlot *slot, /* slot to change */
  *		with the slot's tuple.
  * --------------------------------
  */
-TupleDesc						/* return: old slot tuple descriptor */
+void
 ExecSetSlotDescriptor(TupleTableSlot *slot,		/* slot to change */
-					  TupleDesc tupdesc)		/* tuple descriptor */
+					  TupleDesc tupdesc,		/* new tuple descriptor */
+					  bool shouldFree)			/* is desc owned by slot? */
 {
-	TupleDesc	old_tupdesc = slot->ttc_tupleDescriptor;
+	if (slot->ttc_shouldFreeDesc &&
+		slot->ttc_tupleDescriptor != NULL)
+		FreeTupleDesc(slot->ttc_tupleDescriptor);
 
 	slot->ttc_tupleDescriptor = tupdesc;
-	return old_tupdesc;
+	slot->ttc_shouldFreeDesc = shouldFree;
 }
 
 /* --------------------------------
@@ -507,52 +469,11 @@ ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,		/* slot to change */
 	slot->ttc_descIsNew = isNew;
 }
 
-/* --------------------------------
- *		ExecSetNewSlotDescriptor
- *
- *		This function is used to set the tuple descriptor associated
- *		with the slot's tuple, and set the "isNew" flag at the same time.
- * --------------------------------
- */
-#ifdef NOT_USED
-TupleDesc						/* return: old slot tuple descriptor */
-ExecSetNewSlotDescriptor(TupleTableSlot *slot,	/* slot to change */
-						 TupleDesc tupdesc)		/* tuple descriptor */
-{
-	TupleDesc	old_tupdesc = slot->ttc_tupleDescriptor;
-
-	slot->ttc_tupleDescriptor = tupdesc;
-	slot->ttc_descIsNew = true;
-
-	return old_tupdesc;
-}
-
-#endif
-
 /* ----------------------------------------------------------------
  *				  tuple table slot status predicates
  * ----------------------------------------------------------------
  */
 
-/* --------------------------------
- *		ExecSlotDescriptorIsNew
- *
- *		This function is used to check if the tuple descriptor
- *		associated with this slot has just changed.  ie: we are
- *		now storing a new type of tuple in this slot
- * --------------------------------
- */
-#ifdef NOT_USED
-bool							/* return: descriptor "is new" */
-ExecSlotDescriptorIsNew(TupleTableSlot *slot)	/* slot to inspect */
-{
-/*	  bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
-	return isNew; */
-	return slot->ttc_descIsNew;
-}
-
-#endif
-
 /* ----------------------------------------------------------------
  *				convenience initialization routines
  * ----------------------------------------------------------------
@@ -632,228 +553,13 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 	static struct tupleDesc NullTupleDesc;		/* we assume this inits to
 												 * zeroes */
 
-	ExecSetSlotDescriptor(slot, tupType);
+	ExecSetSlotDescriptor(slot, tupType, false);
 
 	nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
 
 	return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
 }
 
-
-static TupleTableSlot *
-NodeGetResultTupleSlot(Plan *node)
-{
-	TupleTableSlot *slot;
-
-	switch (nodeTag(node))
-	{
-
-		case T_Result:
-			{
-				ResultState *resstate = ((Result *) node)->resstate;
-
-				slot = resstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_SeqScan:
-			{
-				CommonScanState *scanstate = ((SeqScan *) node)->scanstate;
-
-				slot = scanstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_NestLoop:
-			{
-				NestLoopState *nlstate = ((NestLoop *) node)->nlstate;
-
-				slot = nlstate->jstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Append:
-			{
-				AppendState *appendstate = ((Append *) node)->appendstate;
-
-				slot = appendstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_IndexScan:
-			{
-				CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
-
-				slot = scanstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_TidScan:
-			{
-				CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
-
-				slot = scanstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_SubqueryScan:
-			{
-				CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
-
-				slot = scanstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Material:
-			{
-				MaterialState *matstate = ((Material *) node)->matstate;
-
-				slot = matstate->csstate.css_ScanTupleSlot;
-			}
-			break;
-
-		case T_Sort:
-			{
-				SortState  *sortstate = ((Sort *) node)->sortstate;
-
-				slot = sortstate->csstate.css_ScanTupleSlot;
-			}
-			break;
-
-		case T_Agg:
-			{
-				AggState   *aggstate = ((Agg *) node)->aggstate;
-
-				slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Group:
-			{
-				GroupState *grpstate = ((Group *) node)->grpstate;
-
-				slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Hash:
-			{
-				HashState  *hashstate = ((Hash *) node)->hashstate;
-
-				slot = hashstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Unique:
-			{
-				UniqueState *uniquestate = ((Unique *) node)->uniquestate;
-
-				slot = uniquestate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_SetOp:
-			{
-				SetOpState *setopstate = ((SetOp *) node)->setopstate;
-
-				slot = setopstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_Limit:
-			{
-				LimitState *limitstate = ((Limit *) node)->limitstate;
-
-				slot = limitstate->cstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_MergeJoin:
-			{
-				MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
-
-				slot = mergestate->jstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		case T_HashJoin:
-			{
-				HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;
-
-				slot = hashjoinstate->jstate.cs_ResultTupleSlot;
-			}
-			break;
-
-		default:
-			/* ----------------
-			 *	  should never get here
-			 * ----------------
-			 */
-			elog(ERROR, "NodeGetResultTupleSlot: node not yet supported: %d",
-				 (int) nodeTag(node));
-
-			return NULL;
-	}
-	return slot;
-}
-
-/* ----------------------------------------------------------------
- *		ExecGetTupType
- *
- *		this gives you the tuple descriptor for tuples returned
- *		by this node.  I really wish I could ditch this routine,
- *		but since not all nodes store their type info in the same
- *		place, we have to do something special for each node type.
- *
- *		Soon, the system will have to adapt to deal with changing
- *		tuple descriptors as we deal with dynamic tuple types
- *		being returned from procedure nodes.  Perhaps then this
- *		routine can be retired.  -cim 6/3/91
- *
- * old comments
- *		This routine just gets the type information out of the
- *		node's state.  If you already have a node's state, you
- *		can get this information directly, but this is a useful
- *		routine if you want to get the type information from
- *		the node's inner or outer subplan easily without having
- *		to inspect the subplan.. -cim 10/16/89
- *
- * ----------------------------------------------------------------
- */
-
-TupleDesc
-ExecGetTupType(Plan *node)
-{
-	TupleTableSlot *slot;
-	TupleDesc	tupType;
-
-	if (node == NULL)
-		return NULL;
-
-	slot = NodeGetResultTupleSlot(node);
-	tupType = slot->ttc_tupleDescriptor;
-	return tupType;
-}
-
-#ifdef NOT_USED
-TupleDesc
-ExecCopyTupType(TupleDesc td, int natts)
-{
-	TupleDesc newTd;
-	int				i;
-
-	newTd = CreateTemplateTupleDesc(natts);
-	i = 0;
-	while (i < natts)
-		{
-			newTd[i] = (Form_pg_attribute)palloc(sizeof(FormData_pg_attribute));
-			memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
-			i++;
-		}
-	return newTd;
-}
-#endif
-
 /* ----------------------------------------------------------------
  *		ExecTypeFromTL
  *
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 703767cb715..6b030b64a0e 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.72 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.73 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,17 +16,6 @@
  * INTERFACE ROUTINES
  *		ExecAssignExprContext	Common code for plan node init routines.
  *
- *		ExecGetTypeInfo			  |  old execCStructs interface
- *		ExecMakeTypeInfo		  |  code from the version 1
- *		ExecOrderTypeInfo		  |  lisp system.  These should
- *		ExecSetTypeInfo			  |  go away or be updated soon.
- *		ExecFreeTypeInfo		  |  -cim 11/1/89
- *		ExecTupleAttributes		/
- *
-
- *		QueryDescGetTypeInfo - moved here from main.c
- *								am not sure what uses it -cim 10/12/89
- *
  *		ExecOpenIndices			\
  *		ExecCloseIndices		 | referenced by InitPlan, EndPlan,
  *		ExecInsertIndexTuples	/  ExecAppend, ExecReplace
@@ -261,12 +250,11 @@ MakePerTupleExprContext(EState *estate)
  */
 void
 ExecAssignResultType(CommonState *commonstate,
-					 TupleDesc tupDesc)
+					 TupleDesc tupDesc, bool shouldFree)
 {
-	TupleTableSlot *slot;
+	TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
 
-	slot = commonstate->cs_ResultTupleSlot;
-	slot->ttc_tupleDescriptor = tupDesc;
+	ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
 }
 
 /* ----------------
@@ -282,7 +270,7 @@ ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
 	outerPlan = outerPlan(node);
 	tupDesc = ExecGetTupType(outerPlan);
 
-	ExecAssignResultType(commonstate, tupDesc);
+	ExecAssignResultType(commonstate, tupDesc, false);
 }
 
 /* ----------------
@@ -292,12 +280,10 @@ ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
 void
 ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
 {
-	List	   *targetList;
 	TupleDesc	tupDesc;
 
-	targetList = node->targetlist;
-	tupDesc = ExecTypeFromTL(targetList);
-	ExecAssignResultType(commonstate, tupDesc);
+	tupDesc = ExecTypeFromTL(node->targetlist);
+	ExecAssignResultType(commonstate, tupDesc, true);
 }
 
 /* ----------------
@@ -312,25 +298,6 @@ ExecGetResultType(CommonState *commonstate)
 	return slot->ttc_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecFreeResultType
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecFreeResultType(CommonState *commonstate)
-{
-	TupleTableSlot *slot;
-	TupleDesc	tupType;
-
-	slot = commonstate->cs_ResultTupleSlot;
-	tupType = slot->ttc_tupleDescriptor;
-
-	ExecFreeTypeInfo(tupType);
-}
-
-#endif
-
 /* ----------------
  *		ExecAssignProjectionInfo
 		  forms the projection information from the node's targetlist
@@ -413,29 +380,6 @@ ExecFreeExprContext(CommonState *commonstate)
 	commonstate->cs_ExprContext = NULL;
 }
 
-/* ----------------
- *		ExecFreeTypeInfo
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecFreeTypeInfo(CommonState *commonstate)
-{
-	TupleDesc	tupDesc;
-
-	tupDesc = commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor;
-	if (tupDesc == NULL)
-		return;
-
-	/* ----------------
-	 *	clean up memory used.
-	 * ----------------
-	 */
-	FreeTupleDesc(tupDesc);
-	commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
-}
-#endif
-
 /* ----------------------------------------------------------------
  *		the following scan type support functions are for
  *		those nodes which are stubborn and return tuples in
@@ -458,37 +402,17 @@ ExecGetScanType(CommonScanState *csstate)
 	return slot->ttc_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecFreeScanType
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecFreeScanType(CommonScanState *csstate)
-{
-	TupleTableSlot *slot;
-	TupleDesc	tupType;
-
-	slot = csstate->css_ScanTupleSlot;
-	tupType = slot->ttc_tupleDescriptor;
-
-	ExecFreeTypeInfo(tupType);
-}
-
-#endif
-
 /* ----------------
  *		ExecAssignScanType
  * ----------------
  */
 void
 ExecAssignScanType(CommonScanState *csstate,
-				   TupleDesc tupDesc)
+				   TupleDesc tupDesc, bool shouldFree)
 {
-	TupleTableSlot *slot;
+	TupleTableSlot *slot = csstate->css_ScanTupleSlot;
 
-	slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
-	slot->ttc_tupleDescriptor = tupDesc;
+	ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
 }
 
 /* ----------------
@@ -504,154 +428,10 @@ ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
 	outerPlan = outerPlan(node);
 	tupDesc = ExecGetTupType(outerPlan);
 
-	ExecAssignScanType(csstate, tupDesc);
+	ExecAssignScanType(csstate, tupDesc, false);
 }
 
 
-/* ----------------------------------------------------------------
- *		ExecTypeFromTL support routines.
- *
- *		these routines are used mainly from ExecTypeFromTL.
- *		-cim 6/12/90
- *
- * old comments
- *		Routines dealing with the structure 'attribute' which conatains
- *		the type information about attributes in a tuple:
- *
- *		ExecMakeTypeInfo(noType)
- *				returns pointer to array of 'noType' structure 'attribute'.
- *		ExecSetTypeInfo(index, typeInfo, attNum, attLen)
- *				sets the element indexed by 'index' in typeInfo with
- *				the values: attNum, attLen.
- *		ExecFreeTypeInfo(typeInfo)
- *				frees the structure 'typeInfo'.
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- *		ExecSetTypeInfo
- *
- *		This initializes fields of a single attribute in a
- *		tuple descriptor from the specified parameters.
- *
- *		XXX this duplicates much of the functionality of TupleDescInitEntry.
- *			the routines should be moved to the same place and be rewritten
- *			to share common code.
- * ----------------
- */
-#ifdef NOT_USED
-void
-ExecSetTypeInfo(int index,
-				TupleDesc typeInfo,
-				Oid typeID,
-				int attNum,
-				int attLen,
-				char *attName,
-				bool attbyVal,
-				char attalign)
-{
-	Form_pg_attribute att;
-
-	/* ----------------
-	 *	get attribute pointer and preform a sanity check..
-	 * ----------------
-	 */
-	att = typeInfo[index];
-	if (att == NULL)
-		elog(ERROR, "ExecSetTypeInfo: trying to assign through NULL");
-
-	/* ----------------
-	 *	assign values to the tuple descriptor, being careful not
-	 *	to copy a null attName..
-	 *
-	 *	XXX it is unknown exactly what information is needed to
-	 *		initialize the attribute struct correctly so for now
-	 *		we use 0.  this should be fixed -- otherwise we run the
-	 *		risk of using garbage data. -cim 5/5/91
-	 * ----------------
-	 */
-	att->attrelid = 0;			/* dummy value */
-
-	if (attName != (char *) NULL)
-		StrNCpy(NameStr(att->attname), attName, NAMEDATALEN);
-	else
-		MemSet(NameStr(att->attname), 0, NAMEDATALEN);
-
-	att->atttypid = typeID;
-	att->attdefrel = 0;			/* dummy value */
-	att->attdispersion = 0;		/* dummy value */
-	att->atttyparg = 0;			/* dummy value */
-	att->attlen = attLen;
-	att->attnum = attNum;
-	att->attbound = 0;			/* dummy value */
-	att->attbyval = attbyVal;
-	att->attcanindex = 0;		/* dummy value */
-	att->attproc = 0;			/* dummy value */
-	att->attnelems = 0;			/* dummy value */
-	att->attcacheoff = -1;
-	att->atttypmod = -1;
-	att->attisset = false;
-	att->attstorage = 'p';
-	att->attalign = attalign;
-}
-
-/* ----------------
- *		ExecFreeTypeInfo frees the array of attributes
- *		created by ExecMakeTypeInfo and returned by ExecTypeFromTL
- * ----------------
- */
-void
-ExecFreeTypeInfo(TupleDesc typeInfo)
-{
-	/* ----------------
-	 *	do nothing if asked to free a null pointer
-	 * ----------------
-	 */
-	if (typeInfo == NULL)
-		return;
-
-	/* ----------------
-	 *	the entire array of typeinfo pointers created by
-	 *	ExecMakeTypeInfo was allocated with a single palloc()
-	 *	so we can deallocate the whole array with a single pfree().
-	 *	(we should not try and free all the elements in the array)
-	 *	-cim 6/12/90
-	 * ----------------
-	 */
-	pfree(typeInfo);
-}
-
-
-/* ----------------------------------------------------------------
- *		QueryDescGetTypeInfo
- *
- *|		I don't know how this is used, all I know is that it
- *|		appeared one day in main.c so I moved it here. -cim 11/1/89
- * ----------------------------------------------------------------
- */
-TupleDesc
-QueryDescGetTypeInfo(QueryDesc *queryDesc)
-{
-	Plan	   *plan;
-	TupleDesc	tupleType;
-	List	   *targetList;
-	AttrInfo   *attinfo = (AttrInfo *) palloc(sizeof(AttrInfo));
-
-	plan = queryDesc->plantree;
-	tupleType = (TupleDesc) ExecGetTupType(plan);
-/*
-	targetList =  plan->targetlist;
-
-	attinfo->numAttr = ExecTargetListLength(targetList);
-	attinfo->attrs = tupleType;
-*/
-	attinfo->numAttr = tupleType->natts;
-	attinfo->attrs = tupleType->attrs;
-	return attinfo;
-}
-
-#endif
-
 /* ----------------------------------------------------------------
  *				  ExecInsertIndexTuples support
  * ----------------------------------------------------------------
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 18a0e0cbb3b..575f33d84b6 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.42 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.43 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,6 @@
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
-#include "utils/datum.h"
 #include "utils/syscache.h"
 
 
@@ -73,7 +72,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
 static execution_state *init_execution_state(char *src,
 											 Oid *argOidVect, int nargs);
 static void init_sql_fcache(FmgrInfo *finfo);
-static TupleDesc postquel_start(execution_state *es);
+static void postquel_start(execution_state *es);
 static TupleTableSlot *postquel_getnext(execution_state *es);
 static void postquel_end(execution_state *es);
 static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
@@ -82,24 +81,6 @@ static Datum postquel_execute(execution_state *es,
 							  SQLFunctionCachePtr fcache);
 
 
-static Datum
-ProjectAttribute(HeapTuple tup,
-				 AttrNumber	attrno,
-				 TupleDesc TD,
-				 bool *isnullP)
-{
-	Datum		val;
-
-	val = heap_getattr(tup, attrno, TD, isnullP);
-
-	if (*isnullP)
-		return val;
-
-	return datumCopy(val,
-					 TD->attrs[attrno - 1]->attbyval,
-					 TD->attrs[attrno - 1]->attlen);
-}
-
 static execution_state *
 init_execution_state(char *src, Oid *argOidVect, int nargs)
 {
@@ -240,18 +221,7 @@ init_sql_fcache(FmgrInfo *finfo)
 	 * allocated by the executor (i.e. slots and tuples) is freed.
 	 */
 	if (!finfo->fn_retset && !fcache->typbyval)
-	{
-		TupleTableSlot *slot;
-
-		slot = makeNode(TupleTableSlot);
-		slot->val = (HeapTuple) NULL;
-		slot->ttc_shouldFree = true;
-		slot->ttc_descIsNew = true;
-		slot->ttc_tupleDescriptor = (TupleDesc) NULL;
-		slot->ttc_buffer = InvalidBuffer;
-
-		fcache->funcSlot = slot;
-	}
+		fcache->funcSlot = MakeTupleTableSlot();
 	else
 		fcache->funcSlot = NULL;
 
@@ -289,7 +259,7 @@ init_sql_fcache(FmgrInfo *finfo)
 }
 
 
-static TupleDesc
+static void
 postquel_start(execution_state *es)
 {
 
@@ -298,8 +268,8 @@ postquel_start(execution_state *es)
 	 * 30-8-1996
 	 */
 	if (es->qd->operation == CMD_UTILITY)
-		return (TupleDesc) NULL;
-	return ExecutorStart(es->qd, es->estate);
+		return;
+	ExecutorStart(es->qd, es->estate);
 }
 
 static TupleTableSlot *
@@ -379,11 +349,11 @@ copy_function_result(SQLFunctionCachePtr fcache,
 	 * If first time through, we have to initialize the funcSlot's
 	 * tuple descriptor.
 	 */
-	if (TupIsNull(funcSlot))
+	if (funcSlot->ttc_tupleDescriptor == NULL)
 	{
-		resultTd = resultSlot->ttc_tupleDescriptor;
-		funcSlot->ttc_tupleDescriptor = CreateTupleDescCopy(resultTd);
-		funcSlot->ttc_descIsNew = true;
+		resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
+		ExecSetSlotDescriptor(funcSlot, resultTd, true);
+		ExecSetSlotDescriptorIsNew(funcSlot, true);
 	}
 
 	newTuple = heap_copytuple(resultTuple);
@@ -460,10 +430,15 @@ postquel_execute(execution_state *es,
 		}
 		else
 		{
-			value = ProjectAttribute(resSlot->val,
-									 1,
-									 resSlot->ttc_tupleDescriptor,
-									 &fcinfo->isnull);
+			value = heap_getattr(resSlot->val,
+								 1,
+								 resSlot->ttc_tupleDescriptor,
+								 &(fcinfo->isnull));
+			/*
+			 * Note: if result type is pass-by-reference then we are
+			 * returning a pointer into the tuple copied by
+			 * copy_function_result.  This is OK.
+			 */
 		}
 
 		/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 0c6703fb70c..a3fc2f545cb 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.35 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.36 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -400,7 +400,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 	ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
 
 	ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
-						  ExecGetTupType(outerNode));
+						  ExecGetTupType(outerNode),
+						  false);
 
 	/* ----------------
 	 *	initialize hash-specific info
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 7ed93de0848..c0369e8f4cd 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.56 2001/01/24 19:42:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.57 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -121,13 +121,11 @@ IndexNext(IndexScan *node)
 		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		/* probably ought to use ExecStoreTuple here... */
-		slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
-		slot->ttc_shouldFree = false;
-
-		econtext->ecxt_scantuple = slot;
+		ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
+					   slot, InvalidBuffer, false);
 
 		/* Does the tuple meet any of the OR'd indxqual conditions? */
+		econtext->ecxt_scantuple = slot;
 
 		ResetExprContext(econtext);
 
@@ -1043,7 +1041,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	 *	get the scan type from the relation descriptor.
 	 * ----------------
 	 */
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
+	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
 	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
 
 	/* ----------------
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 359d0c7bc5d..fd8868a4a54 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.41 2001/01/24 19:42:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.42 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1505,7 +1505,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 
 	mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
 	ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
-						  ExecGetTupType(innerPlan((Plan *) node)));
+						  ExecGetTupType(innerPlan((Plan *) node)),
+						  false);
 
 	switch (node->join.jointype)
 	{
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index a10e8d4ed96..a39128ff2f0 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.26 2001/01/24 19:42:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.27 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,9 +77,8 @@ SeqNext(SeqScan *node)
 		if (estate->es_evTupleNull[node->scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		/* probably ought to use ExecStoreTuple here... */
-		slot->val = estate->es_evTuple[node->scanrelid - 1];
-		slot->ttc_shouldFree = false;
+		ExecStoreTuple(estate->es_evTuple[node->scanrelid - 1],
+					   slot, InvalidBuffer, false);
 
 		/*
 		 * Note that unlike IndexScan, SeqScan never use keys in
@@ -181,7 +180,7 @@ InitScanRelation(SeqScan *node, EState *estate,
 	scanstate->css_currentRelation = currentRelation;
 	scanstate->css_currentScanDesc = currentScanDesc;
 
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
+	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
 
 	return reloid;
 }
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 99bd3b76437..b8c057c3397 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.30 2001/01/24 19:42:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.31 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -172,7 +172,6 @@ ExecSort(Sort *node)
 				break;
 
 			tuplesort_puttuple(tuplesortstate, (void *) slot->val);
-			ExecClearTuple(slot);
 		}
 
 		/* ----------------
@@ -188,11 +187,10 @@ ExecSort(Sort *node)
 		estate->es_direction = dir;
 
 		/* ----------------
-		 *	make sure the tuple descriptor is up to date
+		 *	make sure the tuple descriptor is up to date (is this needed?)
 		 * ----------------
 		 */
-		slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot;
-		slot->ttc_tupleDescriptor = tupDesc;
+		ExecAssignResultType(&sortstate->csstate.cstate, tupDesc, false);
 
 		/* ----------------
 		 *	finally set the sorted flag to true
@@ -201,8 +199,6 @@ ExecSort(Sort *node)
 		sortstate->sort_Done = true;
 		SO1_printf(stderr, "ExecSort: sorting done.\n");
 	}
-	else
-		slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot;
 
 	SO1_printf("ExecSort: %s\n",
 			   "retrieving tuple from tuplesort");
@@ -216,6 +212,7 @@ ExecSort(Sort *node)
 									   ScanDirectionIsForward(dir),
 									   &should_free);
 
+	slot = sortstate->csstate.cstate.cs_ResultTupleSlot;
 	return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
 }
 
@@ -347,6 +344,12 @@ ExecEndSort(Sort *node)
 		tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate);
 	sortstate->tuplesortstate = NULL;
 
+	if (sortstate->sort_Keys != NULL)
+		pfree(sortstate->sort_Keys);
+
+	pfree(sortstate);
+	node->sortstate = NULL;
+
 	SO1_printf("ExecEndSort: %s\n",
 			   "sort node shutdown");
 }
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 617a41d447d..4c9144bc3a8 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.3 2001/01/24 19:42:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.4 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,9 +77,8 @@ SubqueryNext(SubqueryScan *node)
 		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		/* probably ought to use ExecStoreTuple here... */
-		slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
-		slot->ttc_shouldFree = false;
+		ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
+					   slot, InvalidBuffer, false);
 
 		/* Flag for the next call that no more tuples */
 		estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 49f7594c6f7..a5c0299d289 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.13 2001/01/24 19:42:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.14 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,9 +110,8 @@ TidNext(TidScan *node)
 		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		/* probably ought to use ExecStoreTuple here... */
-		slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
-		slot->ttc_shouldFree = false;
+		ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
+					   slot, InvalidBuffer, false);
 
 		/* Flag for the next call that no more tuples */
 		estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
@@ -487,7 +486,7 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	 *	get the scan type from the relation descriptor.
 	 * ----------------
 	 */
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
+	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
 	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
 
 	/*
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 7808f297649..6dbcc701290 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -78,7 +78,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.12 2001/01/24 19:43:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.13 2001/01/29 00:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -546,6 +546,7 @@ tuplesort_end(Tuplesortstate *state)
 	}
 	if (state->memtupindex)
 		pfree(state->memtupindex);
+	pfree(state);
 }
 
 /*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 0282e7e7140..091841a9c1c 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.55 2001/01/24 19:43:23 momjian Exp $
+ * $Id: executor.h,v 1.56 2001/01/29 00:39:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ extern void ExecRestrPos(Plan *node);
  * prototypes from functions in execJunk.c
  */
 extern JunkFilter *ExecInitJunkFilter(List *targetList, TupleDesc tupType);
+extern void ExecFreeJunkFilter(JunkFilter *junkfilter);
 extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
 					 char *attrName, Datum *value, bool *isNull);
 extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
@@ -68,6 +69,7 @@ extern bool ExecInitNode(Plan *node, EState *estate, Plan *parent);
 extern TupleTableSlot *ExecProcNode(Plan *node, Plan *parent);
 extern int	ExecCountSlotsNode(Plan *node);
 extern void ExecEndNode(Plan *node, Plan *parent);
+extern TupleDesc ExecGetTupType(Plan *node);
 
 /*
  * prototypes from functions in execQual.c
@@ -106,13 +108,14 @@ extern TupleTableSlot *ExecScan(Scan *node, ExecScanAccessMtd accessMtd);
 extern TupleTable ExecCreateTupleTable(int initialSize);
 extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
 extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
+extern TupleTableSlot *MakeTupleTableSlot(void);
 extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 			   TupleTableSlot *slot,
 			   Buffer buffer,
 			   bool shouldFree);
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
-extern TupleDesc ExecSetSlotDescriptor(TupleTableSlot *slot,
-					  TupleDesc tupdesc);
+extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
+								  TupleDesc tupdesc, bool shouldFree);
 extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
 extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
 extern void ExecInitScanTupleSlot(EState *estate,
@@ -120,8 +123,6 @@ extern void ExecInitScanTupleSlot(EState *estate,
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
 extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
 											 TupleDesc tupType);
-
-extern TupleDesc ExecGetTupType(Plan *node);
 extern TupleDesc ExecTypeFromTL(List *targetList);
 extern void SetChangedParamList(Plan *node, List *newchg);
 
@@ -131,7 +132,7 @@ extern void SetChangedParamList(Plan *node, List *newchg);
 extern void ResetTupleCount(void);
 extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
 extern void ExecAssignResultType(CommonState *commonstate,
-					 TupleDesc tupDesc);
+								 TupleDesc tupDesc, bool shouldFree);
 extern void ExecAssignResultTypeFromOuterPlan(Plan *node,
 								  CommonState *commonstate);
 extern void ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate);
@@ -141,7 +142,7 @@ extern void ExecFreeProjectionInfo(CommonState *commonstate);
 extern void ExecFreeExprContext(CommonState *commonstate);
 extern TupleDesc ExecGetScanType(CommonScanState *csstate);
 extern void ExecAssignScanType(CommonScanState *csstate,
-				   TupleDesc tupDesc);
+							   TupleDesc tupDesc, bool shouldFree);
 extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
 								CommonScanState *csstate);
 extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 47018666a78..7def0be4b69 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tuptable.h,v 1.17 2001/01/24 19:43:23 momjian Exp $
+ * $Id: tuptable.h,v 1.18 2001/01/29 00:39:20 tgl Exp $
  *
  * NOTES
  *	  The tuple table interface is getting pretty ugly.
@@ -22,27 +22,36 @@
 
 /* ----------------
  *		The executor tuple table is managed and manipulated by special
- *		code in executor/execTuples.c and tupTable.h
+ *		code in executor/execTuples.c.
  *
  *		TupleTableSlot information
  *
- *			shouldFree			boolean - should we call pfree() on tuple
+ *			val					current tuple, or NULL if no tuple
+ *			shouldFree			boolean - should we pfree() tuple
  *			descIsNew			boolean - true when tupleDescriptor changes
- *			tupleDescriptor		type information kept regarding the tuple data
+ *			tupleDescriptor		type information for the tuple data
+ *			shouldFreeDesc		boolean - should we free tupleDescriptor
  *			buffer				the buffer for tuples pointing to disk pages
  *
  *		The executor stores pointers to tuples in a ``tuple table''
- *		which is composed of TupleTableSlot's.  Some of the tuples
- *		are pointers to buffer pages and others are pointers to
- *		palloc'ed memory and the shouldFree variable tells us when
+ *		which is composed of TupleTableSlots.  Sometimes the tuples
+ *		are pointers to buffer pages, while others are pointers to
+ *		palloc'ed memory; the shouldFree variable tells us when
  *		we may call pfree() on a tuple.  -cim 9/23/90
  *
+ *		If buffer is not InvalidBuffer, then the slot is holding a pin
+ *		on the indicated buffer page; drop the pin when we release the
+ *		slot's reference to that buffer.
+ *
  *		In the implementation of nested-dot queries such as
  *		"retrieve (EMP.hobbies.all)", a single scan may return tuples
  *		of many types, so now we return pointers to tuple descriptors
  *		along with tuples returned via the tuple table.  -cim 1/18/90
  *
- *		Tuple table macros are all excised from the system now.
+ *		shouldFreeDesc is similar to shouldFree: if it's true, then the
+ *		tupleDescriptor is "owned" by the TupleTableSlot and should be
+ *		freed when the slot's reference to the descriptor is dropped.
+ *
  *		See executor.h for decls of functions defined in execTuples.c
  *		-jolly
  *
@@ -54,6 +63,7 @@ typedef struct TupleTableSlot
 	HeapTuple	val;
 	bool		ttc_shouldFree;
 	bool		ttc_descIsNew;
+	bool		ttc_shouldFreeDesc;
 	TupleDesc	ttc_tupleDescriptor;
 	Buffer		ttc_buffer;
 } TupleTableSlot;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5552210de81..b9c10e6b310 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.55 2001/01/24 19:43:25 momjian Exp $
+ * $Id: execnodes.h,v 1.56 2001/01/29 00:39:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -164,11 +164,19 @@ typedef struct ProjectionInfo
  *						(including the junk attributes).
  *	  cleanTargetList:	the "clean" target list (junk attributes removed).
  *	  cleanLength:		the length of 'cleanTargetList'
- *	  cleanTupTyp:		the tuple descriptor of the "clean" tuple (with
+ *	  cleanTupType:		the tuple descriptor of the "clean" tuple (with
  *						junk attributes removed).
- *	  cleanMap:			A map with the correspondance between the non junk
+ *	  cleanMap:			A map with the correspondance between the non-junk
  *						attributes of the "original" tuple and the
  *						attributes of the "clean" tuple.
+ *	  junkContext:		memory context holding the JunkFilter node and all
+ *						its subsidiary data structures.
+ *
+ * NOTE: the original targetList and tupType are passed to ExecInitJunkFilter
+ * and do not belong to the JunkFilter.  All the other subsidiary structures
+ * are created during ExecInitJunkFilter, and all of them can be freed by
+ * deleting the memory context junkContext.  This would not be needed if we
+ * had a cleaner approach to managing query-lifetime data structures...
  * ----------------
  */
 typedef struct JunkFilter
@@ -181,6 +189,7 @@ typedef struct JunkFilter
 	int			jf_cleanLength;
 	TupleDesc	jf_cleanTupType;
 	AttrNumber *jf_cleanMap;
+	MemoryContext jf_junkContext;
 } JunkFilter;
 
 /* ----------------
-- 
GitLab