From badce86a2c327b40c6146242526d1523455d64a6 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 12 Jul 2000 02:37:39 +0000
Subject: [PATCH] First stage of reclaiming memory in executor by resetting
 short-term memory contexts.  Currently, only leaks in expressions executed as
 quals or projections are handled.  Clean up some old dead cruft in executor
 while at it --- unused fields in state nodes, that sort of thing.

---
 src/backend/access/gist/gist.c          |  19 +-
 src/backend/access/hash/hash.c          |  24 +-
 src/backend/access/nbtree/nbtcompare.c  |  33 ++-
 src/backend/access/nbtree/nbtree.c      |  19 +-
 src/backend/access/rtree/rtree.c        |  20 +-
 src/backend/catalog/index.c             |  40 +--
 src/backend/commands/copy.c             |  40 +--
 src/backend/executor/execAmi.c          |  19 +-
 src/backend/executor/execMain.c         |  96 +++----
 src/backend/executor/execQual.c         | 276 +++++++++---------
 src/backend/executor/execScan.c         |  79 +++---
 src/backend/executor/execTuples.c       |  33 +--
 src/backend/executor/execUtils.c        | 125 ++++++---
 src/backend/executor/functions.c        |  28 +-
 src/backend/executor/nodeAgg.c          | 359 ++++++++++++++++--------
 src/backend/executor/nodeAppend.c       |  12 +-
 src/backend/executor/nodeGroup.c        |  63 +++--
 src/backend/executor/nodeHash.c         |  60 ++--
 src/backend/executor/nodeHashjoin.c     |  47 ++--
 src/backend/executor/nodeIndexscan.c    | 182 ++++++------
 src/backend/executor/nodeMaterial.c     |  11 +-
 src/backend/executor/nodeMergejoin.c    | 110 +++++---
 src/backend/executor/nodeNestloop.c     |  61 ++--
 src/backend/executor/nodeResult.c       | 117 ++++----
 src/backend/executor/nodeSeqscan.c      | 211 ++++----------
 src/backend/executor/nodeSort.c         |   8 +-
 src/backend/executor/nodeSubplan.c      |  85 ++++--
 src/backend/executor/nodeTidscan.c      |  55 +---
 src/backend/executor/nodeUnique.c       |  38 ++-
 src/backend/nodes/copyfuncs.c           |   4 +-
 src/backend/nodes/equalfuncs.c          |   6 +-
 src/backend/nodes/outfuncs.c            |  21 +-
 src/backend/nodes/readfuncs.c           |   4 +-
 src/backend/optimizer/plan/createplan.c |  10 +-
 src/backend/optimizer/plan/subselect.c  |   4 +-
 src/backend/optimizer/util/clauses.c    |  30 +-
 src/backend/tcop/pquery.c               |   3 +-
 src/backend/utils/adt/datum.c           | 135 ++++-----
 src/backend/utils/adt/varlena.c         | 108 ++++---
 src/backend/utils/cache/fcache.c        |  15 +-
 src/backend/utils/mmgr/aset.c           |  13 +-
 src/include/catalog/index.h             |   5 +-
 src/include/executor/executor.h         |  31 +-
 src/include/executor/hashjoin.h         |  14 +-
 src/include/executor/nodeGroup.h        |   5 +-
 src/include/executor/nodeHash.h         |  12 +-
 src/include/executor/nodeSubplan.h      |   2 +-
 src/include/fmgr.h                      |  17 +-
 src/include/nodes/execnodes.h           | 271 +++++-------------
 src/include/nodes/plannodes.h           |   6 +-
 src/include/utils/datum.h               |  63 ++---
 src/include/utils/fcache.h              |  13 +-
 src/pl/plpgsql/src/pl_exec.c            |  32 ++-
 53 files changed, 1523 insertions(+), 1571 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 794685da2e1..28c547ffe69 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.60 2000/07/03 23:09:11 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.61 2000/07/12 02:36:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,11 +141,10 @@ gistbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		econtext = makeNode(ExprContext);
-		FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
+		ExecSetSlotDescriptor(slot, hd);
+		econtext = MakeExprContext(slot, TransactionCommandContext);
 	}
 	else
-/* shut the compiler up */
 	{
 		tupleTable = NULL;
 		slot = NULL;
@@ -161,13 +160,13 @@ gistbuild(PG_FUNCTION_ARGS)
 	{
 		nh++;
 
+#ifndef OMIT_PARTIAL_INDEX
 		/*
 		 * If oldPred != NULL, this is an EXTEND INDEX command, so skip
 		 * this tuple if it was already in the existing partial index
 		 */
 		if (oldPred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (ExecQual((List *) oldPred, econtext, false))
@@ -175,7 +174,6 @@ gistbuild(PG_FUNCTION_ARGS)
 				ni++;
 				continue;
 			}
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
 
 		/*
@@ -184,13 +182,12 @@ gistbuild(PG_FUNCTION_ARGS)
 		 */
 		if (pred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (!ExecQual((List *) pred, econtext, false))
 				continue;
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 		ni++;
 
@@ -262,13 +259,13 @@ gistbuild(PG_FUNCTION_ARGS)
 	/* okay, all heap tuples are indexed */
 	heap_endscan(scan);
 
+#ifndef OMIT_PARTIAL_INDEX
 	if (pred != NULL || oldPred != NULL)
 	{
-#ifndef OMIT_PARTIAL_INDEX
 		ExecDropTupleTable(tupleTable, true);
-		pfree(econtext);
-#endif	 /* OMIT_PARTIAL_INDEX */
+		FreeExprContext(econtext);
 	}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 	/*
 	 * Since we just counted the tuples in the heap, we update its stats
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 9102b75f61b..354d4985723 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.40 2000/06/17 23:41:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.41 2000/07/12 02:36:46 tgl Exp $
  *
  * NOTES
  *	  This file contains only the public interface routines.
@@ -102,15 +102,14 @@ hashbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		econtext = makeNode(ExprContext);
-		FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
+		ExecSetSlotDescriptor(slot, htupdesc);
+		econtext = MakeExprContext(slot, TransactionCommandContext);
 	}
 	else
-/* quiet the compiler */
 	{
+		tupleTable = NULL;
+		slot = NULL;
 		econtext = NULL;
-		tupleTable = 0;
-		slot = 0;
 	}
 #endif	 /* OMIT_PARTIAL_INDEX */
 
@@ -122,9 +121,9 @@ hashbuild(PG_FUNCTION_ARGS)
 
 	while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
 	{
-
 		nhtups++;
 
+#ifndef OMIT_PARTIAL_INDEX
 		/*
 		 * If oldPred != NULL, this is an EXTEND INDEX command, so skip
 		 * this tuple if it was already in the existing partial index
@@ -132,14 +131,12 @@ hashbuild(PG_FUNCTION_ARGS)
 		if (oldPred != NULL)
 		{
 			/* SetSlotContents(slot, htup); */
-#ifndef OMIT_PARTIAL_INDEX
 			slot->val = htup;
 			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				nitups++;
 				continue;
 			}
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
 
 		/*
@@ -148,13 +145,12 @@ hashbuild(PG_FUNCTION_ARGS)
 		 */
 		if (pred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (!ExecQual((List *) pred, econtext, false))
 				continue;
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 		nitups++;
 
@@ -221,13 +217,13 @@ hashbuild(PG_FUNCTION_ARGS)
 	/* okay, all heap tuples are indexed */
 	heap_endscan(hscan);
 
+#ifndef OMIT_PARTIAL_INDEX
 	if (pred != NULL || oldPred != NULL)
 	{
-#ifndef OMIT_PARTIAL_INDEX
 		ExecDropTupleTable(tupleTable, true);
-		pfree(econtext);
-#endif	 /* OMIT_PARTIAL_INDEX */
+		FreeExprContext(econtext);
 	}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 	/*
 	 * Since we just counted the tuples in the heap, we update its stats
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 73f52cb861c..411564fb8db 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.38 2000/06/19 03:54:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.39 2000/07/12 02:36:48 tgl Exp $
  *
  * NOTES
  *
@@ -27,6 +27,10 @@
  *	that work on 32-bit or wider datatypes can't just return "a - b".
  *	That could overflow and give the wrong answer.
  *
+ *	NOTE: these routines must not leak memory, since memory allocated
+ *	during an index access won't be recovered till end of query.  This
+ *	primarily affects comparison routines for toastable datatypes;
+ *	they have to be careful to free any detoasted copy of an input datum.
  *-------------------------------------------------------------------------
  */
 
@@ -230,18 +234,23 @@ bttextcmp(PG_FUNCTION_ARGS)
 		} while (res == 0 && len != 0);
 	}
 
+	if (res == 0 && VARSIZE(a) != VARSIZE(b))
+	{
+		/*
+		 * The two strings are the same in the first len bytes,
+		 * and they are of different lengths.
+		 */
+		if (VARSIZE(a) < VARSIZE(b))
+			res = -1;
+		else
+			res = 1;
+	}
+
 #endif
 
-	if (res != 0 || VARSIZE(a) == VARSIZE(b))
-		PG_RETURN_INT32(res);
+	/* Avoid leaking memory when handed toasted input. */
+	PG_FREE_IF_COPY(a, 0);
+	PG_FREE_IF_COPY(b, 1);
 
-	/*
-	 * The two strings are the same in the first len bytes, and they are
-	 * of different lengths.
-	 */
-
-	if (VARSIZE(a) < VARSIZE(b))
-		PG_RETURN_INT32(-1);
-	else
-		PG_RETURN_INT32(1);
+	PG_RETURN_INT32(res);
 }
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 59423ccb5f0..3d8ea1a70a8 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.59 2000/06/17 23:41:16 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.60 2000/07/12 02:36:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -121,8 +121,8 @@ btbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		econtext = makeNode(ExprContext);
-		FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
+		ExecSetSlotDescriptor(slot, htupdesc);
+		econtext = MakeExprContext(slot, TransactionCommandContext);
 
 		/*
 		 * we never want to use sort/build if we are extending an existing
@@ -151,14 +151,13 @@ btbuild(PG_FUNCTION_ARGS)
 	{
 		nhtups++;
 
+#ifndef OMIT_PARTIAL_INDEX
 		/*
 		 * If oldPred != NULL, this is an EXTEND INDEX command, so skip
 		 * this tuple if it was already in the existing partial index
 		 */
 		if (oldPred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
-
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (ExecQual((List *) oldPred, econtext, false))
@@ -166,7 +165,6 @@ btbuild(PG_FUNCTION_ARGS)
 				nitups++;
 				continue;
 			}
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
 
 		/*
@@ -175,13 +173,12 @@ btbuild(PG_FUNCTION_ARGS)
 		 */
 		if (pred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (!ExecQual((List *) pred, econtext, false))
 				continue;
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 		nitups++;
 
@@ -260,13 +257,13 @@ btbuild(PG_FUNCTION_ARGS)
 	/* okay, all heap tuples are indexed */
 	heap_endscan(hscan);
 
+#ifndef OMIT_PARTIAL_INDEX
 	if (pred != NULL || oldPred != NULL)
 	{
-#ifndef OMIT_PARTIAL_INDEX
 		ExecDropTupleTable(tupleTable, true);
-		pfree(econtext);
-#endif	 /* OMIT_PARTIAL_INDEX */
+		FreeExprContext(econtext);
 	}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 	/*
 	 * if we are doing bottom-up btree build, finish the build by (1)
diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c
index 7e84d456389..badff1ee21b 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.50 2000/06/17 23:41:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.51 2000/07/12 02:36:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,14 +136,14 @@ rtbuild(PG_FUNCTION_ARGS)
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		econtext = makeNode(ExprContext);
-		FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
+		ExecSetSlotDescriptor(slot, hd);
+		econtext = MakeExprContext(slot, TransactionCommandContext);
 	}
 	else
 	{
-		econtext = NULL;
 		tupleTable = NULL;
 		slot = NULL;
+		econtext = NULL;
 	}
 #endif	 /* OMIT_PARTIAL_INDEX */
 
@@ -156,13 +156,13 @@ rtbuild(PG_FUNCTION_ARGS)
 	{
 		nh++;
 
+#ifndef OMIT_PARTIAL_INDEX
 		/*
 		 * If oldPred != NULL, this is an EXTEND INDEX command, so skip
 		 * this tuple if it was already in the existing partial index
 		 */
 		if (oldPred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (ExecQual((List *) oldPred, econtext, false))
@@ -170,7 +170,6 @@ rtbuild(PG_FUNCTION_ARGS)
 				ni++;
 				continue;
 			}
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
 
 		/*
@@ -179,13 +178,12 @@ rtbuild(PG_FUNCTION_ARGS)
 		 */
 		if (pred != NULL)
 		{
-#ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
 			if (!ExecQual((List *) pred, econtext, false))
 				continue;
-#endif	 /* OMIT_PARTIAL_INDEX */
 		}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 		ni++;
 
@@ -239,13 +237,13 @@ rtbuild(PG_FUNCTION_ARGS)
 	/* okay, all heap tuples are indexed */
 	heap_endscan(scan);
 
+#ifndef OMIT_PARTIAL_INDEX
 	if (pred != NULL || oldPred != NULL)
 	{
-#ifndef OMIT_PARTIAL_INDEX
 		ExecDropTupleTable(tupleTable, true);
-		pfree(econtext);
-#endif	 /* OMIT_PARTIAL_INDEX */
+		FreeExprContext(econtext);
 	}
+#endif	 /* OMIT_PARTIAL_INDEX */
 
 	/*
 	 * Since we just counted the tuples in the heap, we update its stats
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index e1909e4404c..41d747b0dbf 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.124 2000/07/05 23:11:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.125 2000/07/12 02:36:55 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1670,34 +1670,6 @@ UpdateStats(Oid relid, long reltuples, bool inplace)
 }
 
 
-/* -------------------------
- *		FillDummyExprContext
- *			Sets up dummy ExprContext and TupleTableSlot objects for use
- *			with ExecQual.
- *
- *			NOTE: buffer is passed for historical reasons; it should
- *			almost certainly always be InvalidBuffer.
- * -------------------------
- */
-void
-FillDummyExprContext(ExprContext *econtext,
-					 TupleTableSlot *slot,
-					 TupleDesc tupdesc,
-					 Buffer buffer)
-{
-	econtext->ecxt_scantuple = slot;
-	econtext->ecxt_innertuple = NULL;
-	econtext->ecxt_outertuple = NULL;
-	econtext->ecxt_param_list_info = NULL;
-	econtext->ecxt_range_table = NULL;
-
-	slot->ttc_tupleDescriptor = tupdesc;
-	slot->ttc_buffer = buffer;
-	slot->ttc_shouldFree = false;
-
-}
-
-
 /* ----------------
  *		DefaultBuild
  *
@@ -1777,14 +1749,14 @@ DefaultBuild(Relation heapRelation,
 	{
 		tupleTable = ExecCreateTupleTable(1);
 		slot = ExecAllocTableSlot(tupleTable);
-		econtext = makeNode(ExprContext);
-		FillDummyExprContext(econtext, slot, heapDescriptor, InvalidBuffer);
+		ExecSetSlotDescriptor(slot, heapDescriptor);
+		econtext = MakeExprContext(slot, TransactionCommandContext);
 	}
 	else
 	{
-		econtext = NULL;
-		tupleTable = 0;
+		tupleTable = NULL;
 		slot = NULL;
+		econtext = NULL;
 	}
 #endif	 /* OMIT_PARTIAL_INDEX */
 
@@ -1812,7 +1784,6 @@ DefaultBuild(Relation heapRelation,
 		reltuples++;
 
 #ifndef OMIT_PARTIAL_INDEX
-
 		/*
 		 * If oldPred != NULL, this is an EXTEND INDEX command, so skip
 		 * this tuple if it was already in the existing partial index
@@ -1877,6 +1848,7 @@ DefaultBuild(Relation heapRelation,
 	{
 		/* parameter was 'false', almost certainly wrong --- tgl 9/21/99 */
 		ExecDropTupleTable(tupleTable, true);
+		FreeExprContext(econtext);
 	}
 #endif	 /* OMIT_PARTIAL_INDEX */
 
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 441941762ea..355b218e640 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.117 2000/07/05 23:11:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.118 2000/07/12 02:36:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -611,13 +611,11 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 	char	   *predString;
 	Node	  **indexPred = NULL;
 	TupleDesc	rtupdesc;
-	ExprContext *econtext = NULL;
 	EState	   *estate = makeNode(EState);		/* for ExecConstraints() */
-
 #ifndef OMIT_PARTIAL_INDEX
+	ExprContext *econtext = NULL;
 	TupleTable	tupleTable;
 	TupleTableSlot *slot = NULL;
-
 #endif
 	int			natts;
 	AttrNumber *attnumP;
@@ -651,7 +649,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 			finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo));
 			finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
 			indexPred = (Node **) palloc(n_indices * sizeof(Node *));
-			econtext = NULL;
 			for (i = 0; i < n_indices; i++)
 			{
 				itupdescArr[i] = RelationGetDescr(index_rels[i]);
@@ -680,36 +677,18 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 									PointerGetDatum(&pgIndexP[i]->indpred)));
 					indexPred[i] = stringToNode(predString);
 					pfree(predString);
+#ifndef OMIT_PARTIAL_INDEX
 					/* make dummy ExprContext for use by ExecQual */
 					if (econtext == NULL)
 					{
-#ifndef OMIT_PARTIAL_INDEX
 						tupleTable = ExecCreateTupleTable(1);
 						slot = ExecAllocTableSlot(tupleTable);
-						econtext = makeNode(ExprContext);
-						econtext->ecxt_scantuple = slot;
 						rtupdesc = RelationGetDescr(rel);
-						slot->ttc_tupleDescriptor = rtupdesc;
-
-						/*
-						 * There's no buffer associated with heap tuples
-						 * here, so I set the slot's buffer to NULL.
-						 * Currently, it appears that the only way a
-						 * buffer could be needed would be if the partial
-						 * index predicate referred to the "lock" system
-						 * attribute.  If it did, then heap_getattr would
-						 * call HeapTupleGetRuleLock, which uses the
-						 * buffer's descriptor to get the relation id.
-						 * Rather than try to fix this, I'll just disallow
-						 * partial indexes on "lock", which wouldn't be
-						 * useful anyway. --Nels, Nov '92
-						 */
-						/* SetSlotBuffer(slot, (Buffer) NULL); */
-						/* SetSlotShouldFree(slot, false); */
-						slot->ttc_buffer = (Buffer) NULL;
-						slot->ttc_shouldFree = false;
-#endif	 /* OMIT_PARTIAL_INDEX */
+						ExecSetSlotDescriptor(slot, rtupdesc);
+						econtext = MakeExprContext(slot,
+												   TransactionCommandContext);
 					}
+#endif	 /* OMIT_PARTIAL_INDEX */
 				}
 				else
 					indexPred[i] = NULL;
@@ -927,10 +906,9 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 			{
 				for (i = 0; i < n_indices; i++)
 				{
+#ifndef OMIT_PARTIAL_INDEX
 					if (indexPred[i] != NULL)
 					{
-#ifndef OMIT_PARTIAL_INDEX
-
 						/*
 						 * if tuple doesn't satisfy predicate, don't
 						 * update index
@@ -939,8 +917,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 						/* SetSlotContents(slot, tuple); */
 						if (!ExecQual((List *) indexPred[i], econtext, false))
 							continue;
-#endif	 /* OMIT_PARTIAL_INDEX */
 					}
+#endif	 /* OMIT_PARTIAL_INDEX */
 					FormIndexDatum(indexNatts[i],
 								(AttrNumber *) &(pgIndexP[i]->indkey[0]),
 								   tuple,
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index ff3fa0b6ed1..10478f60ad6 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: execAmi.c,v 1.48 2000/06/18 22:44:03 tgl Exp $
+ *	$Id: execAmi.c,v 1.49 2000/07/12 02:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,8 +171,8 @@ ExecBeginScan(Relation relation,
 /* ----------------------------------------------------------------
  *		ExecCloseR
  *
- *		closes the relation and scan descriptor for a scan or sort
- *		node.  Also closes index relations and scans for index scans.
+ *		closes the relation and scan descriptor for a scan node.
+ *		Also closes index relations and scans for index scans.
  * ----------------------------------------------------------------
  */
 void
@@ -197,20 +197,12 @@ ExecCloseR(Plan *node)
 			state = ((IndexScan *) node)->scan.scanstate;
 			break;
 
-		case T_Sort:
-			state = &(((Sort *) node)->sortstate->csstate);
-			break;
-
-		case T_Agg:
-			state = &(((Agg *) node)->aggstate->csstate);
-			break;
-
 		case T_TidScan:
 			state = ((TidScan *) node)->scan.scanstate;
 			break;
 
 		default:
-			elog(DEBUG, "ExecCloseR: not a scan or sort node!");
+			elog(DEBUG, "ExecCloseR: not a scan node!");
 			return;
 	}
 
@@ -237,13 +229,12 @@ ExecCloseR(Plan *node)
 	if (IsA(node, IndexScan))
 	{
 		IndexScan  *iscan = (IndexScan *) node;
-		IndexScanState *indexstate;
+		IndexScanState *indexstate = iscan->indxstate;
 		int			numIndices;
 		RelationPtr indexRelationDescs;
 		IndexScanDescPtr indexScanDescs;
 		int			i;
 
-		indexstate = iscan->indxstate;
 		numIndices = indexstate->iss_NumIndices;
 		indexRelationDescs = indexstate->iss_RelationDescs;
 		indexScanDescs = indexstate->iss_ScanDescs;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 6f161d95c0f..3125bf175e9 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.121 2000/07/05 16:17:43 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.122 2000/07/12 02:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,9 +86,12 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
  *		This routine must be called at the beginning of any execution of any
  *		query plan
  *
- *		returns (AttrInfo*) which describes the attributes of the tuples to
+ *		returns a TupleDesc which describes the attributes of the tuples to
  *		be returned by the query.
  *
+ * NB: the CurrentMemoryContext when this is called must be the context
+ * to be used as the per-query context for the query plan.  ExecutorRun()
+ * and ExecutorEnd() must be called in this same memory context.
  * ----------------------------------------------------------------
  */
 TupleDesc
@@ -103,7 +106,8 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
 	{
 		estate->es_param_exec_vals = (ParamExecData *)
 			palloc(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
-		memset(estate->es_param_exec_vals, 0, queryDesc->plantree->nParamExec * sizeof(ParamExecData));
+		MemSet(estate->es_param_exec_vals, 0,
+			   queryDesc->plantree->nParamExec * sizeof(ParamExecData));
 	}
 
 	/*
@@ -151,7 +155,6 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
  *			 EXEC_RETONE: return one tuple but don't 'retrieve' it
  *						   used in postquel function processing
  *
- *
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
@@ -687,13 +690,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	 */
 	estate->es_range_table = rangeTable;
 
-	/*
-	 * initialize the BaseId counter so node base_id's are assigned
-	 * correctly.  Someday baseid's will have to be stored someplace other
-	 * than estate because they should be unique per query planned.
-	 */
-	estate->es_BaseId = 1;
-
 	/*
 	 * initialize result relation stuff
 	 */
@@ -793,7 +789,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	/*
 	 * initialize the private state information for all the nodes in the
 	 * query tree.	This opens files, allocates storage and leaves us
-	 * ready to start processing tuples..
+	 * ready to start processing tuples.
 	 */
 	ExecInitNode(plan, estate, NULL);
 
@@ -1589,7 +1585,7 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
 {
 	int			ndef = rel->rd_att->constr->num_defval;
 	AttrDefault *attrdef = rel->rd_att->constr->defval;
-	ExprContext *econtext = makeNode(ExprContext);
+	ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
 	HeapTuple	newtuple;
 	Node	   *expr;
 	bool		isnull;
@@ -1600,23 +1596,13 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
 	char	   *repl = NULL;
 	int			i;
 
-	econtext->ecxt_scantuple = NULL;	/* scan tuple slot */
-	econtext->ecxt_innertuple = NULL;	/* inner tuple slot */
-	econtext->ecxt_outertuple = NULL;	/* outer tuple slot */
-	econtext->ecxt_relation = NULL;		/* relation */
-	econtext->ecxt_relid = 0;	/* relid */
-	econtext->ecxt_param_list_info = NULL;		/* param list info */
-	econtext->ecxt_param_exec_vals = NULL;		/* exec param values */
-	econtext->ecxt_range_table = NULL;	/* range table */
 	for (i = 0; i < ndef; i++)
 	{
 		if (!heap_attisnull(tuple, attrdef[i].adnum))
 			continue;
 		expr = (Node *) stringToNode(attrdef[i].adbin);
 
-		val = ExecEvalExpr(expr, econtext, &isnull, &isdone);
-
-		pfree(expr);
+		val = ExecEvalExprSwitchContext(expr, econtext, &isnull, &isdone);
 
 		if (isnull)
 			continue;
@@ -1635,20 +1621,24 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
 
 	}
 
-	pfree(econtext);
-
 	if (repl == NULL)
-		return tuple;
+	{
+		/* no changes needed */
+		newtuple = tuple;
+	}
+	else
+	{
+		newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
 
-	newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
+		pfree(repl);
+		pfree(replNull);
+		pfree(replValue);
+		heap_freetuple(tuple);
+	}
 
-	pfree(repl);
-	heap_freetuple(tuple);
-	pfree(replNull);
-	pfree(replValue);
+	FreeMemoryContext(econtext);
 
 	return newtuple;
-
 }
 
 #endif
@@ -1658,9 +1648,10 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 {
 	int			ncheck = rel->rd_att->constr->num_check;
 	ConstrCheck *check = rel->rd_att->constr->check;
-	ExprContext *econtext = makeNode(ExprContext);
 	TupleTableSlot *slot = makeNode(TupleTableSlot);
 	RangeTblEntry *rte = makeNode(RangeTblEntry);
+	ExprContext *econtext = MakeExprContext(slot,
+											TransactionCommandContext);
 	List	   *rtlist;
 	List	   *qual;
 	int			i;
@@ -1677,17 +1668,21 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 	rte->relid = RelationGetRelid(rel);
 	/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
 	rtlist = lcons(rte, NIL);
-	econtext->ecxt_scantuple = slot;	/* scan tuple slot */
-	econtext->ecxt_innertuple = NULL;	/* inner tuple slot */
-	econtext->ecxt_outertuple = NULL;	/* outer tuple slot */
-	econtext->ecxt_relation = rel;		/* relation */
-	econtext->ecxt_relid = 0;	/* relid */
-	econtext->ecxt_param_list_info = NULL;		/* param list info */
-	econtext->ecxt_param_exec_vals = NULL;		/* exec param values */
-	econtext->ecxt_range_table = rtlist;		/* range table */
+	econtext->ecxt_range_table = rtlist; /* phony range table */
 
+	/*
+	 * Save the de-stringized constraint expressions in command-level
+	 * memory context.  XXX should build the above stuff there too,
+	 * instead of doing it over for each tuple.
+	 * XXX Is it sufficient to have just one es_result_relation_constraints
+	 * in an inherited insert/update?
+	 */
 	if (estate->es_result_relation_constraints == NULL)
 	{
+		MemoryContext oldContext;
+
+		oldContext = MemoryContextSwitchTo(TransactionCommandContext);
+
 		estate->es_result_relation_constraints =
 			(List **) palloc(ncheck * sizeof(List *));
 
@@ -1696,6 +1691,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 			qual = (List *) stringToNode(check[i].ccbin);
 			estate->es_result_relation_constraints[i] = qual;
 		}
+
+		MemoryContextSwitchTo(oldContext);
 	}
 
 	for (i = 0; i < ncheck; i++)
@@ -1714,16 +1711,15 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 	pfree(slot);
 	pfree(rte);
 	pfree(rtlist);
-	pfree(econtext);
 
-	return (char *) NULL;
+	FreeExprContext(econtext);
 
+	return (char *) NULL;
 }
 
 void
 ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
 {
-
 	Assert(rel->rd_att->constr);
 
 	if (rel->rd_att->constr->has_not_null)
@@ -1732,9 +1728,10 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
 
 		for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++)
 		{
-			if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk))
+			if (rel->rd_att->attrs[attrChk-1]->attnotnull &&
+				heap_attisnull(tuple, attrChk))
 				elog(ERROR, "%s: Fail to add null value in not null attribute %s",
-					 caller, NameStr(rel->rd_att->attrs[attrChk - 1]->attname));
+					 caller, NameStr(rel->rd_att->attrs[attrChk-1]->attname));
 		}
 	}
 
@@ -1743,10 +1740,9 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
 		char	   *failed;
 
 		if ((failed = ExecRelCheck(rel, tuple, estate)) != NULL)
-			elog(ERROR, "%s: rejected due to CHECK constraint %s", caller, failed);
+			elog(ERROR, "%s: rejected due to CHECK constraint %s",
+				 caller, failed);
 	}
-
-	return;
 }
 
 TupleTableSlot *
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 33bfa88734f..fd9d761ffc4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,15 +8,16 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.72 2000/06/15 04:09:50 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.73 2000/07/12 02:37:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  *	 INTERFACE ROUTINES
  *		ExecEvalExpr	- evaluate an expression and return a datum
+ *		ExecEvalExprSwitchContext - same, but switch into eval memory context
  *		ExecQual		- return true/false if qualification is satisfied
- *		ExecTargetList	- form a new tuple by projecting the given tuple
+ *		ExecProject		- form a new tuple by projecting the given tuple
  *
  *	 NOTES
  *		ExecEvalExpr() and ExecEvalVar() are hotspots.	making these faster
@@ -24,7 +25,7 @@
  *		implemented recursively.  Eliminating the recursion is bound to
  *		improve the speed of the executor.
  *
- *		ExecTargetList() is used to make tuple projections.  Rather then
+ *		ExecProject() is used to make tuple projections.  Rather then
  *		trying to speed it up, the execution plan should be pre-processed
  *		to facilitate attribute sharing between nodes wherever possible,
  *		instead of doing needless copying.	-cim 5/31/91
@@ -44,31 +45,19 @@
 #include "utils/fcache2.h"
 
 
-/*
- *		externs and constants
- */
-
-/*
- * XXX Used so we can get rid of use of Const nodes in the executor.
- * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
- * and by ExecEvalArrayRef.
- */
-bool		execConstByVal;
-int			execConstLen;
-
-/* static functions decls */
+/* static function decls */
 static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
 				 bool *isNull, bool *isDone);
-static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
+			 bool *isNull);
 static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
 			 bool *isNull, bool *isDone);
 static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
 							 List *argList, FunctionCallInfo fcinfo,
 							 bool *argIsDone);
 static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
-			 bool *isNull);
+static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
 static Datum ExecMakeFunctionResult(Node *node, List *arguments,
@@ -100,10 +89,11 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 
 	if (arrayRef->refexpr != NULL)
 	{
-		array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
-												   econtext,
-												   isNull,
-												   isDone);
+		array_scanner = (ArrayType *)
+			DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
+										 econtext,
+										 isNull,
+										 isDone));
 		/* If refexpr yields NULL, result is always NULL, for now anyway */
 		if (*isNull)
 			return (Datum) NULL;
@@ -128,10 +118,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 			elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
 				 MAXDIM);
 
-		upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
-											   econtext,
-											   isNull,
-											   &dummy);
+		upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+													 econtext,
+													 isNull,
+													 &dummy));
 		/* If any index expr yields NULL, result is NULL */
 		if (*isNull)
 			return (Datum) NULL;
@@ -145,10 +135,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 				elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
 					 MAXDIM);
 
-			lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
-												   econtext,
-												   isNull,
-												   &dummy);
+			lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+														 econtext,
+														 isNull,
+														 &dummy));
 			/* If any index expr yields NULL, result is NULL */
 			if (*isNull)
 				return (Datum) NULL;
@@ -171,9 +161,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 		if (*isNull)
 			return (Datum) NULL;
 
-		execConstByVal = arrayRef->refelembyval;
-		execConstLen = arrayRef->refelemlength;
-
 		if (array_scanner == NULL)
 			return sourceData;	/* XXX do something else? */
 
@@ -199,9 +186,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 										   isNull));
 	}
 
-	execConstByVal = arrayRef->refelembyval;
-	execConstLen = arrayRef->refelemlength;
-
 	if (lIndex == NULL)
 		return array_ref(array_scanner, i,
 						 upper.indx,
@@ -325,7 +309,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 		ExecSetSlotDescriptor(tempSlot, td);
 
 		ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
-		return (Datum) tempSlot;
+		return PointerGetDatum(tempSlot);
 	}
 
 	result = heap_getattr(heapTuple,	/* tuple containing attribute */
@@ -338,7 +322,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 	 * return null if att is null
 	 */
 	if (*isNull)
-		return (Datum) NULL;
+		return (Datum) 0;
 
 	/*
 	 * get length and type information.. ??? what should we do about
@@ -364,9 +348,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 		byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false;
 	}
 
-	execConstByVal = byval;
-	execConstLen = len;
-
 	return result;
 }
 
@@ -397,7 +378,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
 Datum
 ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
 {
-
 	char	   *thisParameterName;
 	int			thisParameterKind = expression->paramkind;
 	AttrNumber	thisParameterId = expression->paramid;
@@ -406,11 +386,15 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
 
 	if (thisParameterKind == PARAM_EXEC)
 	{
-		ParamExecData *prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
+		ParamExecData *prm;
 
+		prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
 		if (prm->execPlan != NULL)
-			ExecSetParamPlan(prm->execPlan);
-		Assert(prm->execPlan == NULL);
+		{
+			ExecSetParamPlan(prm->execPlan, econtext);
+			/* ExecSetParamPlan should have processed this param... */
+			Assert(prm->execPlan == NULL);
+		}
 		*isNull = prm->isnull;
 		return prm->value;
 	}
@@ -493,7 +477,7 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
 	if (paramList->isnull)
 	{
 		*isNull = true;
-		return (Datum) NULL;
+		return (Datum) 0;
 	}
 
 	if (expression->param_tlist != NIL)
@@ -526,8 +510,12 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
  *		named attribute out of the tuple from the arg slot.  User defined
  *		C functions which take a tuple as an argument are expected
  *		to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
+ *
+ * XXX these two functions are misdeclared: they should be declared to
+ * return Datum.  They are not used anywhere in the backend proper, and
+ * exist only for use by user-defined functions.  Should we change their
+ * definitions, at risk of breaking user code?
  */
-/* static but gets called from external functions */
 char *
 GetAttributeByNum(TupleTableSlot *slot,
 				  AttrNumber attrno,
@@ -559,18 +547,6 @@ GetAttributeByNum(TupleTableSlot *slot,
 	return (char *) retval;
 }
 
-/* XXX name for catalogs */
-#ifdef NOT_USED
-char *
-att_by_num(TupleTableSlot *slot,
-		   AttrNumber attrno,
-		   bool *isNull)
-{
-	return GetAttributeByNum(slot, attrno, isNull);
-}
-
-#endif
-
 char *
 GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 {
@@ -617,15 +593,6 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 	return (char *) retval;
 }
 
-/* XXX name for catalogs */
-#ifdef NOT_USED
-char *
-att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
-{
-	return GetAttributeByName(slot, attname, isNull);
-}
-
-#endif
 
 static void
 ExecEvalFuncArgs(FunctionCachePtr fcache,
@@ -732,9 +699,8 @@ ExecMakeFunctionResult(Node *node,
 		if (fcache->hasSetArg && argDone)
 		{
 			/* can only get here if input is an empty set. */
-			if (isDone)
-				*isDone = true;
 			*isNull = true;
+			*isDone = true;
 			return (Datum) 0;
 		}
 	}
@@ -817,8 +783,8 @@ ExecMakeFunctionResult(Node *node,
 			else
 			{
 				result = (Datum) 0;
-				*isDone = true;
 				*isNull = true;
+				*isDone = true;
 			}
 
 			if (!*isDone)
@@ -872,8 +838,8 @@ ExecMakeFunctionResult(Node *node,
 	else
 	{
 		/* A non-SQL function cannot return a set, at present. */
-		if (isDone)
-			*isDone = true;
+		*isDone = true;
+
 		/*
 		 * If function is strict, and there are any NULL arguments,
 		 * skip calling the function and return NULL.
@@ -904,15 +870,7 @@ ExecMakeFunctionResult(Node *node,
  *		ExecEvalFunc
  *
  *		Evaluate the functional result of a list of arguments by calling the
- *		function manager.  Note that in the case of operator expressions, the
- *		optimizer had better have already replaced the operator OID with the
- *		appropriate function OID or we're hosed.
- *
- * old comments
- *		Presumably the function manager will not take null arguments, so we
- *		check for null arguments before sending the arguments to (fmgr).
- *
- *		Returns the value of the functional expression.
+ *		function manager.
  * ----------------------------------------------------------------
  */
 
@@ -929,8 +887,6 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
 	bool		isDone;
 
 	/*
-	 * an opclause is a list (op args).  (I think)
-	 *
 	 * we extract the oid of the function associated with the op and then
 	 * pass the work onto ExecMakeFunctionResult which evaluates the
 	 * arguments and returns the result of calling the function on the
@@ -954,7 +910,8 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
 	 * call ExecMakeFunctionResult() with a dummy isDone that we ignore.
 	 * We don't have operator whose arguments are sets.
 	 */
-	return ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone);
+	return ExecMakeFunctionResult((Node *) op, argList, econtext,
+								  isNull, &isDone);
 }
 
 /* ----------------------------------------------------------------
@@ -973,8 +930,6 @@ ExecEvalFunc(Expr *funcClause,
 	FunctionCachePtr fcache;
 
 	/*
-	 * an funcclause is a list (func args).  (I think)
-	 *
 	 * we extract the oid of the function associated with the func node and
 	 * then pass the work onto ExecMakeFunctionResult which evaluates the
 	 * arguments and returns the result of calling the function on the
@@ -996,7 +951,8 @@ ExecEvalFunc(Expr *funcClause,
 		fcache = func->func_fcache;
 	}
 
-	return ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone);
+	return ExecMakeFunctionResult((Node *) func, argList, econtext,
+								  isNull, isDone);
 }
 
 /* ----------------------------------------------------------------
@@ -1041,10 +997,7 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
 	 * evaluation of 'not' is simple.. expr is false, then return 'true'
 	 * and vice versa.
 	 */
-	if (DatumGetInt32(expr_value) == 0)
-		return (Datum) true;
-
-	return (Datum) false;
+	return BoolGetDatum(! DatumGetBool(expr_value));
 }
 
 /* ----------------------------------------------------------------
@@ -1094,13 +1047,13 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
 		 */
 		if (*isNull)
 			AnyNull = true;		/* remember we got a null */
-		else if (DatumGetInt32(clause_value) != 0)
+		else if (DatumGetBool(clause_value))
 			return clause_value;
 	}
 
 	/* AnyNull is true if at least one clause evaluated to NULL */
 	*isNull = AnyNull;
-	return (Datum) false;
+	return BoolGetDatum(false);
 }
 
 /* ----------------------------------------------------------------
@@ -1144,13 +1097,13 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
 		 */
 		if (*isNull)
 			AnyNull = true;		/* remember we got a null */
-		else if (DatumGetInt32(clause_value) == 0)
+		else if (! DatumGetBool(clause_value))
 			return clause_value;
 	}
 
 	/* AnyNull is true if at least one clause evaluated to NULL */
 	*isNull = AnyNull;
-	return (Datum) (!AnyNull);
+	return BoolGetDatum(!AnyNull);
 }
 
 /* ----------------------------------------------------------------
@@ -1195,7 +1148,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
 		 * case statement is satisfied.  A NULL result from the test is
 		 * not considered true.
 		 */
-		if (DatumGetInt32(clause_value) != 0 && !*isNull)
+		if (DatumGetBool(clause_value) && !*isNull)
 		{
 			return ExecEvalExpr(wclause->result,
 								econtext,
@@ -1221,19 +1174,15 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
  *
  *		Recursively evaluate a targetlist or qualification expression.
  *
- *		This routine is an inner loop routine and should be as fast
- *		as possible.
+ *		The caller should already have switched into the temporary
+ *		memory context econtext->ecxt_per_tuple_memory.  The convenience
+ *		entry point ExecEvalExprSwitchContext() is provided for callers
+ *		who don't prefer to do the switch in an outer loop.  We do not
+ *		do the switch here because it'd be a waste of cycles during
+ *		recursive entries to ExecEvalExpr().
  *
- *		Node comparison functions were replaced by macros for speed and to plug
- *		memory leaks incurred by using the planner's Lispy stuff for
- *		comparisons.  Order of evaluation of node comparisons IS IMPORTANT;
- *		the macros do no checks.  Order of evaluation:
- *
- *		o an isnull check, largely to avoid coredumps since greg doubts this
- *		  routine is called with a null ptr anyway in proper operation, but is
- *		  not completely sure...
- *		o ExactNodeType checks.
- *		o clause checks or other checks where we look at the lfirst of something.
+ *		This routine is an inner loop routine and must be as fast
+ *		as possible.
  * ----------------------------------------------------------------
  */
 Datum
@@ -1244,25 +1193,21 @@ ExecEvalExpr(Node *expression,
 {
 	Datum		retDatum;
 
+	/* Set default values for result flags: non-null, not a set result */
 	*isNull = false;
+	*isDone = true;
 
-	/*
-	 * Some callers don't care about is done and only want 1 result.  They
-	 * indicate this by passing NULL
-	 */
-	if (isDone)
-		*isDone = true;
-
-	/*
-	 * here we dispatch the work to the appropriate type of function given
-	 * the type of our expression.
-	 */
+	/* Is this still necessary?  Doubtful... */
 	if (expression == NULL)
 	{
 		*isNull = true;
-		return (Datum) true;
+		return (Datum) 0;
 	}
 
+	/*
+	 * here we dispatch the work to the appropriate type of function given
+	 * the type of our expression.
+	 */
 	switch (nodeTag(expression))
 	{
 		case T_Var:
@@ -1350,8 +1295,27 @@ ExecEvalExpr(Node *expression,
 }	/* ExecEvalExpr() */
 
 
+/*
+ * Same as above, but get into the right allocation context explicitly.
+ */
+Datum
+ExecEvalExprSwitchContext(Node *expression,
+						  ExprContext *econtext,
+						  bool *isNull,
+						  bool *isDone)
+{
+	Datum		retDatum;
+	MemoryContext oldContext;
+
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+	retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
+	MemoryContextSwitchTo(oldContext);
+	return retDatum;
+}
+
+
 /* ----------------------------------------------------------------
- *					 ExecQual / ExecTargetList
+ *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
@@ -1386,6 +1350,8 @@ ExecEvalExpr(Node *expression,
 bool
 ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 {
+	bool		result;
+	MemoryContext oldContext;
 	List	   *qlist;
 
 	/*
@@ -1397,6 +1363,11 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 
 	IncrProcessed();
 
+	/*
+	 * Run in short-lived per-tuple context while computing expressions.
+	 */
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
 	/*
 	 * Evaluate the qual conditions one at a time.	If we find a FALSE
 	 * result, we can stop evaluating and return FALSE --- the AND result
@@ -1409,6 +1380,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 	 * is NULL (one or more NULL subresult, with all the rest TRUE) and
 	 * the caller has specified resultForNull = TRUE.
 	 */
+	result = true;
 
 	foreach(qlist, qual)
 	{
@@ -1417,14 +1389,6 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 		bool		isNull;
 		bool		isDone;
 
-		/*
-		 * If there is a null clause, consider the qualification to fail.
-		 * XXX is this still correct for constraints?  It probably
-		 * shouldn't happen at all ...
-		 */
-		if (clause == NULL)
-			return false;
-
 		/*
 		 * pass isDone, but ignore it.	We don't iterate over multiple
 		 * returns in the qualifications.
@@ -1434,16 +1398,24 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 		if (isNull)
 		{
 			if (resultForNull == false)
-				return false;	/* treat NULL as FALSE */
+			{
+				result = false;	/* treat NULL as FALSE */
+				break;
+			}
 		}
 		else
 		{
-			if (DatumGetInt32(expr_value) == 0)
-				return false;	/* definitely FALSE */
+			if (! DatumGetBool(expr_value))
+			{
+				result = false;	/* definitely FALSE */
+				break;
+			}
 		}
 	}
 
-	return true;
+	MemoryContextSwitchTo(oldContext);
+
+	return result;
 }
 
 int
@@ -1481,6 +1453,7 @@ ExecTargetList(List *targetlist,
 			   ExprContext *econtext,
 			   bool *isDone)
 {
+	MemoryContext oldContext;
 	char		nulls_array[64];
 	bool		fjNullArray[64];
 	bool		itemIsDoneArray[64];
@@ -1506,6 +1479,11 @@ ExecTargetList(List *targetlist,
 	EV_nodeDisplay(targetlist);
 	EV_printf("\n");
 
+	/*
+	 * Run in short-lived per-tuple context while computing expressions.
+	 */
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
 	/*
 	 * There used to be some klugy and demonstrably broken code here that
 	 * special-cased the situation where targetlist == NIL.  Now we just
@@ -1563,10 +1541,10 @@ ExecTargetList(List *targetlist,
 			resdom = tle->resdom;
 			resind = resdom->resno - 1;
 
-			constvalue = (Datum) ExecEvalExpr(expr,
-											  econtext,
-											  &isNull,
-											  &itemIsDone[resind]);
+			constvalue = ExecEvalExpr(expr,
+									  econtext,
+									  &isNull,
+									  &itemIsDone[resind]);
 
 			values[resind] = constvalue;
 
@@ -1597,7 +1575,10 @@ ExecTargetList(List *targetlist,
 
 			/* this is probably wrong: */
 			if (*isDone)
-				return (HeapTuple) NULL;
+			{
+				newTuple = NULL;
+				goto exit;
+			}
 
 			/*
 			 * get the result from the inner node
@@ -1681,10 +1662,10 @@ ExecTargetList(List *targetlist,
 
 					if (IsA(expr, Iter) &&itemIsDone[resind])
 					{
-						constvalue = (Datum) ExecEvalExpr(expr,
-														  econtext,
-														  &isNull,
-													&itemIsDone[resind]);
+						constvalue = ExecEvalExpr(expr,
+												  econtext,
+												  &isNull,
+												  &itemIsDone[resind]);
 						if (itemIsDone[resind])
 						{
 
@@ -1710,8 +1691,10 @@ ExecTargetList(List *targetlist,
 	}
 
 	/*
-	 * form the new result tuple (in the "normal" context)
+	 * form the new result tuple (in the caller's memory context!)
 	 */
+	MemoryContextSwitchTo(oldContext);
+
 	newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
 
 exit:
@@ -1726,6 +1709,9 @@ exit:
 		pfree(itemIsDone);
 	}
 
+	/* make sure we are in the right context if we did "goto exit" */
+	MemoryContextSwitchTo(oldContext);
+
 	return newTuple;
 }
 
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 98345b408a3..40bbbd916f6 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.11 2000/01/26 05:56:21 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,7 @@
  *		returns the next qualifying tuple in the direction specified
  *		in the global variable ExecDirection.
  *		The access method returns the next tuple and execScan() is
- *		responisble for checking the tuple returned against the qual-clause.
+ *		responsible for checking the tuple returned against the qual-clause.
  *
  *		Conditions:
  *		  -- the "cursor" maintained by the AMI is positioned at the tuple
@@ -39,59 +39,50 @@
  *		Initial States:
  *		  -- the relation indicated is opened for scanning so that the
  *			 "cursor" is positioned before the first qualifying tuple.
- *
- *		May need to put startmmgr  and endmmgr in here.
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
 ExecScan(Scan *node,
-		 TupleTableSlot *(*accessMtd) ())		/* function returning a
-												 * tuple */
+		 ExecScanAccessMtd accessMtd) /* function returning a tuple */
 {
 	CommonScanState *scanstate;
 	EState	   *estate;
 	List	   *qual;
 	bool		isDone;
-
-	TupleTableSlot *slot;
 	TupleTableSlot *resultSlot;
-	HeapTuple	newTuple;
-
 	ExprContext *econtext;
 	ProjectionInfo *projInfo;
 
-
 	/* ----------------
-	 *	initialize misc variables
+	 *	Fetch data from node
 	 * ----------------
 	 */
-	newTuple = NULL;
-	slot = NULL;
-
 	estate = node->plan.state;
 	scanstate = node->scanstate;
+	econtext = scanstate->cstate.cs_ExprContext;
+	qual = node->plan.qual;
 
 	/* ----------------
-	 *	get the expression context
+	 *	Reset per-tuple memory context to free any expression evaluation
+	 *	storage allocated in the previous tuple cycle.
 	 * ----------------
 	 */
-	econtext = scanstate->cstate.cs_ExprContext;
+	ResetExprContext(econtext);
 
 	/* ----------------
-	 *	initialize fields in ExprContext which don't change
-	 *	in the course of the scan..
+	 *	Check to see if we're still projecting out tuples from a previous
+	 *	scan tuple (because there is a function-returning-set in the
+	 *	projection expressions).  If so, try to project another one.
 	 * ----------------
 	 */
-	qual = node->plan.qual;
-	econtext->ecxt_relation = scanstate->css_currentRelation;
-	econtext->ecxt_relid = node->scanrelid;
-
 	if (scanstate->cstate.cs_TupFromTlist)
 	{
 		projInfo = scanstate->cstate.cs_ProjInfo;
 		resultSlot = ExecProject(projInfo, &isDone);
 		if (!isDone)
 			return resultSlot;
+		/* Done with that source tuple... */
+		scanstate->cstate.cs_TupFromTlist = false;
 	}
 
 	/*
@@ -100,27 +91,23 @@ ExecScan(Scan *node,
 	 */
 	for (;;)
 	{
-		slot = (TupleTableSlot *) (*accessMtd) (node);
+		TupleTableSlot *slot;
+
+		slot = (*accessMtd) (node);
 
 		/* ----------------
 		 *	if the slot returned by the accessMtd contains
 		 *	NULL, then it means there is nothing more to scan
-		 *	so we just return the empty slot...
-		 *
-		 *	... with invalid TupleDesc (not the same as in
-		 *	projInfo->pi_slot) and break upper MergeJoin node.
-		 *	New code below do what ExecProject() does.	- vadim 02/26/98
+		 *	so we just return an empty slot, being careful to use
+		 *	the projection result slot so it has correct tupleDesc.
 		 * ----------------
 		 */
 		if (TupIsNull(slot))
 		{
-			scanstate->cstate.cs_TupFromTlist = false;
-			resultSlot = scanstate->cstate.cs_ProjInfo->pi_slot;
-			return (TupleTableSlot *)
-				ExecStoreTuple(NULL,
-							   resultSlot,
-							   InvalidBuffer,
-							   true);
+			return ExecStoreTuple(NULL,
+								  scanstate->cstate.cs_ProjInfo->pi_slot,
+								  InvalidBuffer,
+								  true);
 		}
 
 		/* ----------------
@@ -131,22 +118,28 @@ ExecScan(Scan *node,
 
 		/* ----------------
 		 *	check that the current tuple satisfies the qual-clause
-		 *	if our qualification succeeds then we
+		 *	if our qualification succeeds then we may
 		 *	leave the loop.
-		 * ----------------
-		 */
-
-		/*
-		 * add a check for non-nil qual here to avoid a function call to
+		 *
+		 * check for non-nil qual here to avoid a function call to
 		 * ExecQual() when the qual is nil ... saves only a few cycles,
 		 * but they add up ...
+		 * ----------------
 		 */
 		if (!qual || ExecQual(qual, econtext, false))
 			break;
+
+		/* ----------------
+		 *	Tuple fails qual, so free per-tuple memory and try again.
+		 * ----------------
+		 */
+		ResetExprContext(econtext);
 	}
 
 	/* ----------------
-	 *	form a projection tuple, store it in the result tuple
+	 *	Found a satisfactory scan tuple.
+	 *
+	 *	Form a projection tuple, store it in the result tuple
 	 *	slot and return it.
 	 * ----------------
 	 */
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 578b8b80681..37b092fc20f 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.37 2000/04/12 17:15:08 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.38 2000/07/12 02:37:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,8 +76,7 @@
  *		  by the access methods into the scan tuple slot.
  *
  *		- ExecSeqScan() calls ExecStoreTuple() to take the result
- *		  tuple from ExecTargetList() and place it into the result tuple
- *		  slot.
+ *		  tuple from ExecProject() and place it into the result tuple slot.
  *
  *		- ExecutePlan() calls ExecRetrieve() which gets the tuple out of
  *		  the slot passed to it by calling ExecFetchTuple().  this tuple
@@ -182,8 +181,8 @@ ExecCreateTupleTable(int initialSize)	/* initial number of slots in
 /* --------------------------------
  *		ExecDropTupleTable
  *
- *		This pfrees the storage assigned to the tuple table and
- *		optionally pfrees the contents of the table also.
+ *		This frees the storage used by the tuple table itself
+ *		and optionally frees the contents of the table also.
  *		It is expected that this routine be called by EndPlan().
  * --------------------------------
  */
@@ -239,7 +238,6 @@ ExecDropTupleTable(TupleTable table,	/* tuple table */
 	 */
 	pfree(array);
 	pfree(table);
-
 }
 
 
@@ -252,13 +250,12 @@ ExecDropTupleTable(TupleTable table,	/* tuple table */
  *
  *		This routine is used to reserve slots in the table for
  *		use by the various plan nodes.	It is expected to be
- *		called by the node init routines (ex: ExecInitNestLoop).
+ *		called by the node init routines (ex: ExecInitNestLoop)
  *		once per slot needed by the node.  Not all nodes need
  *		slots (some just pass tuples around).
  * --------------------------------
  */
-TupleTableSlot *				/* return: the slot allocated in the tuple
-								 * table */
+TupleTableSlot *
 ExecAllocTableSlot(TupleTable table)
 {
 	int			slotnum;		/* new slot number */
@@ -283,22 +280,12 @@ ExecAllocTableSlot(TupleTable table)
 	 *	pointers into _freed_ memory.  This leads to bad ends.	We
 	 *	now count the number of slots we will need and create all the
 	 *	slots we will need ahead of time.  The if below should never
-	 *	happen now.  Give a WARN if it does.  -mer 4 Aug 1992
+	 *	happen now.  Fail if it does.  -mer 4 Aug 1992
 	 * ----------------
 	 */
 	if (table->next >= table->size)
-	{
-
-		/*
-		 * int newsize = NewTableSize(table->size);
-		 *
-		 * pfree(table->array); table->array = (Pointer) palloc(newsize *
-		 * TableSlotSize); bzero(table->array, newsize * TableSlotSize);
-		 * table->size =  newsize;
-		 */
-		elog(NOTICE, "Plan requires more slots than are available");
-		elog(ERROR, "send mail to your local executor guru to fix this");
-	}
+		elog(ERROR, "Plan requires more slots than are available"
+			 "\n\tsend mail to your local executor guru to fix this");
 
 	/* ----------------
 	 *	at this point, space in the table is guaranteed so we
@@ -427,7 +414,7 @@ ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 
 	slot->val = (HeapTuple) NULL;
 
-	slot->ttc_shouldFree = true;/* probably useless code... */
+	slot->ttc_shouldFree = true;	/* probably useless code... */
 
 	/* ----------------
 	 *	Drop the pin on the referenced buffer, if there is one.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a023405a275..daa40dccf75 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1,22 +1,20 @@
 /*-------------------------------------------------------------------------
  *
  * execUtils.c
- *	  miscellanious executor utility routines
+ *	  miscellaneous executor utility routines
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.62 2000/07/05 23:11:14 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.63 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  * INTERFACE ROUTINES
- *		ExecAssignNodeBaseInfo	\
- *		ExecAssignDebugHooks	 >	preforms misc work done in all the
- *		ExecAssignExprContext	/	init node routines.
+ *		ExecAssignExprContext	Common code for plan node init routines.
  *
  *		ExecGetTypeInfo			  |  old execCStructs interface
  *		ExecMakeTypeInfo		  |  code from the version 1
@@ -53,6 +51,7 @@
 #include "miscadmin.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/memutils.h"
 #include "utils/relcache.h"
 #include "utils/syscache.h"
 
@@ -137,57 +136,104 @@ DisplayTupleCount(FILE *statfp)
 #endif
 
 /* ----------------------------------------------------------------
- *				 miscellanious init node support functions
+ *				 miscellaneous node-init support functions
  *
- *		ExecAssignNodeBaseInfo	- assigns the baseid field of the node
- *		ExecAssignDebugHooks	- assigns the node's debugging hooks
  *		ExecAssignExprContext	- assigns the node's expression context
  * ----------------------------------------------------------------
  */
 
 /* ----------------
- *		ExecAssignNodeBaseInfo
+ *		ExecAssignExprContext
+ *
+ *		This initializes the ExprContext field.  It is only necessary
+ *		to do this for nodes which use ExecQual or ExecProject
+ *		because those routines depend on econtext.	Other nodes that
+ *		don't have to evaluate expressions don't need to do this.
  *
- *		as it says, this assigns the baseid field of the node and
- *		increments the counter in the estate.  In addition, it initializes
- *		the base_parent field of the basenode.
+ * Note: we assume CurrentMemoryContext is the correct per-query context.
+ * This should be true during plan node initialization.
  * ----------------
  */
 void
-ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
+ExecAssignExprContext(EState *estate, CommonState *commonstate)
 {
-	int			baseId;
+	ExprContext *econtext = makeNode(ExprContext);
 
-	baseId = estate->es_BaseId;
-	cstate->cs_base_id = baseId;
-	estate->es_BaseId = baseId + 1;
+	econtext->ecxt_scantuple = NULL;
+	econtext->ecxt_innertuple = NULL;
+	econtext->ecxt_outertuple = NULL;
+	econtext->ecxt_per_query_memory = CurrentMemoryContext;
+	/*
+	 * Create working memory for expression evaluation in this context.
+	 */
+	econtext->ecxt_per_tuple_memory =
+		AllocSetContextCreate(CurrentMemoryContext,
+							  "PlanExprContext",
+							  ALLOCSET_DEFAULT_MINSIZE,
+							  ALLOCSET_DEFAULT_INITSIZE,
+							  ALLOCSET_DEFAULT_MAXSIZE);
+	econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
+	econtext->ecxt_param_list_info = estate->es_param_list_info;
+	econtext->ecxt_aggvalues = NULL;
+	econtext->ecxt_aggnulls = NULL;
+	econtext->ecxt_range_table = estate->es_range_table;
+
+	commonstate->cs_ExprContext = econtext;
 }
 
 /* ----------------
- *		ExecAssignExprContext
+ *		MakeExprContext
  *
- *		This initializes the ExprContext field.  It is only necessary
- *		to do this for nodes which use ExecQual or ExecTargetList
- *		because those routines depend on econtext.	Other nodes which
- *		dont have to evaluate expressions don't need to do this.
+ *		Build an expression context for use outside normal plan-node cases.
+ *		A fake scan-tuple slot can be supplied (pass NULL if not needed).
+ *		A memory context sufficiently long-lived to use as fcache context
+ *		must be supplied as well.
  * ----------------
  */
-void
-ExecAssignExprContext(EState *estate, CommonState *commonstate)
+ExprContext *
+MakeExprContext(TupleTableSlot *slot,
+				MemoryContext queryContext)
 {
-	ExprContext *econtext;
+	ExprContext *econtext = makeNode(ExprContext);
 
-	econtext = makeNode(ExprContext);
-	econtext->ecxt_scantuple = NULL;	/* scan tuple slot */
-	econtext->ecxt_innertuple = NULL;	/* inner tuple slot */
-	econtext->ecxt_outertuple = NULL;	/* outer tuple slot */
-	econtext->ecxt_relation = NULL;		/* relation */
-	econtext->ecxt_relid = 0;	/* relid */
-	econtext->ecxt_param_list_info = estate->es_param_list_info;
-	econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
-	econtext->ecxt_range_table = estate->es_range_table;		/* range table */
+	econtext->ecxt_scantuple = slot;
+	econtext->ecxt_innertuple = NULL;
+	econtext->ecxt_outertuple = NULL;
+	econtext->ecxt_per_query_memory = queryContext;
+	/*
+	 * We make the temporary context a child of current working context,
+	 * not of the specified queryContext.  This seems reasonable but I'm
+	 * not totally sure about it...
+	 *
+	 * Expression contexts made via this routine typically don't live long
+	 * enough to get reset, so specify a minsize of 0.  That avoids alloc'ing
+	 * any memory in the common case where expr eval doesn't use any.
+	 */
+	econtext->ecxt_per_tuple_memory =
+		AllocSetContextCreate(CurrentMemoryContext,
+							  "TempExprContext",
+							  0,
+							  ALLOCSET_DEFAULT_INITSIZE,
+							  ALLOCSET_DEFAULT_MAXSIZE);
+	econtext->ecxt_param_exec_vals = NULL;
+	econtext->ecxt_param_list_info = NULL;
+	econtext->ecxt_aggvalues = NULL;
+	econtext->ecxt_aggnulls = NULL;
+	econtext->ecxt_range_table = NIL;
+
+	return econtext;
+}
 
-	commonstate->cs_ExprContext = econtext;
+/*
+ * Free an ExprContext made by MakeExprContext, including the temporary
+ * context used for expression evaluation.  Note this will cause any
+ * pass-by-reference expression result to go away!
+ */
+void
+FreeExprContext(ExprContext *econtext)
+{
+	MemoryContextDelete(econtext->ecxt_per_tuple_memory);
+	pfree(econtext);
 }
 
 /* ----------------------------------------------------------------
@@ -390,6 +436,7 @@ ExecFreeExprContext(CommonState *commonstate)
 	 *	clean up memory used.
 	 * ----------------
 	 */
+	MemoryContextDelete(econtext->ecxt_per_tuple_memory);
 	pfree(econtext);
 	commonstate->cs_ExprContext = NULL;
 }
@@ -398,6 +445,7 @@ ExecFreeExprContext(CommonState *commonstate)
  *		ExecFreeTypeInfo
  * ----------------
  */
+#ifdef NOT_USED
 void
 ExecFreeTypeInfo(CommonState *commonstate)
 {
@@ -414,6 +462,7 @@ ExecFreeTypeInfo(CommonState *commonstate)
 	FreeTupleDesc(tupDesc);
 	commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
 }
+#endif
 
 /* ----------------------------------------------------------------
  *		the following scan type support functions are for
@@ -974,8 +1023,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		if (predicate != NULL)
 		{
 			if (econtext == NULL)
-				econtext = makeNode(ExprContext);
-			econtext->ecxt_scantuple = slot;
+				econtext = MakeExprContext(slot,
+										   TransactionCommandContext);
 
 			/* Skip this index-update if the predicate isn't satisfied */
 			if (!ExecQual((List *) predicate, econtext, false))
@@ -1023,7 +1072,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 			pfree(result);
 	}
 	if (econtext != NULL)
-		pfree(econtext);
+		FreeExprContext(econtext);
 }
 
 void
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 6d8d93a47f1..a92811d0342 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.35 2000/06/28 03:31:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,20 +57,18 @@ ProjectAttribute(TupleDesc TD,
 				 HeapTuple tup,
 				 bool *isnullP)
 {
-	Datum		val,
-				valueP;
+	Datum		val;
 	Var		   *attrVar = (Var *) tlist->expr;
 	AttrNumber	attrno = attrVar->varattno;
 
 	val = heap_getattr(tup, attrno, TD, isnullP);
+
 	if (*isnullP)
-		return (Datum) NULL;
+		return (Datum) 0;
 
-	valueP = datumCopy(val,
-					   TD->attrs[attrno - 1]->atttypid,
-					   TD->attrs[attrno - 1]->attbyval,
-					   (Size) TD->attrs[attrno - 1]->attlen);
-	return valueP;
+	return datumCopy(val,
+					 TD->attrs[attrno - 1]->attbyval,
+					 TD->attrs[attrno - 1]->attlen);
 }
 
 static execution_state *
@@ -351,10 +349,18 @@ postquel_function(FunctionCallInfo fcinfo,
 				  List *func_tlist,
 				  bool *isDone)
 {
+	MemoryContext oldcontext;
 	execution_state *es;
 	Datum		result = 0;
 	CommandId	savedId;
 
+	/*
+	 * Switch to context in which the fcache lives.  This ensures that
+	 * parsetrees, plans, etc, will have sufficient lifetime.  The
+	 * sub-executor is responsible for deleting per-tuple information.
+	 */
+	oldcontext = MemoryContextSwitchTo(fcache->fcacheCxt);
+
 	/*
 	 * Before we start do anything we must save CurrentScanCommandId to
 	 * restore it before return to upper Executor. Also, we have to set
@@ -416,6 +422,7 @@ postquel_function(FunctionCallInfo fcinfo,
 		 * Let caller know we're finished.
 		 */
 		*isDone = true;
+		MemoryContextSwitchTo(oldcontext);
 		return (fcache->oneResult) ? result : (Datum) NULL;
 	}
 
@@ -426,5 +433,8 @@ postquel_function(FunctionCallInfo fcinfo,
 	Assert(LAST_POSTQUEL_COMMAND(es));
 
 	*isDone = false;
+
+	MemoryContextSwitchTo(oldcontext);
+
 	return result;
 }
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0289cf45bd3..1ac5c3c9e21 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -32,7 +32,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.68 2000/06/28 03:31:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.69 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,6 +50,7 @@
 #include "parser/parse_type.h"
 #include "utils/syscache.h"
 #include "utils/tuplesort.h"
+#include "utils/datum.h"
 
 /*
  * AggStatePerAggData - per-aggregate working state for the Agg scan
@@ -101,13 +102,15 @@ typedef struct AggStatePerAggData
 				initValue2IsNull;
 
 	/*
-	 * We need the len and byval info for the agg's input and transition
-	 * data types in order to know how to copy/delete values.
+	 * We need the len and byval info for the agg's input, result, and
+	 * transition data types in order to know how to copy/delete values.
 	 */
 	int			inputtypeLen,
+				resulttypeLen,
 				transtype1Len,
 				transtype2Len;
 	bool		inputtypeByVal,
+				resulttypeByVal,
 				transtype1ByVal,
 				transtype2ByVal;
 
@@ -143,13 +146,16 @@ typedef struct AggStatePerAggData
 static void initialize_aggregate(AggStatePerAgg peraggstate);
 static void advance_transition_functions(AggStatePerAgg peraggstate,
 							 Datum newVal, bool isNull);
+static void process_sorted_aggregate(AggState *aggstate,
+									 AggStatePerAgg peraggstate);
 static void finalize_aggregate(AggStatePerAgg peraggstate,
 				   Datum *resultVal, bool *resultIsNull);
-static Datum copyDatum(Datum val, int typLen, bool typByVal);
 
 
 /*
  * Initialize one aggregate for a new set of input values.
+ *
+ * When called, CurrentMemoryContext should be the per-query context.
  */
 static void
 initialize_aggregate(AggStatePerAgg peraggstate)
@@ -177,23 +183,14 @@ initialize_aggregate(AggStatePerAgg peraggstate)
 
 	/*
 	 * (Re)set value1 and value2 to their initial values.
+	 *
+	 * Note that when the initial values are pass-by-ref, we just reuse
+	 * them without copying for each group.  Hence, transition function
+	 * had better not scribble on its input!
 	 */
-	if (OidIsValid(peraggstate->xfn1_oid) &&
-		!peraggstate->initValue1IsNull)
-		peraggstate->value1 = copyDatum(peraggstate->initValue1,
-										peraggstate->transtype1Len,
-										peraggstate->transtype1ByVal);
-	else
-		peraggstate->value1 = (Datum) NULL;
+	peraggstate->value1 = peraggstate->initValue1;
 	peraggstate->value1IsNull = peraggstate->initValue1IsNull;
-
-	if (OidIsValid(peraggstate->xfn2_oid) &&
-		!peraggstate->initValue2IsNull)
-		peraggstate->value2 = copyDatum(peraggstate->initValue2,
-										peraggstate->transtype2Len,
-										peraggstate->transtype2ByVal);
-	else
-		peraggstate->value2 = (Datum) NULL;
+	peraggstate->value2 = peraggstate->initValue2;
 	peraggstate->value2IsNull = peraggstate->initValue2IsNull;
 
 	/* ------------------------------------------
@@ -211,6 +208,9 @@ initialize_aggregate(AggStatePerAgg peraggstate)
 /*
  * Given a new input value, advance the transition functions of an aggregate.
  *
+ * When called, CurrentMemoryContext should be the context we want transition
+ * function results to be delivered into on this cycle.
+ *
  * Note: if the agg does not have usenulls set, null inputs will be filtered
  * out before reaching here.
  */
@@ -237,12 +237,13 @@ advance_transition_functions(AggStatePerAgg peraggstate,
 			 * XXX We assume, without having checked, that the agg's input
 			 * type is binary-compatible with its transtype1!
 			 *
-			 * We have to copy the datum since the tuple from which it came
+			 * We had better copy the datum if it is pass-by-ref, since
+			 * the given pointer may be pointing into a scan tuple that
 			 * will be freed on the next iteration of the scan.
 			 */
-			peraggstate->value1 = copyDatum(newVal,
-											peraggstate->transtype1Len,
-											peraggstate->transtype1ByVal);
+			peraggstate->value1 = datumCopy(newVal,
+											peraggstate->transtype1ByVal,
+											peraggstate->transtype1Len);
 			peraggstate->value1IsNull = false;
 			peraggstate->noInitValue = false;
 		}
@@ -264,8 +265,18 @@ advance_transition_functions(AggStatePerAgg peraggstate,
 			}
 			else
 				newVal = FunctionCallInvoke(&fcinfo);
-			if (!peraggstate->transtype1ByVal && !peraggstate->value1IsNull)
-				pfree(DatumGetPointer(peraggstate->value1));
+			/*
+			 * If the transition function was uncooperative, it may have
+			 * given us a pass-by-ref result that points at the scan tuple
+			 * or the prior-cycle working memory.  Copy it into the active
+			 * context if it doesn't look right.
+			 */
+			if (!peraggstate->transtype1ByVal && !fcinfo.isnull &&
+				! MemoryContextContains(CurrentMemoryContext,
+										DatumGetPointer(newVal)))
+				newVal = datumCopy(newVal,
+								   peraggstate->transtype1ByVal,
+								   peraggstate->transtype1Len);
 			peraggstate->value1 = newVal;
 			peraggstate->value1IsNull = fcinfo.isnull;
 		}
@@ -287,70 +298,116 @@ advance_transition_functions(AggStatePerAgg peraggstate,
 		}
 		else
 			newVal = FunctionCallInvoke(&fcinfo);
-		if (!peraggstate->transtype2ByVal && !peraggstate->value2IsNull)
-			pfree(DatumGetPointer(peraggstate->value2));
+		/*
+		 * If the transition function was uncooperative, it may have
+		 * given us a pass-by-ref result that points at the scan tuple
+		 * or the prior-cycle working memory.  Copy it into the active
+		 * context if it doesn't look right.
+		 */
+		if (!peraggstate->transtype2ByVal && !fcinfo.isnull &&
+			! MemoryContextContains(CurrentMemoryContext,
+									DatumGetPointer(newVal)))
+			newVal = datumCopy(newVal,
+							   peraggstate->transtype2ByVal,
+							   peraggstate->transtype2Len);
 		peraggstate->value2 = newVal;
 		peraggstate->value2IsNull = fcinfo.isnull;
 	}
 }
 
 /*
- * Compute the final value of one aggregate.
+ * Run the transition functions for a DISTINCT aggregate.  This is called
+ * after we have completed entering all the input values into the sort
+ * object.  We complete the sort, read out the value in sorted order, and
+ * run the transition functions on each non-duplicate value.
+ *
+ * When called, CurrentMemoryContext should be the per-query context.
  */
 static void
-finalize_aggregate(AggStatePerAgg peraggstate,
-				   Datum *resultVal, bool *resultIsNull)
+process_sorted_aggregate(AggState *aggstate,
+						 AggStatePerAgg peraggstate)
 {
-	Aggref				   *aggref = peraggstate->aggref;
-	FunctionCallInfoData	fcinfo;
+	Datum		oldVal = (Datum) 0;
+	bool		haveOldVal = false;
+	MemoryContext oldContext;
+	Datum		newVal;
+	bool		isNull;
 
-	MemSet(&fcinfo, 0, sizeof(fcinfo));
+	tuplesort_performsort(peraggstate->sortstate);
 
 	/*
-	 * If it's a DISTINCT aggregate, all we've done so far is to stuff the
-	 * input values into the sort object.  Complete the sort, then run the
-	 * transition functions on the non-duplicate values.  Note that
-	 * DISTINCT always suppresses nulls, per SQL spec, regardless of
-	 * usenulls.
+	 * Note: if input type is pass-by-ref, the datums returned by the sort
+	 * are freshly palloc'd in the per-query context, so we must be careful
+	 * to pfree them when they are no longer needed.
 	 */
-	if (aggref->aggdistinct)
+
+	while (tuplesort_getdatum(peraggstate->sortstate, true,
+							  &newVal, &isNull))
 	{
-		Datum		oldVal = (Datum) 0;
-		bool		haveOldVal = false;
-		Datum		newVal;
-		bool		isNull;
-
-		tuplesort_performsort(peraggstate->sortstate);
-		while (tuplesort_getdatum(peraggstate->sortstate, true,
-								  &newVal, &isNull))
+		/*
+		 * DISTINCT always suppresses nulls, per SQL spec, regardless of
+		 * the aggregate's usenulls setting.
+		 */
+		if (isNull)
+			continue;
+		/*
+		 * Clear and select the current working context for evaluation of
+		 * the equality function and transition functions.
+		 */
+		MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]);
+		oldContext =
+			MemoryContextSwitchTo(aggstate->agg_cxt[aggstate->which_cxt]);
+
+		if (haveOldVal &&
+			DatumGetBool(FunctionCall2(&peraggstate->equalfn,
+									   oldVal, newVal)))
+		{
+			/* equal to prior, so forget this one */
+			if (!peraggstate->inputtypeByVal)
+				pfree(DatumGetPointer(newVal));
+			/* note we do NOT flip contexts in this case... */
+		}
+		else
 		{
-			if (isNull)
-				continue;
-			if (haveOldVal)
-			{
-				if (DatumGetBool(FunctionCall2(&peraggstate->equalfn,
-											   oldVal, newVal)))
-				{
-					/* equal to prior, so forget this one */
-					if (!peraggstate->inputtypeByVal)
-						pfree(DatumGetPointer(newVal));
-					continue;
-				}
-			}
 			advance_transition_functions(peraggstate, newVal, false);
+			/*
+			 * Make the other context current so that this transition
+			 * result is preserved.
+			 */
+			aggstate->which_cxt = 1 - aggstate->which_cxt;
+			/* forget the old value, if any */
 			if (haveOldVal && !peraggstate->inputtypeByVal)
 				pfree(DatumGetPointer(oldVal));
 			oldVal = newVal;
 			haveOldVal = true;
 		}
-		if (haveOldVal && !peraggstate->inputtypeByVal)
-			pfree(DatumGetPointer(oldVal));
-		tuplesort_end(peraggstate->sortstate);
-		peraggstate->sortstate = NULL;
+
+		MemoryContextSwitchTo(oldContext);
 	}
 
+	if (haveOldVal && !peraggstate->inputtypeByVal)
+		pfree(DatumGetPointer(oldVal));
+
+	tuplesort_end(peraggstate->sortstate);
+	peraggstate->sortstate = NULL;
+}
+
+/*
+ * Compute the final value of one aggregate.
+ *
+ * When called, CurrentMemoryContext should be the context where we want
+ * final values delivered (ie, the per-output-tuple expression context).
+ */
+static void
+finalize_aggregate(AggStatePerAgg peraggstate,
+				   Datum *resultVal, bool *resultIsNull)
+{
+	FunctionCallInfoData	fcinfo;
+
+	MemSet(&fcinfo, 0, sizeof(fcinfo));
+
 	/*
-	 * Now apply the agg's finalfn, or substitute the appropriate
+	 * Apply the agg's finalfn, or substitute the appropriate
 	 * transition value if there is no finalfn.
 	 *
 	 * XXX For now, only apply finalfn if we got at least one non-null input
@@ -403,35 +460,27 @@ finalize_aggregate(AggStatePerAgg peraggstate,
 		/* Return value1 */
 		*resultVal = peraggstate->value1;
 		*resultIsNull = peraggstate->value1IsNull;
-		/* prevent pfree below */
-		peraggstate->value1IsNull = true;
 	}
 	else if (OidIsValid(peraggstate->xfn2_oid))
 	{
 		/* Return value2 */
 		*resultVal = peraggstate->value2;
 		*resultIsNull = peraggstate->value2IsNull;
-		/* prevent pfree below */
-		peraggstate->value2IsNull = true;
 	}
 	else
 		elog(ERROR, "ExecAgg: no valid transition functions??");
-
 	/*
-	 * Release any per-group working storage, unless we're passing it back
-	 * as the result of the aggregate.
+	 * If result is pass-by-ref, make sure it is in the right context.
 	 */
-	if (OidIsValid(peraggstate->xfn1_oid) &&
-		!peraggstate->value1IsNull &&
-		!peraggstate->transtype1ByVal)
-		pfree(DatumGetPointer(peraggstate->value1));
-
-	if (OidIsValid(peraggstate->xfn2_oid) &&
-		!peraggstate->value2IsNull &&
-		!peraggstate->transtype2ByVal)
-		pfree(DatumGetPointer(peraggstate->value2));
+	if (!peraggstate->resulttypeByVal && ! *resultIsNull &&
+		! MemoryContextContains(CurrentMemoryContext,
+								DatumGetPointer(*resultVal)))
+		*resultVal = datumCopy(*resultVal,
+							   peraggstate->resulttypeByVal,
+							   peraggstate->resulttypeLen);
 }
 
+
 /* ---------------------------------------
  *
  * ExecAgg -
@@ -461,6 +510,7 @@ ExecAgg(Agg *node)
 	Datum	   *aggvalues;
 	bool	   *aggnulls;
 	AggStatePerAgg peragg;
+	MemoryContext oldContext;
 	TupleTableSlot *resultSlot;
 	HeapTuple	inputTuple;
 	int			aggno;
@@ -481,14 +531,18 @@ ExecAgg(Agg *node)
 	peragg = aggstate->peragg;
 
 	/*
-	 * We loop retrieving groups until we find one matching
-	 * node->plan.qual
+	 * We loop retrieving groups until we find one matching node->plan.qual
 	 */
 	do
 	{
 		if (aggstate->agg_done)
 			return NULL;
 
+		/*
+		 * Clear the per-output-tuple context for each group
+		 */
+		MemoryContextReset(aggstate->tup_cxt);
+
 		/*
 		 * Initialize working state for a new input tuple group
 		 */
@@ -514,6 +568,17 @@ ExecAgg(Agg *node)
 				break;
 			econtext->ecxt_scantuple = outerslot;
 
+			/*
+			 * Clear and select the current working context for evaluation
+			 * of the input expressions and transition functions at this
+			 * input tuple.
+			 */
+			econtext->ecxt_per_tuple_memory =
+				aggstate->agg_cxt[aggstate->which_cxt];
+			ResetExprContext(econtext);
+			oldContext =
+				MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
 			for (aggno = 0; aggno < aggstate->numaggs; aggno++)
 			{
 				AggStatePerAgg peraggstate = &peragg[aggno];
@@ -527,13 +592,26 @@ ExecAgg(Agg *node)
 					continue;	/* ignore this tuple for this agg */
 
 				if (aggref->aggdistinct)
+				{
+					/* putdatum has to be called in per-query context */
+					MemoryContextSwitchTo(oldContext);
 					tuplesort_putdatum(peraggstate->sortstate,
 									   newVal, isNull);
+					MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+				}
 				else
 					advance_transition_functions(peraggstate,
 												 newVal, isNull);
 			}
 
+			/*
+			 * Make the other context current so that these transition
+			 * results are preserved.
+			 */
+			aggstate->which_cxt = 1 - aggstate->which_cxt;
+
+			MemoryContextSwitchTo(oldContext);
+
 			/*
 			 * Keep a copy of the first input tuple for the projection.
 			 * (We only need one since only the GROUP BY columns in it can
@@ -546,14 +624,38 @@ ExecAgg(Agg *node)
 
 		/*
 		 * Done scanning input tuple group. Finalize each aggregate
-		 * calculation.
+		 * calculation, and stash results in the per-output-tuple context.
+		 *
+		 * This is a bit tricky when there are both DISTINCT and plain
+		 * aggregates: we must first finalize all the plain aggs and then all
+		 * the DISTINCT ones.  This is needed because the last transition
+		 * values for the plain aggs are stored in the not-current working
+		 * context, and we have to evaluate those aggs (and stash the results
+		 * in the output tup_cxt!) before we start flipping contexts again
+		 * in process_sorted_aggregate.
 		 */
+		oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
+		for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+		{
+			AggStatePerAgg peraggstate = &peragg[aggno];
+
+			if (! peraggstate->aggref->aggdistinct)
+				finalize_aggregate(peraggstate,
+								   &aggvalues[aggno], &aggnulls[aggno]);
+		}
+		MemoryContextSwitchTo(oldContext);
 		for (aggno = 0; aggno < aggstate->numaggs; aggno++)
 		{
 			AggStatePerAgg peraggstate = &peragg[aggno];
 
-			finalize_aggregate(peraggstate,
-							   &aggvalues[aggno], &aggnulls[aggno]);
+			if (peraggstate->aggref->aggdistinct)
+			{
+				process_sorted_aggregate(aggstate, peraggstate);
+				oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
+				finalize_aggregate(peraggstate,
+								   &aggvalues[aggno], &aggnulls[aggno]);
+				MemoryContextSwitchTo(oldContext);
+			}
 		}
 
 		/*
@@ -584,7 +686,7 @@ ExecAgg(Agg *node)
 			/*
 			 * If inputtuple==NULL (ie, the outerPlan didn't return
 			 * anything), create a dummy all-nulls input tuple for use by
-			 * execProject. 99.44% of the time this is a waste of cycles,
+			 * ExecProject. 99.44% of the time this is a waste of cycles,
 			 * because ordinarily the projected output tuple's targetlist
 			 * cannot contain any direct (non-aggregated) references to
 			 * input columns, so the dummy tuple will not be referenced.
@@ -619,7 +721,8 @@ ExecAgg(Agg *node)
 
 		/*
 		 * Store the representative input tuple in the tuple table slot
-		 * reserved for it.
+		 * reserved for it.  The tuple will be deleted when it is cleared
+		 * from the slot.
 		 */
 		ExecStoreTuple(inputTuple,
 					   aggstate->csstate.css_ScanTupleSlot,
@@ -627,6 +730,11 @@ ExecAgg(Agg *node)
 					   true);
 		econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
 
+		/*
+		 * Do projection and qual check in the per-output-tuple context.
+		 */
+		econtext->ecxt_per_tuple_memory = aggstate->tup_cxt;
+
 		/*
 		 * Form a projection tuple using the aggregate results and the
 		 * representative input tuple.	Store it in the result tuple slot.
@@ -701,11 +809,33 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	}
 
 	/*
-	 * assign node's base id and create expression context
+	 * Create expression context
 	 */
-	ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, (Plan *) parent);
 	ExecAssignExprContext(estate, &aggstate->csstate.cstate);
 
+	/*
+	 * We actually need three separate expression memory contexts: one
+	 * for calculating per-output-tuple values (ie, the finished aggregate
+	 * results), and two that we ping-pong between for per-input-tuple
+	 * evaluation of input expressions and transition functions.  The
+	 * context made by ExecAssignExprContext() is used as the output context.
+	 */
+	aggstate->tup_cxt =
+		aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory;
+	aggstate->agg_cxt[0] = 
+		AllocSetContextCreate(CurrentMemoryContext,
+							  "AggExprContext1",
+							  ALLOCSET_DEFAULT_MINSIZE,
+							  ALLOCSET_DEFAULT_INITSIZE,
+							  ALLOCSET_DEFAULT_MAXSIZE);
+	aggstate->agg_cxt[1] = 
+		AllocSetContextCreate(CurrentMemoryContext,
+							  "AggExprContext2",
+							  ALLOCSET_DEFAULT_MINSIZE,
+							  ALLOCSET_DEFAULT_INITSIZE,
+							  ALLOCSET_DEFAULT_MAXSIZE);
+	aggstate->which_cxt = 0;
+
 #define AGG_NSLOTS 2
 
 	/*
@@ -769,16 +899,20 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 		/* Fill in the peraggstate data */
 		peraggstate->aggref = aggref;
 
-		aggTuple = SearchSysCacheTuple(AGGNAME,
-									   PointerGetDatum(aggname),
-									   ObjectIdGetDatum(aggref->basetype),
-									   0, 0);
+		aggTuple = SearchSysCacheTupleCopy(AGGNAME,
+										   PointerGetDatum(aggname),
+										   ObjectIdGetDatum(aggref->basetype),
+										   0, 0);
 		if (!HeapTupleIsValid(aggTuple))
 			elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
 				 aggname,
 				 typeidTypeName(aggref->basetype));
 		aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
 
+		typeInfo = typeidType(aggform->aggfinaltype);
+		peraggstate->resulttypeLen = typeLen(typeInfo);
+		peraggstate->resulttypeByVal = typeByVal(typeInfo);
+
 		peraggstate->initValue1 =
 			AggNameGetInitVal(aggname,
 							  aggform->aggbasetype,
@@ -846,6 +980,8 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 			peraggstate->sortOperator = any_ordering_op(inputType);
 			peraggstate->sortstate = NULL;
 		}
+
+		heap_freetuple(aggTuple);
 	}
 
 	return TRUE;
@@ -866,6 +1002,17 @@ ExecEndAgg(Agg *node)
 	Plan	   *outerPlan;
 
 	ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+	/*
+	 * Make sure ExecFreeExprContext() frees the right expr context...
+	 */
+	aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory =
+		aggstate->tup_cxt;
+	ExecFreeExprContext(&aggstate->csstate.cstate);
+	/*
+	 * ... and I free the others.
+	 */
+	MemoryContextDelete(aggstate->agg_cxt[0]);
+	MemoryContextDelete(aggstate->agg_cxt[1]);
 
 	outerPlan = outerPlan(node);
 	ExecEndNode(outerPlan, (Plan *) node);
@@ -890,28 +1037,4 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
 	 */
 	if (((Plan *) node)->lefttree->chgParam == NULL)
 		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
-}
-
-
-/*
- * Helper routine to make a copy of a Datum.
- *
- * NB: input had better not be a NULL; might cause null-pointer dereference.
- */
-static Datum
-copyDatum(Datum val, int typLen, bool typByVal)
-{
-	if (typByVal)
-		return val;
-	else
-	{
-		char	   *newVal;
-
-		if (typLen == -1)		/* variable length type? */
-			typLen = VARSIZE((struct varlena *) DatumGetPointer(val));
-		newVal = (char *) palloc(typLen);
-		memcpy(newVal, DatumGetPointer(val), typLen);
-		return PointerGetDatum(newVal);
-	}
 }
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 5e34e806e32..2b1eceb15d9 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.34 2000/06/17 21:48:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.35 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -219,16 +219,12 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 	node->appendstate = appendstate;
 
 	/* ----------------
-	 *	Miscellanious initialization
-	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks
+	 *	Miscellaneous initialization
 	 *
 	 *	Append plans don't have expression contexts because they
-	 *	never call ExecQual or ExecTargetList.
+	 *	never call ExecQual or ExecProject.
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &appendstate->cstate, parent);
 
 #define APPEND_NSLOTS 1
 	/* ----------------
@@ -380,7 +376,7 @@ ExecCountSlotsAppend(Append *node)
  *
  *		Handles the iteration over the multiple scans.
  *
- *	   NOTE: Can't call this ExecAppend, that name is used in execMain.l
+ *	   NOTE: Can't call this ExecAppend, that name is used in execMain.
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index d1ae02616c1..8a445b53d41 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -15,7 +15,7 @@
  *	  locate group boundaries.
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.36 2000/05/30 04:24:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.37 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,13 +68,11 @@ ExecGroupEveryTuple(Group *node)
 	EState	   *estate;
 	ExprContext *econtext;
 	TupleDesc	tupdesc;
-
 	HeapTuple	outerTuple = NULL;
 	HeapTuple	firsttuple;
 	TupleTableSlot *outerslot;
 	ProjectionInfo *projInfo;
 	TupleTableSlot *resultSlot;
-
 	bool		isDone;
 
 	/* ---------------------
@@ -84,14 +82,16 @@ ExecGroupEveryTuple(Group *node)
 	grpstate = node->grpstate;
 	if (grpstate->grp_done)
 		return NULL;
-
 	estate = node->plan.state;
-
 	econtext = grpstate->csstate.cstate.cs_ExprContext;
-
 	tupdesc = ExecGetScanType(&grpstate->csstate);
 
-	/* if we haven't returned first tuple of new group yet ... */
+	/*
+	 *	We need not call ResetExprContext here because execTuplesMatch
+	 *	will reset the per-tuple memory context once per input tuple.
+	 */
+
+	/* if we haven't returned first tuple of a new group yet ... */
 	if (grpstate->grp_useFirstTuple)
 	{
 		grpstate->grp_useFirstTuple = FALSE;
@@ -130,7 +130,8 @@ ExecGroupEveryTuple(Group *node)
 			if (!execTuplesMatch(firsttuple, outerTuple,
 								 tupdesc,
 								 node->numCols, node->grpColIdx,
-								 grpstate->eqfunctions))
+								 grpstate->eqfunctions,
+								 econtext->ecxt_per_tuple_memory))
 			{
 
 				/*
@@ -179,13 +180,11 @@ ExecGroupOneTuple(Group *node)
 	EState	   *estate;
 	ExprContext *econtext;
 	TupleDesc	tupdesc;
-
 	HeapTuple	outerTuple = NULL;
 	HeapTuple	firsttuple;
 	TupleTableSlot *outerslot;
 	ProjectionInfo *projInfo;
 	TupleTableSlot *resultSlot;
-
 	bool		isDone;
 
 	/* ---------------------
@@ -195,13 +194,15 @@ ExecGroupOneTuple(Group *node)
 	grpstate = node->grpstate;
 	if (grpstate->grp_done)
 		return NULL;
-
 	estate = node->plan.state;
-
 	econtext = node->grpstate->csstate.cstate.cs_ExprContext;
-
 	tupdesc = ExecGetScanType(&grpstate->csstate);
 
+	/*
+	 *	We need not call ResetExprContext here because execTuplesMatch
+	 *	will reset the per-tuple memory context once per input tuple.
+	 */
+
 	firsttuple = grpstate->grp_firstTuple;
 	if (firsttuple == NULL)
 	{
@@ -237,7 +238,8 @@ ExecGroupOneTuple(Group *node)
 		if (!execTuplesMatch(firsttuple, outerTuple,
 							 tupdesc,
 							 node->numCols, node->grpColIdx,
-							 grpstate->eqfunctions))
+							 grpstate->eqfunctions,
+							 econtext->ecxt_per_tuple_memory))
 			break;
 	}
 
@@ -296,10 +298,8 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
 	grpstate->grp_firstTuple = NULL;
 
 	/*
-	 * assign node's base id and create expression context
+	 * create expression context
 	 */
-	ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
-						   (Plan *) parent);
 	ExecAssignExprContext(estate, &grpstate->csstate.cstate);
 
 #define GROUP_NSLOTS 2
@@ -360,6 +360,7 @@ ExecEndGroup(Group *node)
 	grpstate = node->grpstate;
 
 	ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+	ExecFreeExprContext(&grpstate->csstate.cstate);
 
 	outerPlan = outerPlan(node);
 	ExecEndNode(outerPlan, (Plan *) node);
@@ -406,6 +407,9 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
  * numCols: the number of attributes to be examined
  * matchColIdx: array of attribute column numbers
  * eqFunctions: array of fmgr lookup info for the equality functions to use
+ * evalContext: short-term memory context for executing the functions
+ *
+ * NB: evalContext is reset each time!
  */
 bool
 execTuplesMatch(HeapTuple tuple1,
@@ -413,16 +417,25 @@ execTuplesMatch(HeapTuple tuple1,
 				TupleDesc tupdesc,
 				int numCols,
 				AttrNumber *matchColIdx,
-				FmgrInfo *eqfunctions)
+				FmgrInfo *eqfunctions,
+				MemoryContext evalContext)
 {
+	MemoryContext oldContext;
+	bool		result;
 	int			i;
 
+	/* Reset and switch into the temp context. */
+	MemoryContextReset(evalContext);
+	oldContext = MemoryContextSwitchTo(evalContext);
+
 	/*
 	 * We cannot report a match without checking all the fields, but we
 	 * can report a non-match as soon as we find unequal fields.  So,
 	 * start comparing at the last field (least significant sort key).
 	 * That's the most likely to be different...
 	 */
+	result = true;
+
 	for (i = numCols; --i >= 0;)
 	{
 		AttrNumber	att = matchColIdx[i];
@@ -442,7 +455,10 @@ execTuplesMatch(HeapTuple tuple1,
 							 &isNull2);
 
 		if (isNull1 != isNull2)
-			return FALSE;		/* one null and one not; they aren't equal */
+		{
+			result = false;		/* one null and one not; they aren't equal */
+			break;
+		}
 
 		if (isNull1)
 			continue;			/* both are null, treat as equal */
@@ -451,10 +467,15 @@ execTuplesMatch(HeapTuple tuple1,
 
 		if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
 										 attr1, attr2)))
-			return FALSE;
+		{
+			result = false;		/* they aren't equal */
+			break;
+		}
 	}
 
-	return TRUE;
+	MemoryContextSwitchTo(oldContext);
+
+	return result;
 }
 
 /*
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 34b0a269a5b..9c2d293858c 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- *	$Id: nodeHash.c,v 1.48 2000/06/28 03:31:34 tgl Exp $
+ *	$Id: nodeHash.c,v 1.49 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,7 +28,8 @@
 #include "executor/nodeHash.h"
 #include "executor/nodeHashjoin.h"
 #include "miscadmin.h"
-
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
 
 static int	hashFunc(Datum key, int len, bool byVal);
 
@@ -45,7 +46,7 @@ ExecHash(Hash *node)
 	EState	   *estate;
 	HashState  *hashstate;
 	Plan	   *outerNode;
-	Var		   *hashkey;
+	Node	   *hashkey;
 	HashJoinTable hashtable;
 	TupleTableSlot *slot;
 	ExprContext *econtext;
@@ -139,12 +140,9 @@ ExecInitHash(Hash *node, EState *estate, Plan *parent)
 	/* ----------------
 	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
 	ExecAssignExprContext(estate, &hashstate->cstate);
 
 	/* ----------------
@@ -204,6 +202,7 @@ ExecEndHash(Hash *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&hashstate->cstate);
+	ExecFreeExprContext(&hashstate->cstate);
 
 	/* ----------------
 	 *	shut down the subplan
@@ -236,6 +235,7 @@ ExecHashTableCreate(Hash *node)
 	int			totalbuckets;
 	int			bucketsize;
 	int			i;
+	Type		typeInfo;
 	MemoryContext oldcxt;
 
 	/* ----------------
@@ -346,6 +346,14 @@ ExecHashTableCreate(Hash *node)
 	hashtable->innerBatchSize = NULL;
 	hashtable->outerBatchSize = NULL;
 
+	/* ----------------
+	 *	Get info about the datatype of the hash key.
+	 * ----------------
+	 */
+	typeInfo = typeidType(exprType(node->hashkey));
+	hashtable->typByVal = typeByVal(typeInfo);
+	hashtable->typLen = typeLen(typeInfo);
+
 	/* ----------------
 	 *	Create temporary memory contexts in which to keep the hashtable
 	 *	working storage.  See notes in executor/hashjoin.h.
@@ -448,7 +456,7 @@ ExecHashTableDestroy(HashJoinTable hashtable)
 void
 ExecHashTableInsert(HashJoinTable hashtable,
 					ExprContext *econtext,
-					Var *hashkey)
+					Node *hashkey)
 {
 	int			bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
 	TupleTableSlot *slot = econtext->ecxt_innertuple;
@@ -508,43 +516,44 @@ ExecHashTableInsert(HashJoinTable hashtable,
 int
 ExecHashGetBucket(HashJoinTable hashtable,
 				  ExprContext *econtext,
-				  Var *hashkey)
+				  Node *hashkey)
 {
 	int			bucketno;
 	Datum		keyval;
 	bool		isNull;
+	bool		isDone;
 
 	/* ----------------
 	 *	Get the join attribute value of the tuple
 	 *
-	 * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
-	 * hashkey may be T_ArrayRef, not just T_Var.		- vadim 04/22/97
+	 *	We reset the eval context each time to avoid any possibility
+	 *	of memory leaks in the hash function.
 	 * ----------------
 	 */
-	keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
+	ResetExprContext(econtext);
 
-	/*
-	 * keyval could be null, so we better point it to something valid
-	 * before trying to run hashFunc on it. --djm 8/17/96
-	 */
-	if (isNull)
-	{
-		execConstByVal = 0;
-		execConstLen = 0;
-		keyval = (Datum) "";
-	}
+	keyval = ExecEvalExprSwitchContext(hashkey, econtext,
+									   &isNull, &isDone);
 
 	/* ------------------
 	 *	compute the hash function
 	 * ------------------
 	 */
-	bucketno = hashFunc(keyval, execConstLen, execConstByVal) % hashtable->totalbuckets;
+	if (isNull)
+	{
+		bucketno = 0;
+	}
+	else
+	{
+		bucketno = hashFunc(keyval, hashtable->typLen, hashtable->typByVal)
+			% hashtable->totalbuckets;
+	}
 
 #ifdef HJDEBUG
 	if (bucketno >= hashtable->nbuckets)
-		printf("hash(%d) = %d SAVED\n", keyval, bucketno);
+		printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
 	else
-		printf("hash(%d) = %d\n", keyval, bucketno);
+		printf("hash(%ld) = %d\n", (long) keyval, bucketno);
 #endif
 
 	return bucketno;
@@ -585,6 +594,9 @@ ExecScanHashBucket(HashJoinState *hjstate,
 								  false);		/* do not pfree this tuple */
 		econtext->ecxt_innertuple = inntuple;
 
+		/* reset temp memory each time to avoid leaks from qual expression */
+		ResetExprContext(econtext);
+
 		if (ExecQual(hjclauses, econtext, false))
 		{
 			hjstate->hj_CurTuple = hashTuple;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index d1cb1fe2420..e136c131541 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.30 2000/01/26 05:56:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.31 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,13 +51,12 @@ ExecHashJoin(HashJoin *node)
 	List	   *qual;
 	ScanDirection dir;
 	TupleTableSlot *inntuple;
-	Var		   *outerVar;
+	Node	   *outerVar;
 	ExprContext *econtext;
 	HashJoinTable hashtable;
 	HeapTuple	curtuple;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	Var		   *innerhashkey;
 	int			i;
 	bool		hashPhaseDone;
 
@@ -73,7 +72,6 @@ ExecHashJoin(HashJoin *node)
 	hashNode = (Hash *) innerPlan(node);
 	outerNode = outerPlan(node);
 	hashPhaseDone = node->hashdone;
-
 	dir = estate->es_direction;
 
 	/* -----------------
@@ -81,13 +79,21 @@ ExecHashJoin(HashJoin *node)
 	 * -----------------
 	 */
 	hashtable = hjstate->hj_HashTable;
+	econtext = hjstate->jstate.cs_ExprContext;
 
-	/* --------------------
-	 * initialize expression context
-	 * --------------------
+	/* ----------------
+	 *	Reset per-tuple memory context to free any expression evaluation
+	 *	storage allocated in the previous tuple cycle.
+	 * ----------------
 	 */
-	econtext = hjstate->jstate.cs_ExprContext;
+	ResetExprContext(econtext);
 
+	/* ----------------
+	 *	Check to see if we're still projecting out tuples from a previous
+	 *	join tuple (because there is a function-returning-set in the
+	 *	projection expressions).  If so, try to project another one.
+	 * ----------------
+	 */
 	if (hjstate->jstate.cs_TupFromTlist)
 	{
 		TupleTableSlot *result;
@@ -96,6 +102,8 @@ ExecHashJoin(HashJoin *node)
 		result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
 		if (!isDone)
 			return result;
+		/* Done with that source tuple... */
+		hjstate->jstate.cs_TupFromTlist = false;
 	}
 
 	/* ----------------
@@ -112,8 +120,7 @@ ExecHashJoin(HashJoin *node)
 			 */
 			hashtable = ExecHashTableCreate(hashNode);
 			hjstate->hj_HashTable = hashtable;
-			innerhashkey = hashNode->hashkey;
-			hjstate->hj_InnerHashKey = innerhashkey;
+			hjstate->hj_InnerHashKey = hashNode->hashkey;
 
 			/* ----------------
 			 * execute the Hash node, to build the hash table
@@ -139,7 +146,7 @@ ExecHashJoin(HashJoin *node)
 	 * ----------------
 	 */
 	outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
-	outerVar = get_leftop(clause);
+	outerVar = (Node *) get_leftop(clause);
 
 	for (;;)
 	{
@@ -220,6 +227,10 @@ ExecHashJoin(HashJoin *node)
 									  InvalidBuffer,
 									  false);	/* don't pfree this tuple */
 			econtext->ecxt_innertuple = inntuple;
+
+			/* reset temp memory each time to avoid leaks from qpqual */
+			ResetExprContext(econtext);
+
 			/* ----------------
 			 * if we pass the qual, then save state for next call and
 			 * have ExecProject form the projection, store it
@@ -279,12 +290,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 	/* ----------------
 	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
 	ExecAssignExprContext(estate, &hjstate->jstate);
 
 #define HASHJOIN_NSLOTS 2
@@ -343,10 +351,10 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 	hjstate->hj_HashTable = (HashJoinTable) NULL;
 	hjstate->hj_CurBucketNo = 0;
 	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
-	hjstate->hj_InnerHashKey = (Var *) NULL;
+	hjstate->hj_InnerHashKey = (Node *) NULL;
 
 	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
-	hjstate->jstate.cs_TupFromTlist = (bool) false;
+	hjstate->jstate.cs_TupFromTlist = false;
 
 	return TRUE;
 }
@@ -396,6 +404,7 @@ ExecEndHashJoin(HashJoin *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&hjstate->jstate);
+	ExecFreeExprContext(&hjstate->jstate);
 
 	/* ----------------
 	 * clean up subtrees
@@ -510,7 +519,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
 	BufFile    *innerFile;
 	TupleTableSlot *slot;
 	ExprContext *econtext;
-	Var		   *innerhashkey;
+	Node	   *innerhashkey;
 
 	if (newbatch > 1)
 	{
@@ -651,10 +660,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
 
 	hjstate->hj_CurBucketNo = 0;
 	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
-	hjstate->hj_InnerHashKey = (Var *) NULL;
+	hjstate->hj_InnerHashKey = (Node *) NULL;
 
 	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
-	hjstate->jstate.cs_TupFromTlist = (bool) false;
+	hjstate->jstate.cs_TupFromTlist = false;
 
 	/*
 	 * if chgParam of subnodes is not null then plans will be re-scanned
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index be5617d5ef4..0eb3b9f5ae9 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.51 2000/06/15 04:09:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.52 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,6 +79,7 @@ IndexNext(IndexScan *node)
 	EState	   *estate;
 	CommonScanState *scanstate;
 	IndexScanState *indexstate;
+	ExprContext *econtext;
 	ScanDirection direction;
 	Snapshot	snapshot;
 	IndexScanDescPtr scanDescs;
@@ -89,7 +90,6 @@ IndexNext(IndexScan *node)
 	TupleTableSlot *slot;
 	Buffer		buffer = InvalidBuffer;
 	int			numIndices;
-
 	bool		bBackward;
 	int			indexNumber;
 
@@ -112,6 +112,7 @@ IndexNext(IndexScan *node)
 	scanDescs = indexstate->iss_ScanDescs;
 	heapRelation = scanstate->css_currentRelation;
 	numIndices = indexstate->iss_NumIndices;
+	econtext = scanstate->cstate.cs_ExprContext;
 	slot = scanstate->css_ScanTupleSlot;
 
 	/*
@@ -133,14 +134,15 @@ IndexNext(IndexScan *node)
 		slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
 		slot->ttc_shouldFree = false;
 
-		scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
+		econtext->ecxt_scantuple = slot;
 
 		/* Does the tuple meet any of the OR'd indxqual conditions? */
+
+		ResetExprContext(econtext);
+
 		foreach(qual, node->indxqualorig)
 		{
-			if (ExecQual((List *) lfirst(qual),
-						 scanstate->cstate.cs_ExprContext,
-						 false))
+			if (ExecQual((List *) lfirst(qual), econtext, false))
 				break;
 		}
 		if (qual == NIL)		/* would not be returned by indices */
@@ -219,14 +221,13 @@ IndexNext(IndexScan *node)
 				 * and checking for failure with all previous
 				 * qualifications.
 				 */
-				scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
+				econtext->ecxt_scantuple = slot;
+				ResetExprContext(econtext);
 				qual = node->indxqualorig;
 				for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
 					 prev_index++)
 				{
-					if (ExecQual((List *) lfirst(qual),
-								 scanstate->cstate.cs_ExprContext,
-								 false))
+					if (ExecQual((List *) lfirst(qual), econtext, false))
 					{
 						prev_matches = true;
 						break;
@@ -234,7 +235,7 @@ IndexNext(IndexScan *node)
 					qual = lnext(qual);
 				}
 				if (!prev_matches)
-					return slot;/* OK to return tuple */
+					return slot; /* OK to return tuple */
 				/* Duplicate tuple, so drop it and loop back for another */
 				ExecClearTuple(slot);
 			}
@@ -284,7 +285,7 @@ ExecIndexScan(IndexScan *node)
 	 *	use IndexNext as access method
 	 * ----------------
 	 */
-	return ExecScan(&node->scan, IndexNext);
+	return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext);
 }
 
 /* ----------------------------------------------------------------
@@ -293,9 +294,8 @@ ExecIndexScan(IndexScan *node)
  *		Recalculates the value of the scan keys whose value depends on
  *		information known at runtime and rescans the indexed relation.
  *		Updating the scan key was formerly done separately in
- *		ExecUpdateIndexScanKeys. Integrating it into ReScan
- *		makes rescans of indices and
- *		relations/general streams more uniform.
+ *		ExecUpdateIndexScanKeys. Integrating it into ReScan makes
+ *		rescans of indices and relations/general streams more uniform.
  *
  * ----------------------------------------------------------------
  */
@@ -304,6 +304,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 {
 	EState	   *estate;
 	IndexScanState *indexstate;
+	ExprContext *econtext;
 	ScanDirection direction;
 	IndexScanDescPtr scanDescs;
 	ScanKey    *scanKeys;
@@ -311,8 +312,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 	ScanKey		skey;
 	int			numIndices;
 	int			i;
-
-	Pointer    *runtimeKeyInfo;
+	int		  **runtimeKeyInfo;
 	int		   *numScanKeys;
 	List	   *indxqual;
 	List	   *qual;
@@ -326,22 +326,34 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 	bool		isNull;
 	bool		isDone;
 
-	indexstate = node->indxstate;
 	estate = node->scan.plan.state;
+	indexstate = node->indxstate;
+	econtext = indexstate->iss_RuntimeContext; /* context for runtime keys */
 	direction = estate->es_direction;
 	numIndices = indexstate->iss_NumIndices;
 	scanDescs = indexstate->iss_ScanDescs;
 	scanKeys = indexstate->iss_ScanKeys;
-	runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+	runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
 	numScanKeys = indexstate->iss_NumScanKeys;
 	indexstate->iss_IndexPtr = -1;
 	if (ScanDirectionIsBackward(node->indxorderdir))
 		indexstate->iss_IndexPtr = numIndices;
 
-	/* If we are being passed an outer tuple, save it for runtime key calc */
-	if (exprCtxt != NULL)
-		node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
-			exprCtxt->ecxt_outertuple;
+	if (econtext)
+	{
+		/*
+		 * If we are being passed an outer tuple,
+		 * save it for runtime key calc
+		 */
+		if (exprCtxt != NULL)
+			econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
+		/*
+		 * Reset the runtime-key context so we don't leak memory as
+		 * each outer tuple is scanned.  Note this assumes that we
+		 * will recalculate *all* runtime keys on each call.
+		 */
+		ResetExprContext(econtext);
+	}
 
 	/* If this is re-scanning of PlanQual ... */
 	if (estate->es_evTuple != NULL &&
@@ -364,7 +376,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 
 		if (runtimeKeyInfo)
 		{
-			run_keys = (int *) runtimeKeyInfo[i];
+			run_keys = runtimeKeyInfo[i];
 			for (j = 0; j < n_keys; j++)
 			{
 
@@ -373,6 +385,13 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 				 * expression and evaluate it with respect to the current
 				 * outer tuple.  We then stick the result into the scan
 				 * key.
+				 *
+				 * Note: the result of the eval could be a pass-by-ref
+				 * value that's stored in the outer scan's tuple, not in
+				 * econtext->ecxt_per_tuple_memory.  We assume that the
+				 * outer tuple will stay put throughout our scan.  If this
+				 * is wrong, we could copy the result into our context
+				 * explicitly, but I think that's not necessary...
 				 */
 				if (run_keys[j] != NO_OP)
 				{
@@ -385,10 +404,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 					 * pass in isDone but ignore it.  We don't iterate in
 					 * quals
 					 */
-					scanvalue = (Datum)
-						ExecEvalExpr(scanexpr,
-							 node->scan.scanstate->cstate.cs_ExprContext,
-									 &isNull, &isDone);
+					scanvalue = ExecEvalExprSwitchContext(scanexpr,
+														  econtext,
+														  &isNull,
+														  &isDone);
 					scan_keys[j].sk_argument = scanvalue;
 					if (isNull)
 						scan_keys[j].sk_flags |= SK_ISNULL;
@@ -401,11 +420,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 		skey = scanKeys[i];
 		index_rescan(scan, direction, skey);
 	}
-	/* ----------------
-	 *	perhaps return something meaningful
-	 * ----------------
-	 */
-	return;
 }
 
 /* ----------------------------------------------------------------
@@ -421,7 +435,7 @@ ExecEndIndexScan(IndexScan *node)
 {
 	CommonScanState *scanstate;
 	IndexScanState *indexstate;
-	Pointer    *runtimeKeyInfo;
+	int		  **runtimeKeyInfo;
 	ScanKey    *scanKeys;
 	List	   *indxqual;
 	int		   *numScanKeys;
@@ -431,7 +445,7 @@ ExecEndIndexScan(IndexScan *node)
 	scanstate = node->scan.scanstate;
 	indexstate = node->indxstate;
 	indxqual = node->indxqual;
-	runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+	runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
 
 	/* ----------------
 	 *	extract information from the node
@@ -451,6 +465,9 @@ ExecEndIndexScan(IndexScan *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&scanstate->cstate);
+	ExecFreeExprContext(&scanstate->cstate);
+	if (indexstate->iss_RuntimeContext)
+		FreeExprContext(indexstate->iss_RuntimeContext);
 
 	/* ----------------
 	 *	close the heap and index relations
@@ -474,12 +491,7 @@ ExecEndIndexScan(IndexScan *node)
 	{
 		for (i = 0; i < numIndices; i++)
 		{
-			List	   *qual;
-			int			n_keys;
-
-			qual = nth(i, indxqual);
-			n_keys = length(qual);
-			if (n_keys > 0)
+			if (runtimeKeyInfo[i] != NULL)
 				pfree(runtimeKeyInfo[i]);
 		}
 		pfree(runtimeKeyInfo);
@@ -491,7 +503,6 @@ ExecEndIndexScan(IndexScan *node)
 	 */
 	ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
 	ExecClearTuple(scanstate->css_ScanTupleSlot);
-/*	  ExecClearTuple(scanstate->css_RawTupleSlot); */
 }
 
 /* ----------------------------------------------------------------
@@ -562,7 +573,7 @@ ExecIndexRestrPos(IndexScan *node)
  *
  * old comments
  *		Creates the run-time state information for the node and
- *		sets the relation id to contain relevant decriptors.
+ *		sets the relation id to contain relevant descriptors.
  *
  *		Parameters:
  *		  node: IndexNode node produced by the planner.
@@ -583,19 +594,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	int		   *numScanKeys;
 	RelationPtr relationDescs;
 	IndexScanDescPtr scanDescs;
-	Pointer    *runtimeKeyInfo;
+	int		  **runtimeKeyInfo;
 	bool		have_runtime_keys;
 	List	   *rangeTable;
 	RangeTblEntry *rtentry;
 	Index		relid;
 	Oid			reloid;
-
 	Relation	currentRelation;
 	HeapScanDesc currentScanDesc;
 	ScanDirection direction;
-	int			baseid;
-
-	List	   *execParam = NULL;
+	List	   *execParam = NIL;
 
 	/* ----------------
 	 *	assign execution state to node
@@ -610,25 +618,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	 * --------------------------------
 	 */
 	scanstate = makeNode(CommonScanState);
-/*
-	scanstate->ss_ProcOuterFlag = false;
-	scanstate->ss_OldRelId = 0;
-*/
-
 	node->scan.scanstate = scanstate;
 
 	/* ----------------
-	 *	assign node's base_id .. we don't use AssignNodeBaseid() because
-	 *	the increment is done later on after we assign the index scan's
-	 *	scanstate.	see below.
-	 * ----------------
-	 */
-	baseid = estate->es_BaseId;
-/*	  scanstate->csstate.cstate.bnode.base_id = baseid; */
-	scanstate->cstate.cs_base_id = baseid;
-
-	/* ----------------
-	 *	create expression context for node
+	 *	Miscellaneous initialization
+	 *
+	 *		 +	create expression context for node
 	 * ----------------
 	 */
 	ExecAssignExprContext(estate, &scanstate->cstate);
@@ -640,7 +635,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	 */
 	ExecInitResultTupleSlot(estate, &scanstate->cstate);
 	ExecInitScanTupleSlot(estate, scanstate);
-/*	  ExecInitRawTupleSlot(estate, scanstate); */
 
 	/* ----------------
 	 *	initialize projection info.  result type comes from scan desc
@@ -661,19 +655,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	indexstate->iss_ScanKeys = NULL;
 	indexstate->iss_NumScanKeys = NULL;
 	indexstate->iss_RuntimeKeyInfo = NULL;
+	indexstate->iss_RuntimeContext = NULL;
 	indexstate->iss_RelationDescs = NULL;
 	indexstate->iss_ScanDescs = NULL;
 
 	node->indxstate = indexstate;
 
-	/* ----------------
-	 *	assign base id to index scan state also
-	 * ----------------
-	 */
-	indexstate->cstate.cs_base_id = baseid;
-	baseid++;
-	estate->es_BaseId = baseid;
-
 	/* ----------------
 	 *	get the index node information
 	 * ----------------
@@ -696,12 +683,11 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
 
 	/* ----------------
-	 *	initialize runtime key info.
+	 *	initialize space for runtime key info (may not be needed)
 	 * ----------------
 	 */
 	have_runtime_keys = false;
-	runtimeKeyInfo = (Pointer *)
-		palloc(numIndices * sizeof(Pointer));
+	runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
 
 	/* ----------------
 	 *	build the index scan keys from the index qualification
@@ -719,9 +705,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 		qual = lfirst(indxqual);
 		indxqual = lnext(indxqual);
 		n_keys = length(qual);
-		scan_keys = (n_keys <= 0) ? NULL :
+		scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
 			(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
-		run_keys = (n_keys <= 0) ? NULL :
+		run_keys = (n_keys <= 0) ? (int *) NULL :
 			(int *) palloc(n_keys * sizeof(int));
 
 		CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
@@ -966,12 +952,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 		}
 
 		/* ----------------
-		 *	store the key information into our array.
+		 *	store the key information into our arrays.
 		 * ----------------
 		 */
 		numScanKeys[i] = n_keys;
 		scanKeys[i] = scan_keys;
-		runtimeKeyInfo[i] = (Pointer) run_keys;
+		runtimeKeyInfo[i] = run_keys;
 	}
 
 	indexstate->iss_NumIndices = numIndices;
@@ -988,12 +974,35 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	 *	(one for each index) to arrays of flags (one for each key)
 	 *	which indicate that the qual needs to be evaluated at runtime.
 	 *	-cim 10/24/89
+	 *
+	 *	If we do have runtime keys, we need an ExprContext to evaluate them;
+	 *	the node's standard context won't do because we want to reset that
+	 *	context for every tuple.  So, build another context just like the
+	 *	other one...
+	 *	-tgl 7/11/00
 	 * ----------------
 	 */
 	if (have_runtime_keys)
-		indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
+	{
+		ExprContext	   *stdecontext = scanstate->cstate.cs_ExprContext;
+
+		ExecAssignExprContext(estate, &scanstate->cstate);
+		indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
+		indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext;
+		scanstate->cstate.cs_ExprContext = stdecontext;
+	}
 	else
+	{
 		indexstate->iss_RuntimeKeyInfo = NULL;
+		indexstate->iss_RuntimeContext = NULL;
+		/* Get rid of the speculatively-allocated flag arrays, too */
+		for (i = 0; i < numIndices; i++)
+		{
+			if (runtimeKeyInfo[i] != NULL)
+				pfree(runtimeKeyInfo[i]);
+		}
+		pfree(runtimeKeyInfo);
+	}
 
 	/* ----------------
 	 *	get the range table and direction information
@@ -1026,7 +1035,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	scanstate->css_currentRelation = currentRelation;
 	scanstate->css_currentScanDesc = currentScanDesc;
 
-
 	/* ----------------
 	 *	get the scan type from the relation descriptor.
 	 * ----------------
@@ -1034,12 +1042,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
 	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
 
-	/* ----------------
-	 *	index scans don't have subtrees..
-	 * ----------------
-	 */
-/*	  scanstate->ss_ProcOuterFlag = false; */
-
 	/* ----------------
 	 *	open the index relations and initialize
 	 *	relation and scan descriptors.
@@ -1066,10 +1068,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	indexstate->iss_RelationDescs = relationDescs;
 	indexstate->iss_ScanDescs = scanDescs;
 
-	indexstate->cstate.cs_TupFromTlist = false;
-
 	/*
-	 * if there are some PARAM_EXEC in skankeys then force index rescan on
+	 * if there are some PARAM_EXEC in scankeys then force index rescan on
 	 * first scan.
 	 */
 	((Plan *) node)->chgParam = execParam;
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 1d5c9042489..c0a94066bea 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.31 2000/06/18 22:44:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.32 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,17 +158,12 @@ ExecInitMaterial(Material *node, EState *estate, Plan *parent)
 	node->matstate = matstate;
 
 	/* ----------------
-	 *	Miscellanious initialization
-	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
-	 *		 +	assign result tuple slot
+	 *	Miscellaneous initialization
 	 *
 	 *	Materialization nodes don't need ExprContexts because
-	 *	they never call ExecQual or ExecTargetList.
+	 *	they never call ExecQual or ExecProject.
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
 
 #define MATERIAL_NSLOTS 1
 	/* ----------------
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 0d522f0d515..a3f92b06901 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.35 2000/06/15 04:09:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -202,45 +202,53 @@ MJFormSkipQual(List *qualList, char *replaceopname)
 static bool
 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 {
+	bool		result;
+	MemoryContext oldContext;
 	List	   *clause;
 	List	   *eqclause;
-	Datum		const_value;
-	bool		isNull;
-	bool		isDone;
 
-	/* ----------------
-	 *	if we have no compare qualification, return nil
-	 * ----------------
+	/*
+	 * Do expression eval in short-lived context.
 	 */
-	if (compareQual == NIL)
-		return false;
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
 	/* ----------------
 	 *	for each pair of clauses, test them until
-	 *	our compare conditions are satisfied
+	 *	our compare conditions are satisfied.
+	 *	if we reach the end of the list, none of our key greater-than
+	 *	conditions were satisfied so we return false.
 	 * ----------------
 	 */
+	result = false;				/* assume 'false' result */
+
 	eqclause = eqQual;
 	foreach(clause, compareQual)
 	{
+		Datum		const_value;
+		bool		isNull;
+		bool		isDone;
+
 		/* ----------------
 		 *	 first test if our compare clause is satisfied.
-		 *	 if so then return true. ignore isDone, don't iterate in
-		 *	 quals.
+		 *	 if so then return true.
+		 *
+		 *	 A NULL result is considered false.
+		 *	 ignore isDone, don't iterate in quals.
 		 * ----------------
 		 */
-		const_value = (Datum)
-			ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone);
+		const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
+								   &isNull, &isDone);
 
-		if (DatumGetInt32(const_value) != 0)
-			return true;
+		if (DatumGetBool(const_value) && !isNull)
+		{
+			result = true;
+			break;
+		}
 
 		/* ----------------
 		 *	 ok, the compare clause failed so we test if the keys
 		 *	 are equal... if key1 != key2, we return false.
 		 *	 otherwise key1 = key2 so we move on to the next pair of keys.
-		 *
-		 *	 ignore isDone, don't iterate in quals.
 		 * ----------------
 		 */
 		const_value = ExecEvalExpr((Node *) lfirst(eqclause),
@@ -248,17 +256,15 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 								   &isNull,
 								   &isDone);
 
-		if (DatumGetInt32(const_value) == 0)
-			return false;
+		if (! DatumGetBool(const_value) || isNull)
+			break;				/* return false */
+
 		eqclause = lnext(eqclause);
 	}
 
-	/* ----------------
-	 *	if we get here then it means none of our key greater-than
-	 *	conditions were satisfied so we return false.
-	 * ----------------
-	 */
-	return false;
+	MemoryContextSwitchTo(oldContext);
+
+	return result;
 }
 
 /* ----------------------------------------------------------------
@@ -403,24 +409,18 @@ ExecMergeJoin(MergeJoin *node)
 	List	   *qual;
 	bool		qualResult;
 	bool		compareResult;
-
 	Plan	   *innerPlan;
 	TupleTableSlot *innerTupleSlot;
-
 	Plan	   *outerPlan;
 	TupleTableSlot *outerTupleSlot;
-
 	ExprContext *econtext;
-
 #ifdef ENABLE_OUTER_JOINS
-
 	/*
 	 * These should be set from the expression context! - thomas
 	 * 1999-02-20
 	 */
 	static bool isLeftJoin = true;
 	static bool isRightJoin = false;
-
 #endif
 
 	/* ----------------
@@ -448,20 +448,34 @@ ExecMergeJoin(MergeJoin *node)
 	}
 
 	/* ----------------
-	 *	ok, everything is setup.. let's go to work
+	 *	Reset per-tuple memory context to free any expression evaluation
+	 *	storage allocated in the previous tuple cycle.
+	 * ----------------
+	 */
+	ResetExprContext(econtext);
+
+	/* ----------------
+	 *	Check to see if we're still projecting out tuples from a previous
+	 *	join tuple (because there is a function-returning-set in the
+	 *	projection expressions).  If so, try to project another one.
 	 * ----------------
 	 */
 	if (mergestate->jstate.cs_TupFromTlist)
 	{
 		TupleTableSlot *result;
-		ProjectionInfo *projInfo;
 		bool		isDone;
 
-		projInfo = mergestate->jstate.cs_ProjInfo;
-		result = ExecProject(projInfo, &isDone);
+		result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
 		if (!isDone)
 			return result;
+		/* Done with that source tuple... */
+		mergestate->jstate.cs_TupFromTlist = false;
 	}
+
+	/* ----------------
+	 *	ok, everything is setup.. let's go to work
+	 * ----------------
+	 */
 	for (;;)
 	{
 		/* ----------------
@@ -547,6 +561,8 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_JOINTEST:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
 
+				ResetExprContext(econtext);
+
 				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
@@ -565,6 +581,14 @@ ExecMergeJoin(MergeJoin *node)
 				MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
 				mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
 
+				/*
+				 * Check the qpqual to see if we actually want to return
+				 * this join tuple.  If not, can proceed with merge.
+				 *
+				 * (We don't bother with a ResetExprContext here, on the
+				 * assumption that we just did one before checking the merge
+				 * qual.  One per tuple should be sufficient.)
+				 */
 				qualResult = ExecQual((List *) qual, econtext, false);
 				MJ_DEBUG_QUAL(qual, qualResult);
 
@@ -693,6 +717,8 @@ ExecMergeJoin(MergeJoin *node)
 				innerTupleSlot = econtext->ecxt_innertuple;
 				econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
 
+				ResetExprContext(econtext);
+
 				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
@@ -709,11 +735,7 @@ ExecMergeJoin(MergeJoin *node)
 					 */
 
 					ExecRestrPos(innerPlan);
-#if 0
-					mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
-#endif
 					mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
-
 				}
 				else
 				{
@@ -777,6 +799,8 @@ ExecMergeJoin(MergeJoin *node)
 				 *	we update the marked tuple and go join them.
 				 * ----------------
 				 */
+				ResetExprContext(econtext);
+
 				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
@@ -886,6 +910,8 @@ ExecMergeJoin(MergeJoin *node)
 				 *	we update the marked tuple and go join them.
 				 * ----------------
 				 */
+				ResetExprContext(econtext);
+
 				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
@@ -1142,12 +1168,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	/* ----------------
 	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
 	ExecAssignExprContext(estate, &mergestate->jstate);
 
 #define MERGEJOIN_NSLOTS 2
@@ -1251,6 +1274,7 @@ ExecEndMergeJoin(MergeJoin *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&mergestate->jstate);
+	ExecFreeExprContext(&mergestate->jstate);
 
 	/* ----------------
 	 *	shut down the subplans
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index ec71ad7ac71..0186e394367 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.16 2000/06/15 04:09:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,18 +32,18 @@
  *
  *		It scans the inner relation to join with current outer tuple.
  *
- *		If none is found, next tuple form the outer relation is retrieved
+ *		If none is found, next tuple from the outer relation is retrieved
  *		and the inner relation is scanned from the beginning again to join
  *		with the outer tuple.
  *
- *		Nil is returned if all the remaining outer tuples are tried and
+ *		NULL is returned if all the remaining outer tuples are tried and
  *		all fail to join with the inner tuples.
  *
- *		Nil is also returned if there is no tuple from inner realtion.
+ *		NULL is also returned if there is no tuple from inner relation.
  *
  *		Conditions:
  *		  -- outerTuple contains current tuple from outer relation and
- *			 the right son(inner realtion) maintains "cursor" at the tuple
+ *			 the right son(inner relation) maintains "cursor" at the tuple
  *			 returned previously.
  *				This is achieved by maintaining a scan position on the outer
  *				relation.
@@ -60,10 +60,8 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 	Plan	   *innerPlan;
 	Plan	   *outerPlan;
 	bool		needNewOuterTuple;
-
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-
 	List	   *qual;
 	ExprContext *econtext;
 
@@ -77,11 +75,6 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 	qual = node->join.qual;
 	outerPlan = outerPlan(&node->join);
 	innerPlan = innerPlan(&node->join);
-
-	/* ----------------
-	 *	initialize expression context
-	 * ----------------
-	 */
 	econtext = nlstate->jstate.cs_ExprContext;
 
 	/* ----------------
@@ -92,11 +85,18 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 	econtext->ecxt_outertuple = outerTupleSlot;
 
 	/* ----------------
-	 *	Ok, everything is setup for the join so now loop until
-	 *	we return a qualifying join tuple..
+	 *	Reset per-tuple memory context to free any expression evaluation
+	 *	storage allocated in the previous tuple cycle.
 	 * ----------------
 	 */
+	ResetExprContext(econtext);
 
+	/* ----------------
+	 *	Check to see if we're still projecting out tuples from a previous
+	 *	join tuple (because there is a function-returning-set in the
+	 *	projection expressions).  If so, try to project another one.
+	 * ----------------
+	 */
 	if (nlstate->jstate.cs_TupFromTlist)
 	{
 		TupleTableSlot *result;
@@ -105,9 +105,17 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 		result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
 		if (!isDone)
 			return result;
+		/* Done with that source tuple... */
+		nlstate->jstate.cs_TupFromTlist = false;
 	}
 
+	/* ----------------
+	 *	Ok, everything is setup for the join so now loop until
+	 *	we return a qualifying join tuple..
+	 * ----------------
+	 */
 	ENL1_printf("entering main loop");
+
 	for (;;)
 	{
 		/* ----------------
@@ -115,15 +123,7 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 		 *	and join it with the current outer tuple.
 		 * ----------------
 		 */
-		needNewOuterTuple = false;
-
-		if (!TupIsNull(outerTupleSlot))
-			ENL1_printf("have outer tuple, deal with it");
-		else
-		{
-			ENL1_printf("outer tuple is nil, need new outer tuple");
-			needNewOuterTuple = true;
-		}
+		needNewOuterTuple = TupIsNull(outerTupleSlot);
 
 		/* ----------------
 		 *	if we have an outerTuple, try to get the next inner tuple.
@@ -229,9 +229,11 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 		}
 
 		/* ----------------
-		 *	qualification failed so we have to try again..
+		 *	Tuple fails qual, so free per-tuple memory and try again.
 		 * ----------------
 		 */
+		ResetExprContext(econtext);
+
 		ENL1_printf("qualification failed, looping");
 	}
 }
@@ -263,18 +265,14 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
 	 * ----------------
 	 */
 	nlstate = makeNode(NestLoopState);
-	nlstate->nl_PortalFlag = false;
 	node->nlstate = nlstate;
 
 	/* ----------------
-	 *	Miscellanious initialization
+	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
 	ExecAssignExprContext(estate, &nlstate->jstate);
 
 #define NESTLOOP_NSLOTS 1
@@ -348,6 +346,7 @@ ExecEndNestLoop(NestLoop *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&nlstate->jstate);
+	ExecFreeExprContext(&nlstate->jstate);
 
 	/* ----------------
 	 *	close down subplans
@@ -386,9 +385,7 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
 	if (outerPlan->chgParam == NULL)
 		ExecReScan(outerPlan, exprCtxt, (Plan *) node);
 
-	/* let outerPlan to free its result typle ... */
+	/* let outerPlan to free its result tuple ... */
 	nlstate->jstate.cs_OuterTupleSlot = NULL;
 	nlstate->jstate.cs_TupFromTlist = false;
-
-	return;
 }
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 5bf132520cb..a1daaf52c4b 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -3,21 +3,18 @@
  * nodeResult.c
  *	  support for constant nodes needing special code.
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
  * DESCRIPTION
  *
- *		Example: in constant queries where no relations are scanned,
- *		the planner generates result nodes.  Examples of such queries are:
+ *		Result nodes are used in queries where no relations are scanned.
+ *		Examples of such queries are:
  *
  *				retrieve (x = 1)
  *		and
  *				append emp (name = "mike", salary = 15000)
  *
- *		Result nodes are also used to optimise queries
- *		with tautological qualifications like:
+ *		Result nodes are also used to optimise queries with constant
+ *		qualifications (ie, quals that do not depend on the scanned data),
+ *		such as:
  *
  *				retrieve (emp.all) where 2 > 1
  *
@@ -27,13 +24,22 @@
  *						/
  *				   SeqScan (emp.all)
  *
+ *		At runtime, the Result node evaluates the constant qual once.
+ *		If it's false, we can return an empty result set without running
+ *		the controlled plan at all.  If it's true, we run the controlled
+ *		plan normally and pass back the results.
+ *
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.13 2000/01/26 05:56:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.14 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#include "postgres.h"
 
+#include "postgres.h"
 
 #include "executor/executor.h"
 #include "executor/nodeResult.h"
@@ -41,7 +47,7 @@
 /* ----------------------------------------------------------------
  *		ExecResult(node)
  *
- *		returns the tuples from the outer plan which satisify the
+ *		returns the tuples from the outer plan which satisfy the
  *		qualification clause.  Since result nodes with right
  *		subtrees are never planned, we ignore the right subtree
  *		entirely (for now).. -cim 10/7/89
@@ -67,15 +73,17 @@ ExecResult(Result *node)
 	 * ----------------
 	 */
 	resstate = node->resstate;
+	econtext = resstate->cstate.cs_ExprContext;
 
 	/* ----------------
-	 *	get the expression context
+	 *	Reset per-tuple memory context to free any expression evaluation
+	 *	storage allocated in the previous tuple cycle.
 	 * ----------------
 	 */
-	econtext = resstate->cstate.cs_ExprContext;
+	ResetExprContext(econtext);
 
 	/* ----------------
-	 *	 check tautological qualifications like (2 > 1)
+	 *	 check constant qualifications like (2 > 1), if not already done
 	 * ----------------
 	 */
 	if (resstate->rs_checkqual)
@@ -92,74 +100,64 @@ ExecResult(Result *node)
 		}
 	}
 
+	/* ----------------
+	 *	Check to see if we're still projecting out tuples from a previous
+	 *	scan tuple (because there is a function-returning-set in the
+	 *	projection expressions).  If so, try to project another one.
+	 * ----------------
+	 */
 	if (resstate->cstate.cs_TupFromTlist)
 	{
-		ProjectionInfo *projInfo;
-
-		projInfo = resstate->cstate.cs_ProjInfo;
-		resultSlot = ExecProject(projInfo, &isDone);
+		resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
 		if (!isDone)
 			return resultSlot;
+		/* Done with that source tuple... */
+		resstate->cstate.cs_TupFromTlist = false;
 	}
 
 	/* ----------------
-	 *	retrieve a tuple that satisfy the qual from the outer plan until
-	 *	there are no more.
-	 *
-	 *	if rs_done is 1 then it means that we were asked to return
-	 *	a constant tuple and we alread did the last time ExecResult()
-	 *	was called, so now we are through.
+	 *	if rs_done is true then it means that we were asked to return
+	 *	a constant tuple and we already did the last time ExecResult()
+	 *	was called, OR that we failed the constant qual check.
+	 *	Either way, now we are through.
 	 * ----------------
 	 */
-	outerPlan = outerPlan(node);
-
-	while (!resstate->rs_done)
+	if (!resstate->rs_done)
 	{
+		outerPlan = outerPlan(node);
 
-		/* ----------------
-		 *	  get next outer tuple if necessary.
-		 * ----------------
-		 */
 		if (outerPlan != NULL)
 		{
+			/* ----------------
+			 *	retrieve tuples from the outer plan until there are no more.
+			 * ----------------
+			 */
 			outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
 
 			if (TupIsNull(outerTupleSlot))
 				return NULL;
 
 			resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
+
+			/* ----------------
+			 *	 XXX gross hack. use outer tuple as scan tuple for projection
+			 * ----------------
+			 */
+			econtext->ecxt_outertuple = outerTupleSlot;
+			econtext->ecxt_scantuple = outerTupleSlot;
 		}
 		else
 		{
-
 			/* ----------------
-			 *	if we don't have an outer plan, then it's probably
-			 *	the case that we are doing a retrieve or an append
-			 *	with a constant target list, so we should only return
-			 *	the constant tuple once or never if we fail the qual.
+			 *	if we don't have an outer plan, then we are just generating
+			 *	the results from a constant target list.  Do it only once.
 			 * ----------------
 			 */
-			resstate->rs_done = 1;
+			resstate->rs_done = true;
 		}
 
 		/* ----------------
-		 *	  get the information to place into the expr context
-		 * ----------------
-		 */
-		resstate = node->resstate;
-
-		outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
-
-		/* ----------------
-		 *	 fill in the information in the expression context
-		 *	 XXX gross hack. use outer tuple as scan tuple
-		 * ----------------
-		 */
-		econtext->ecxt_outertuple = outerTupleSlot;
-		econtext->ecxt_scantuple = outerTupleSlot;
-
-		/* ----------------
-		 *	 form the result tuple and pass it back using ExecProject()
+		 *	 form the result tuple using ExecProject(), and return it.
 		 * ----------------
 		 */
 		projInfo = resstate->cstate.cs_ProjInfo;
@@ -200,14 +198,11 @@ ExecInitResult(Result *node, EState *estate, Plan *parent)
 	node->resstate = resstate;
 
 	/* ----------------
-	 *	Miscellanious initialization
+	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
 	ExecAssignExprContext(estate, &resstate->cstate);
 
 #define RESULT_NSLOTS 1
@@ -247,7 +242,7 @@ ExecCountSlotsResult(Result *node)
 /* ----------------------------------------------------------------
  *		ExecEndResult
  *
- *		fees up storage allocated through C routines
+ *		frees up storage allocated through C routines
  * ----------------------------------------------------------------
  */
 void
@@ -266,9 +261,8 @@ ExecEndResult(Result *node)
 	 *		  is freed at end-transaction time.  -cim 6/2/91
 	 * ----------------
 	 */
-	ExecFreeExprContext(&resstate->cstate);		/* XXX - new for us - er1p */
-	ExecFreeTypeInfo(&resstate->cstate);		/* XXX - new for us - er1p */
 	ExecFreeProjectionInfo(&resstate->cstate);
+	ExecFreeExprContext(&resstate->cstate);
 
 	/* ----------------
 	 *	shut down subplans
@@ -301,5 +295,4 @@ ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent)
 	if (((Plan *) node)->lefttree &&
 		((Plan *) node)->lefttree->chgParam == NULL)
 		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
 }
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 24de6184010..b953dcd3697 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.23 2000/06/15 04:09:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.24 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,8 +31,7 @@
 #include "parser/parsetree.h"
 
 static Oid InitScanRelation(SeqScan *node, EState *estate,
-				 CommonScanState *scanstate, Plan *outerPlan);
-
+							CommonScanState *scanstate);
 static TupleTableSlot *SeqNext(SeqScan *node);
 
 /* ----------------------------------------------------------------
@@ -132,25 +131,11 @@ SeqNext(SeqScan *node)
 TupleTableSlot *
 ExecSeqScan(SeqScan *node)
 {
-	TupleTableSlot *slot;
-	Plan	   *outerPlan;
-
-	S_printf("ExecSeqScan: scanning node: ");
-	S_nodeDisplay(node);
-
 	/* ----------------
-	 * if there is an outer subplan, get a tuple from it
-	 * else, scan the relation
+	 *	use SeqNext as access method
 	 * ----------------
 	 */
-	if ((outerPlan = outerPlan((Plan *) node)) != NULL)
-		slot = ExecProcNode(outerPlan, (Plan *) node);
-	else
-		slot = ExecScan(node, SeqNext);
-
-	S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
-
-	return slot;
+	return ExecScan(node, (ExecScanAccessMtd) SeqNext);
 }
 
 /* ----------------------------------------------------------------
@@ -162,7 +147,7 @@ ExecSeqScan(SeqScan *node)
  */
 static Oid
 InitScanRelation(SeqScan *node, EState *estate,
-				 CommonScanState *scanstate, Plan *outerPlan)
+				 CommonScanState *scanstate)
 {
 	Index		relid;
 	List	   *rangeTable;
@@ -173,84 +158,56 @@ InitScanRelation(SeqScan *node, EState *estate,
 	HeapScanDesc currentScanDesc;
 	RelationInfo *resultRelationInfo;
 
-	if (outerPlan == NULL)
-	{
-		/* ----------------
-		 * if the outer node is nil then we are doing a simple
-		 * sequential scan of a relation...
-		 *
-		 * get the relation object id from the relid'th entry
-		 * in the range table, open that relation and initialize
-		 * the scan state...
-		 * ----------------
-		 */
-		relid = node->scanrelid;
-		rangeTable = estate->es_range_table;
-		rtentry = rt_fetch(relid, rangeTable);
-		reloid = rtentry->relid;
-		direction = estate->es_direction;
-		resultRelationInfo = estate->es_result_relation_info;
-
-		ExecOpenScanR(reloid,	/* relation */
-					  0,		/* nkeys */
-					  NULL,		/* scan key */
-					  0,		/* is index */
-					  direction,/* scan direction */
-					  estate->es_snapshot,
-					  &currentRelation, /* return: rel desc */
-					  (Pointer *) &currentScanDesc);	/* return: scan desc */
-
-		scanstate->css_currentRelation = currentRelation;
-		scanstate->css_currentScanDesc = currentScanDesc;
-
-		ExecAssignScanType(scanstate,
-						   RelationGetDescr(currentRelation));
-	}
-	else
-	{
-		/* ----------------
-		 *	 otherwise we are scanning tuples from the
-		 *	 outer subplan so we initialize the outer plan
-		 *	 and nullify
-		 * ----------------
-		 */
-		ExecInitNode(outerPlan, estate, (Plan *) node);
-
-		node->scanrelid = 0;
-		scanstate->css_currentRelation = NULL;
-		scanstate->css_currentScanDesc = NULL;
-		ExecAssignScanType(scanstate, NULL);
-		reloid = InvalidOid;
-	}
-
 	/* ----------------
-	 *	return the relation
+	 * get the relation object id from the relid'th entry
+	 * in the range table, open that relation and initialize
+	 * the scan state...
 	 * ----------------
 	 */
+	relid = node->scanrelid;
+	rangeTable = estate->es_range_table;
+	rtentry = rt_fetch(relid, rangeTable);
+	reloid = rtentry->relid;
+	direction = estate->es_direction;
+	resultRelationInfo = estate->es_result_relation_info;
+
+	ExecOpenScanR(reloid,		/* relation */
+				  0,			/* nkeys */
+				  NULL,			/* scan key */
+				  0,			/* is index */
+				  direction,	/* scan direction */
+				  estate->es_snapshot,
+				  &currentRelation, /* return: rel desc */
+				  (Pointer *) &currentScanDesc); /* return: scan desc */
+
+	scanstate->css_currentRelation = currentRelation;
+	scanstate->css_currentScanDesc = currentScanDesc;
+
+	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
+
 	return reloid;
 }
 
 
 /* ----------------------------------------------------------------
  *		ExecInitSeqScan
- *
- * old comments
- *		Creates the run-time state information for the seqscan node
- *		and sets the relation id to contain relevant descriptors.
- *
- *		If there is a outer subtree (sort), the outer subtree
- *		is initialized and the relation id is set to the descriptors
- *		returned by the subtree.
  * ----------------------------------------------------------------
  */
 bool
 ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
 {
 	CommonScanState *scanstate;
-	Plan	   *outerPlan;
 	Oid			reloid;
 	HeapScanDesc scandesc;
 
+	/* ----------------
+	 *	Once upon a time it was possible to have an outerPlan of a SeqScan,
+	 *	but not any more.
+	 * ----------------
+	 */
+	Assert(outerPlan((Plan *) node) == NULL);
+	Assert(innerPlan((Plan *) node) == NULL);
+
 	/* ----------------
 	 *	assign the node's execution state
 	 * ----------------
@@ -265,13 +222,11 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
 	node->scanstate = scanstate;
 
 	/* ----------------
-	 *	Miscellanious initialization
+	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
 	 *		 +	create expression context for node
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
 	ExecAssignExprContext(estate, &scanstate->cstate);
 
 #define SEQSCAN_NSLOTS 3
@@ -283,12 +238,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
 	ExecInitScanTupleSlot(estate, scanstate);
 
 	/* ----------------
-	 *	initialize scan relation or outer subplan
+	 *	initialize scan relation
 	 * ----------------
 	 */
-	outerPlan = outerPlan((Plan *) node);
-
-	reloid = InitScanRelation(node, estate, scanstate, outerPlan);
+	reloid = InitScanRelation(node, estate, scanstate);
 
 	scandesc = scanstate->css_currentScanDesc;
 	scanstate->cstate.cs_TupFromTlist = false;
@@ -315,15 +268,12 @@ ExecCountSlotsSeqScan(SeqScan *node)
  *		ExecEndSeqScan
  *
  *		frees any storage allocated through C routines.
- *|		...and also closes relations and/or shuts down outer subplan
- *|		-cim 8/14/89
  * ----------------------------------------------------------------
  */
 void
 ExecEndSeqScan(SeqScan *node)
 {
 	CommonScanState *scanstate;
-	Plan	   *outerPlan;
 
 	/* ----------------
 	 *	get information from node
@@ -341,6 +291,7 @@ ExecEndSeqScan(SeqScan *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&scanstate->cstate);
+	ExecFreeExprContext(&scanstate->cstate);
 
 	/* ----------------
 	 * close scan relation
@@ -348,13 +299,6 @@ ExecEndSeqScan(SeqScan *node)
 	 */
 	ExecCloseR((Plan *) node);
 
-	/* ----------------
-	 * clean up outer subtree (does nothing if there is no outerPlan)
-	 * ----------------
-	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecEndNode(outerPlan, (Plan *) node);
-
 	/* ----------------
 	 *	clean out the tuple table
 	 * ----------------
@@ -367,6 +311,7 @@ ExecEndSeqScan(SeqScan *node)
  *						Join Support
  * ----------------------------------------------------------------
  */
+
 /* ----------------------------------------------------------------
  *		ExecSeqReScan
  *
@@ -378,7 +323,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
 {
 	CommonScanState *scanstate;
 	EState	   *estate;
-	Plan	   *outerPlan;
 	Relation	rel;
 	HeapScanDesc scan;
 	ScanDirection direction;
@@ -386,28 +330,18 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
 	scanstate = node->scanstate;
 	estate = node->plan.state;
 
-	if ((outerPlan = outerPlan((Plan *) node)) != NULL)
-	{
-		/* we are scanning a subplan */
-		outerPlan = outerPlan((Plan *) node);
-		ExecReScan(outerPlan, exprCtxt, parent);
-	}
-	else
-/* otherwise, we are scanning a relation */
+	/* If this is re-scanning of PlanQual ... */
+	if (estate->es_evTuple != NULL &&
+		estate->es_evTuple[node->scanrelid - 1] != NULL)
 	{
-		/* If this is re-scanning of PlanQual ... */
-		if (estate->es_evTuple != NULL &&
-			estate->es_evTuple[node->scanrelid - 1] != NULL)
-		{
-			estate->es_evTupleNull[node->scanrelid - 1] = false;
-			return;
-		}
-		rel = scanstate->css_currentRelation;
-		scan = scanstate->css_currentScanDesc;
-		direction = estate->es_direction;
-		scan = ExecReScanR(rel, scan, direction, 0, NULL);
-		scanstate->css_currentScanDesc = scan;
+		estate->es_evTupleNull[node->scanrelid - 1] = false;
+		return;
 	}
+	rel = scanstate->css_currentRelation;
+	scan = scanstate->css_currentScanDesc;
+	direction = estate->es_direction;
+	scan = ExecReScanR(rel, scan, direction, 0, NULL);
+	scanstate->css_currentScanDesc = scan;
 }
 
 /* ----------------------------------------------------------------
@@ -420,33 +354,11 @@ void
 ExecSeqMarkPos(SeqScan *node)
 {
 	CommonScanState *scanstate;
-	Plan	   *outerPlan;
 	HeapScanDesc scan;
 
 	scanstate = node->scanstate;
-
-	/* ----------------
-	 *	if we are scanning a subplan then propagate
-	 *	the ExecMarkPos() request to the subplan
-	 * ----------------
-	 */
-	outerPlan = outerPlan((Plan *) node);
-	if (outerPlan)
-	{
-		ExecMarkPos(outerPlan);
-		return;
-	}
-
-	/* ----------------
-	 *	otherwise we are scanning a relation so mark the
-	 *	position using the access methods..
-	 *
-	 * ----------------
-	 */
 	scan = scanstate->css_currentScanDesc;
 	heap_markpos(scan);
-
-	return;
 }
 
 /* ----------------------------------------------------------------
@@ -459,28 +371,9 @@ void
 ExecSeqRestrPos(SeqScan *node)
 {
 	CommonScanState *scanstate;
-	Plan	   *outerPlan;
 	HeapScanDesc scan;
 
 	scanstate = node->scanstate;
-
-	/* ----------------
-	 *	if we are scanning a subplan then propagate
-	 *	the ExecRestrPos() request to the subplan
-	 * ----------------
-	 */
-	outerPlan = outerPlan((Plan *) node);
-	if (outerPlan)
-	{
-		ExecRestrPos(outerPlan);
-		return;
-	}
-
-	/* ----------------
-	 *	otherwise we are scanning a relation so restore the
-	 *	position using the access methods..
-	 * ----------------
-	 */
 	scan = scanstate->css_currentScanDesc;
 	heap_restrpos(scan);
 }
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index f8b5571a578..6f2e1f7f47b 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.28 2000/07/09 04:17:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.29 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -255,14 +255,10 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
 	/* ----------------
 	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks
-	 *
 	 *	Sort nodes don't initialize their ExprContexts because
-	 *	they never call ExecQual or ExecTargetList.
+	 *	they never call ExecQual or ExecProject.
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
 
 #define SORT_NSLOTS 1
 	/* ----------------
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 99b09f685a0..3d331c714f7 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.25 2000/04/12 17:15:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.26 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,11 +37,19 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	SubLink    *sublink = node->sublink;
 	SubLinkType subLinkType = sublink->subLinkType;
 	bool		useor = sublink->useor;
+	MemoryContext oldcontext;
 	TupleTableSlot *slot;
 	Datum		result;
+	bool		isDone;
 	bool		found = false;	/* TRUE if got at least one subplan tuple */
 	List	   *lst;
 
+	/*
+	 * We are probably in a short-lived expression-evaluation context.
+	 * Switch to longer-lived per-query context.
+	 */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
 	if (node->setParam != NIL)
 		elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
 
@@ -52,12 +60,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	{
 		foreach(lst, node->parParam)
 		{
-			ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
+			ParamExecData *prm;
 
+			prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
 			Assert(pvar != NIL);
-			prm->value = ExecEvalExpr((Node *) lfirst(pvar),
-									  econtext,
-									  &(prm->isnull), NULL);
+			prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
+												   econtext,
+												   &(prm->isnull),
+												   &isDone);
+			if (!isDone)
+				elog(ERROR, "ExecSubPlan: set values not supported for params");
 			pvar = lnext(pvar);
 		}
 		plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
@@ -84,7 +96,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	 * return NULL.  Assuming we get a tuple, we just return its first
 	 * column (there can be only one non-junk column in this case).
 	 */
-	result = (Datum) (subLinkType == ALL_SUBLINK ? true : false);
+	result = BoolGetDatum(subLinkType == ALL_SUBLINK);
 	*isNull = false;
 
 	for (slot = ExecProcNode(plan, plan);
@@ -93,12 +105,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	{
 		HeapTuple	tup = slot->val;
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
-		Datum		rowresult = (Datum) (useor ? false : true);
+		Datum		rowresult = BoolGetDatum(! useor);
 		bool		rownull = false;
 		int			col = 1;
 
 		if (subLinkType == EXISTS_SUBLINK)
-			return (Datum) true;
+		{
+			found = true;
+			result = BoolGetDatum(true);
+			break;
+		}
 
 		if (subLinkType == EXPR_SUBLINK)
 		{
@@ -172,8 +188,10 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 			/*
 			 * Now we can eval the combining operator for this column.
 			 */
-			expresult = ExecEvalExpr((Node *) expr, econtext, &expnull,
-									 (bool *) NULL);
+			expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
+												  &expnull, &isDone);
+			if (!isDone)
+				elog(ERROR, "ExecSubPlan: set values not supported for combining operators");
 
 			/*
 			 * Combine the result into the row result as appropriate.
@@ -188,9 +206,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 				/* combine within row per OR semantics */
 				if (expnull)
 					rownull = true;
-				else if (DatumGetInt32(expresult) != 0)
+				else if (DatumGetBool(expresult))
 				{
-					rowresult = (Datum) true;
+					rowresult = BoolGetDatum(true);
 					rownull = false;
 					break;		/* needn't look at any more columns */
 				}
@@ -200,9 +218,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 				/* combine within row per AND semantics */
 				if (expnull)
 					rownull = true;
-				else if (DatumGetInt32(expresult) == 0)
+				else if (! DatumGetBool(expresult))
 				{
-					rowresult = (Datum) false;
+					rowresult = BoolGetDatum(false);
 					rownull = false;
 					break;		/* needn't look at any more columns */
 				}
@@ -215,9 +233,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 			/* combine across rows per OR semantics */
 			if (rownull)
 				*isNull = true;
-			else if (DatumGetInt32(rowresult) != 0)
+			else if (DatumGetBool(rowresult))
 			{
-				result = (Datum) true;
+				result = BoolGetDatum(true);
 				*isNull = false;
 				break;			/* needn't look at any more rows */
 			}
@@ -227,9 +245,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 			/* combine across rows per AND semantics */
 			if (rownull)
 				*isNull = true;
-			else if (DatumGetInt32(rowresult) == 0)
+			else if (! DatumGetBool(rowresult))
 			{
-				result = (Datum) false;
+				result = BoolGetDatum(false);
 				*isNull = false;
 				break;			/* needn't look at any more rows */
 			}
@@ -252,11 +270,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 		 */
 		if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
 		{
-			result = (Datum) false;
+			result = (Datum) 0;
 			*isNull = true;
 		}
 	}
 
+	MemoryContextSwitchTo(oldcontext);
+
 	return result;
 }
 
@@ -277,13 +297,13 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
 		ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
 	sp_estate->es_snapshot = estate->es_snapshot;
 
-	node->shutdown = false;
+	node->needShutdown = false;
 	node->curTuple = NULL;
 
 	if (!ExecInitNode(node->plan, sp_estate, NULL))
 		return false;
 
-	node->shutdown = true;		/* now we need to shutdown the subplan */
+	node->needShutdown = true;	/* now we need to shutdown the subplan */
 
 	/*
 	 * If this plan is un-correlated or undirect correlated one and want
@@ -317,14 +337,21 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecSetParamPlan(SubPlan *node)
+ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 {
 	Plan	   *plan = node->plan;
 	SubLink    *sublink = node->sublink;
+	MemoryContext oldcontext;
 	TupleTableSlot *slot;
 	List	   *lst;
 	bool		found = false;
 
+	/*
+	 * We are probably in a short-lived expression-evaluation context.
+	 * Switch to longer-lived per-query context.
+	 */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
 	if (sublink->subLinkType == ANY_SUBLINK ||
 		sublink->subLinkType == ALL_SUBLINK)
 		elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
@@ -345,7 +372,7 @@ ExecSetParamPlan(SubPlan *node)
 			ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
 
 			prm->execPlan = NULL;
-			prm->value = (Datum) true;
+			prm->value = BoolGetDatum(true);
 			prm->isnull = false;
 			found = true;
 			break;
@@ -386,7 +413,7 @@ ExecSetParamPlan(SubPlan *node)
 			ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
 
 			prm->execPlan = NULL;
-			prm->value = (Datum) false;
+			prm->value = BoolGetDatum(false);
 			prm->isnull = false;
 		}
 		else
@@ -396,16 +423,18 @@ ExecSetParamPlan(SubPlan *node)
 				ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
 
 				prm->execPlan = NULL;
-				prm->value = (Datum) NULL;
+				prm->value = (Datum) 0;
 				prm->isnull = true;
 			}
 		}
 	}
 
+	MemoryContextSwitchTo(oldcontext);
+
 	if (plan->extParam == NULL) /* un-correlated ... */
 	{
 		ExecEndNode(plan, plan);
-		node->shutdown = false;
+		node->needShutdown = false;
 	}
 }
 
@@ -416,10 +445,10 @@ ExecSetParamPlan(SubPlan *node)
 void
 ExecEndSubPlan(SubPlan *node)
 {
-	if (node->shutdown)
+	if (node->needShutdown)
 	{
 		ExecEndNode(node->plan, node->plan);
-		node->shutdown = false;
+		node->needShutdown = false;
 	}
 	if (node->curTuple)
 	{
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0978c1ec033..824ead5ec6b 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.9 2000/06/15 04:09:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.10 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,12 +38,16 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
 	List	   *lst;
 	ItemPointer itemptr;
 	bool		isNull;
+	bool		isDone;
 	int			numTids = 0;
 
 	foreach(lst, evalList)
 	{
-		itemptr = (ItemPointer) ExecEvalExpr(lfirst(lst), econtext,
-											 &isNull, (bool *) 0);
+		itemptr = (ItemPointer)
+			DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
+													  econtext,
+													  &isNull,
+													  &isDone));
 		if (itemptr && ItemPointerIsValid(itemptr))
 		{
 			tidList[numTids] = itemptr;
@@ -243,7 +247,7 @@ ExecTidScan(TidScan *node)
 	 *	use TidNext as access method
 	 * ----------------
 	 */
-	return ExecScan(&node->scan, TidNext);
+	return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
 }
 
 /* ----------------------------------------------------------------
@@ -319,6 +323,7 @@ ExecEndTidScan(TidScan *node)
 	 * ----------------
 	 */
 	ExecFreeProjectionInfo(&scanstate->cstate);
+	ExecFreeExprContext(&scanstate->cstate);
 
 	/* ----------------
 	 *	close the heap and tid relations
@@ -332,7 +337,6 @@ ExecEndTidScan(TidScan *node)
 	 */
 	ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
 	ExecClearTuple(scanstate->css_ScanTupleSlot);
-/*	  ExecClearTuple(scanstate->css_RawTupleSlot); */
 }
 
 /* ----------------------------------------------------------------
@@ -394,11 +398,8 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	RangeTblEntry *rtentry;
 	Oid			relid;
 	Oid			reloid;
-
 	Relation	currentRelation;
-	int			baseid;
-
-	List	   *execParam = NULL;
+	List	   *execParam = NIL;
 
 	/* ----------------
 	 *	assign execution state to node
@@ -413,25 +414,12 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	 * --------------------------------
 	 */
 	scanstate = makeNode(CommonScanState);
-/*
-	scanstate->ss_ProcOuterFlag = false;
-	scanstate->ss_OldRelId = 0;
-*/
-
 	node->scan.scanstate = scanstate;
 
 	/* ----------------
-	 *	assign node's base_id .. we don't use AssignNodeBaseid() because
-	 *	the increment is done later on after we assign the tid scan's
-	 *	scanstate.	see below.
-	 * ----------------
-	 */
-	baseid = estate->es_BaseId;
-/*	  scanstate->csstate.cstate.bnode.base_id = baseid; */
-	scanstate->cstate.cs_base_id = baseid;
-
-	/* ----------------
-	 *	create expression context for node
+	 *	Miscellaneous initialization
+	 *
+	 *		 +	create expression context for node
 	 * ----------------
 	 */
 	ExecAssignExprContext(estate, &scanstate->cstate);
@@ -443,7 +431,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	 */
 	ExecInitResultTupleSlot(estate, &scanstate->cstate);
 	ExecInitScanTupleSlot(estate, scanstate);
-/*	  ExecInitRawTupleSlot(estate, scanstate); */
 
 	/* ----------------
 	 *	initialize projection info.  result type comes from scan desc
@@ -461,14 +448,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	tidstate = makeNode(TidScanState);
 	node->tidstate = tidstate;
 
-	/* ----------------
-	 *	assign base id to tid scan state also
-	 * ----------------
-	 */
-	tidstate->cstate.cs_base_id = baseid;
-	baseid++;
-	estate->es_BaseId = baseid;
-
 	/* ----------------
 	 *	get the tid node information
 	 * ----------------
@@ -514,14 +493,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
 	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
 
-	/* ----------------
-	 *	tid scans don't have subtrees..
-	 * ----------------
-	 */
-/*	  scanstate->ss_ProcOuterFlag = false; */
-
-	tidstate->cstate.cs_TupFromTlist = false;
-
 	/*
 	 * if there are some PARAM_EXEC in skankeys then force tid rescan on
 	 * first scan.
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index add569a2c21..29c790b2c76 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.29 2000/05/30 00:49:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.30 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,27 +88,32 @@ ExecUnique(Unique *node)
 		if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
 							 tupDesc,
 							 node->numCols, node->uniqColIdx,
-							 uniquestate->eqfunctions))
+							 uniquestate->eqfunctions,
+							 uniquestate->tempContext))
 			break;
 	}
 
 	/* ----------------
 	 *	We have a new tuple different from the previous saved tuple (if any).
-	 *	Save it and return it.	Note that we make two copies of the tuple:
-	 *	one to keep for our own future comparisons, and one to return to the
-	 *	caller.  We need to copy the tuple returned by the subplan to avoid
-	 *	holding buffer refcounts, and we need our own copy because the caller
-	 *	may alter the resultTupleSlot (eg via ExecRemoveJunk).
+	 *	Save it and return it.	We must copy it because the source subplan
+	 *	won't guarantee that this source tuple is still accessible after
+	 *	fetching the next source tuple.
+	 *
+	 *	Note that we manage the copy ourselves.  We can't rely on the result
+	 *	tuple slot to maintain the tuple reference because our caller may
+	 *	replace the slot contents with a different tuple (see junk filter
+	 *	handling in execMain.c).  We assume that the caller will no longer
+	 *	be interested in the current tuple after he next calls us.
 	 * ----------------
 	 */
 	if (uniquestate->priorTuple != NULL)
 		heap_freetuple(uniquestate->priorTuple);
 	uniquestate->priorTuple = heap_copytuple(slot->val);
 
-	ExecStoreTuple(heap_copytuple(slot->val),
+	ExecStoreTuple(uniquestate->priorTuple,
 				   resultTupleSlot,
 				   InvalidBuffer,
-				   true);
+				   false);		/* tuple does not belong to slot */
 
 	return resultTupleSlot;
 }
@@ -143,14 +148,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
 	/* ----------------
 	 *	Miscellaneous initialization
 	 *
-	 *		 +	assign node's base_id
-	 *		 +	assign debugging hooks and
-	 *
 	 *	Unique nodes have no ExprContext initialization because
-	 *	they never call ExecQual or ExecTargetList.
+	 *	they never call ExecQual or ExecProject.  But they do need a
+	 *	per-tuple memory context anyway for calling execTuplesMatch.
 	 * ----------------
 	 */
-	ExecAssignNodeBaseInfo(estate, &uniquestate->cstate, parent);
+	uniquestate->tempContext =
+		AllocSetContextCreate(CurrentMemoryContext,
+							  "Unique",
+							  ALLOCSET_DEFAULT_MINSIZE,
+							  ALLOCSET_DEFAULT_INITSIZE,
+							  ALLOCSET_DEFAULT_MAXSIZE);
 
 #define UNIQUE_NSLOTS 1
 	/* ------------
@@ -207,6 +215,8 @@ ExecEndUnique(Unique *node)
 
 	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
 
+	MemoryContextDelete(uniquestate->tempContext);
+
 	/* clean up tuple table */
 	ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
 	if (uniquestate->priorTuple != NULL)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 605fe70e6c1..bc305382dfe 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.115 2000/06/29 07:35:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.116 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -578,7 +578,7 @@ _copySubPlan(SubPlan *from)
 	Node_Copy(from, newnode, sublink);
 
 	/* do not copy execution state */
-	newnode->shutdown = false;
+	newnode->needShutdown = false;
 	newnode->curTuple = NULL;
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 308f4d90e87..b8814786180 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -24,7 +24,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.67 2000/06/29 07:35:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.68 2000/07/12 02:37:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -184,8 +184,8 @@ _equalConst(Const *a, Const *b)
 	 */
 	if (a->constisnull)
 		return true;
-	return (datumIsEqual(a->constvalue, b->constvalue,
-						 a->consttype, a->constbyval, a->constlen));
+	return datumIsEqual(a->constvalue, b->constvalue,
+						a->constbyval, a->constlen);
 }
 
 static bool
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b9830edc22f..2999c4263b1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.120 2000/06/18 22:44:05 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.121 2000/07/12 02:37:06 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1155,10 +1155,10 @@ _outJoinInfo(StringInfo str, JoinInfo *node)
 static void
 _outDatum(StringInfo str, Datum value, Oid type)
 {
-	char	   *s;
-	Size		length,
-				typeLength;
 	bool		byValue;
+	int			typeLength;
+	Size		length;
+	char	   *s;
 	int			i;
 
 	/*
@@ -1167,12 +1167,12 @@ _outDatum(StringInfo str, Datum value, Oid type)
 	 */
 	byValue = get_typbyval(type);
 	typeLength = get_typlen(type);
-	length = datumGetSize(value, type, byValue, typeLength);
+	length = datumGetSize(value, byValue, typeLength);
 
 	if (byValue)
 	{
 		s = (char *) (&value);
-		appendStringInfo(str, " %d [ ", length);
+		appendStringInfo(str, " %u [ ", (unsigned int) length);
 		for (i = 0; i < (int) sizeof(Datum); i++)
 			appendStringInfo(str, "%d ", (int) (s[i]));
 		appendStringInfo(str, "] ");
@@ -1184,14 +1184,7 @@ _outDatum(StringInfo str, Datum value, Oid type)
 			appendStringInfo(str, " 0 [ ] ");
 		else
 		{
-
-			/*
-			 * length is unsigned - very bad to do < comparison to -1
-			 * without casting it to int first!! -mer 8 Jan 1991
-			 */
-			if (((int) length) <= -1)
-				length = VARSIZE(s);
-			appendStringInfo(str, " %d [ ", length);
+			appendStringInfo(str, " %u [ ", (unsigned int) length);
 			for (i = 0; i < (int) length; i++)
 				appendStringInfo(str, "%d ", (int) (s[i]));
 			appendStringInfo(str, "] ");
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f872d952d03..4754cbc327a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.91 2000/06/18 22:44:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.92 2000/07/12 02:37:06 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -608,7 +608,7 @@ _readHash()
 	_getPlan((Plan *) local_node);
 
 	token = lsptok(NULL, &length);		/* eat :hashkey */
-	local_node->hashkey = (Var *) nodeRead(true);
+	local_node->hashkey = nodeRead(true);
 
 	return local_node;
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 4915133d0a6..bf91b8d1a6c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.93 2000/06/18 22:44:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.94 2000/07/12 02:37:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -66,7 +66,7 @@ static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
 			  Plan *righttree);
 static HashJoin *make_hashjoin(List *tlist, List *qpqual,
 			  List *hashclauses, Plan *lefttree, Plan *righttree);
-static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
+static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
 static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
 			   List *mergeclauses, Plan *righttree, Plan *lefttree);
 static void copy_path_costsize(Plan *dest, Path *src);
@@ -664,7 +664,7 @@ create_hashjoin_node(HashPath *best_path,
 	List	   *hashclauses;
 	HashJoin   *join_node;
 	Hash	   *hash_node;
-	Var		   *innerhashkey;
+	Node	   *innerhashkey;
 
 	/*
 	 * NOTE: there will always be exactly one hashclause in the list
@@ -694,7 +694,7 @@ create_hashjoin_node(HashPath *best_path,
 											   (Index) 0));
 
 	/* Now the righthand op of the sole hashclause is the inner hash key. */
-	innerhashkey = get_rightop(lfirst(hashclauses));
+	innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
 
 	/*
 	 * Build the hash node and hash join node.
@@ -1103,7 +1103,7 @@ make_hashjoin(List *tlist,
 }
 
 static Hash *
-make_hash(List *tlist, Var *hashkey, Plan *lefttree)
+make_hash(List *tlist, Node *hashkey, Plan *lefttree)
 {
 	Hash	   *node = makeNode(Hash);
 	Plan	   *plan = &node->plan;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index cd624fb111d..6d69171f1b4 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.38 2000/06/18 22:44:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.39 2000/07/12 02:37:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -649,7 +649,7 @@ SS_finalize_plan(Plan *plan)
 			break;
 
 		case T_Hash:
-			finalize_primnode((Node *) ((Hash *) plan)->hashkey,
+			finalize_primnode(((Hash *) plan)->hashkey,
 							  &results);
 			break;
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f1963d22966..d3a813fb86c 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.68 2000/05/30 00:49:49 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.69 2000/07/12 02:37:11 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -30,6 +30,7 @@
 #include "optimizer/var.h"
 #include "parser/parse_type.h"
 #include "parser/parsetree.h"
+#include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -1317,7 +1318,10 @@ simplify_op_or_func(Expr *expr, List *args)
 	HeapTuple	func_tuple;
 	Form_pg_proc funcform;
 	Type		resultType;
+	bool		resultTypByVal;
+	int			resultTypLen;
 	Expr	   *newexpr;
+	ExprContext *econtext;
 	Datum		const_val;
 	bool		has_nonconst_input = false;
 	bool		has_null_input = false;
@@ -1424,25 +1428,35 @@ simplify_op_or_func(Expr *expr, List *args)
 	newexpr->oper = expr->oper;
 	newexpr->args = args;
 
+	/* Get info needed about result datatype */
+	resultType = typeidType(result_typeid);
+	resultTypByVal = typeByVal(resultType);
+	resultTypLen = typeLen(resultType);
+
 	/*
-	 * It is OK to pass econtext = NULL because none of the ExecEvalExpr()
+	 * It is OK to pass a dummy econtext because none of the ExecEvalExpr()
 	 * code used in this situation will use econtext.  That might seem
 	 * fortuitous, but it's not so unreasonable --- a constant expression
 	 * does not depend on context, by definition, n'est ce pas?
 	 */
-	const_val = ExecEvalExpr((Node *) newexpr, NULL,
-							 &const_is_null, &isDone);
+	econtext = MakeExprContext(NULL, CurrentMemoryContext);
+
+	const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
+										  &const_is_null, &isDone);
 	Assert(isDone);				/* if this isn't set, we blew it... */
+
+	/* Must copy result out of sub-context used by expression eval */
+	const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
+
+	FreeExprContext(econtext);
 	pfree(newexpr);
 
 	/*
 	 * Make the constant result node.
 	 */
-	resultType = typeidType(result_typeid);
-	return (Expr *) makeConst(result_typeid, typeLen(resultType),
+	return (Expr *) makeConst(result_typeid, resultTypLen,
 							  const_val, const_is_null,
-							  typeByVal(resultType),
-							  false, false);
+							  resultTypByVal, false, false);
 }
 
 
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 9e52930e20f..57818afb9ed 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.35 2000/06/28 03:32:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.36 2000/07/12 02:37:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,7 +72,6 @@ CreateExecutorState(void)
 	state->es_param_list_info = NULL;
 	state->es_param_exec_vals = NULL;
 
-	state->es_BaseId = 0;
 	state->es_tupleTable = NULL;
 
 	state->es_junkFilter = NULL;
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index a86ec87067b..7f590e06e46 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -1,13 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * datum.c
+ *	  POSTGRES Datum (abstract data type) manipulation routines.
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.17 2000/01/26 05:57:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.18 2000/07/12 02:37:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +17,7 @@
  *
  * A) if a type is "byVal" then all the information is stored in the
  * Datum itself (i.e. no pointers involved!). In this case the
- * length of the type is always greater than zero and less than
+ * length of the type is always greater than zero and not more than
  * "sizeof(Datum)"
  * B) if a type is not "byVal" and it has a fixed length, then
  * the "Datum" always contain a pointer to a stream of bytes.
@@ -27,15 +28,19 @@
  * This varlena structure has information about the actual length of this
  * particular instance of the type and about its value.
  *
+ * Note that we do not treat "toasted" datums specially; therefore what
+ * will be copied or compared is the compressed data or toast reference.
  */
+
 #include "postgres.h"
+
 #include "utils/datum.h"
 
 /*-------------------------------------------------------------------------
  * datumGetSize
  *
  * Find the "real" size of a datum, given the datum value,
- * its type, whether it is a "by value", and its length.
+ * whether it is a "by value", and its length.
  *
  * To cut a long story short, usually the real size is equal to the
  * type length, with the exception of variable length types which have
@@ -45,47 +50,31 @@
  *-------------------------------------------------------------------------
  */
 Size
-datumGetSize(Datum value, Oid type, bool byVal, Size len)
+datumGetSize(Datum value, bool typByVal, int typLen)
 {
+	Size		size;
 
-	struct varlena *s;
-	Size		size = 0;
-
-	if (byVal)
+	if (typByVal)
 	{
-		if (len <= sizeof(Datum))
-			size = len;
-		else
-		{
-			elog(ERROR,
-				 "datumGetSize: Error: type=%ld, byVaL with len=%d",
-				 (long) type, len);
-		}
+		/* Pass-by-value types are always fixed-length */
+		Assert(typLen > 0 && typLen <= sizeof(Datum));
+		size = (Size) typLen;
 	}
 	else
-	{							/* not byValue */
-		if (len == -1)
+	{
+		if (typLen == -1)
 		{
+			/* Assume it is a varlena datatype */
+			struct varlena *s = (struct varlena *) DatumGetPointer(value);
 
-			/*
-			 * variable length type Look at the varlena struct for its
-			 * real length...
-			 */
-			s = (struct varlena *) DatumGetPointer(value);
 			if (!PointerIsValid(s))
-			{
-				elog(ERROR,
-					 "datumGetSize: Invalid Datum Pointer");
-			}
+				elog(ERROR, "datumGetSize: Invalid Datum Pointer");
 			size = (Size) VARSIZE(s);
 		}
 		else
 		{
-
-			/*
-			 * fixed length type
-			 */
-			size = len;
+			/* Fixed-length pass-by-ref type */
+			size = (Size) typLen;
 		}
 	}
 
@@ -97,37 +86,29 @@ datumGetSize(Datum value, Oid type, bool byVal, Size len)
  *
  * make a copy of a datum
  *
- * If the type of the datum is not passed by value (i.e. "byVal=false")
- * then we assume that the datum contains a pointer and we copy all the
- * bytes pointed by this pointer
+ * If the datatype is pass-by-reference, memory is obtained with palloc().
  *-------------------------------------------------------------------------
  */
 Datum
-datumCopy(Datum value, Oid type, bool byVal, Size len)
+datumCopy(Datum value, bool typByVal, int typLen)
 {
-
-	Size		realSize;
 	Datum		res;
-	char	   *s;
-
 
-	if (byVal)
+	if (typByVal)
 		res = value;
 	else
 	{
-		if (value == 0)
-			return (Datum) NULL;
-		realSize = datumGetSize(value, type, byVal, len);
+		Size		realSize;
+		char	   *s;
+
+		if (DatumGetPointer(value) == NULL)
+			return PointerGetDatum(NULL);
+
+		realSize = datumGetSize(value, typByVal, typLen);
 
-		/*
-		 * the value is a pointer. Allocate enough space and copy the
-		 * pointed data.
-		 */
 		s = (char *) palloc(realSize);
-		if (s == NULL)
-			elog(ERROR, "datumCopy: out of memory\n");
-		memmove(s, DatumGetPointer(value), realSize);
-		res = (Datum) s;
+		memcpy(s, DatumGetPointer(value), realSize);
+		res = PointerGetDatum(s);
 	}
 	return res;
 }
@@ -143,21 +124,12 @@ datumCopy(Datum value, Oid type, bool byVal, Size len)
  */
 #ifdef NOT_USED
 void
-datumFree(Datum value, Oid type, bool byVal, Size len)
+datumFree(Datum value, bool typByVal, int typLen)
 {
-
-	Size		realSize;
-	Pointer		s;
-
-	realSize = datumGetSize(value, type, byVal, len);
-
-	if (!byVal)
+	if (!typByVal)
 	{
+		Pointer		s = DatumGetPointer(value);
 
-		/*
-		 * free the space palloced by "datumCopy()"
-		 */
-		s = DatumGetPointer(value);
 		pfree(s);
 	}
 }
@@ -174,46 +146,41 @@ datumFree(Datum value, Oid type, bool byVal, Size len)
  * This routine will return false if there are 2 different
  * representations of the same value (something along the lines
  * of say the representation of zero in one's complement arithmetic).
- *
+ * Also, it will probably not give the answer you want if either
+ * datum has been "toasted".
  *-------------------------------------------------------------------------
  */
 bool
-datumIsEqual(Datum value1, Datum value2, Oid type, bool byVal, Size len)
+datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
 {
-	Size		size1,
-				size2;
-	char	   *s1,
-			   *s2;
+	bool	res;
 
-	if (byVal)
+	if (typByVal)
 	{
-
 		/*
 		 * just compare the two datums. NOTE: just comparing "len" bytes
 		 * will not do the work, because we do not know how these bytes
 		 * are aligned inside the "Datum".
 		 */
-		if (value1 == value2)
-			return true;
-		else
-			return false;
+		res = (value1 == value2);
 	}
 	else
 	{
+		Size		size1,
+					size2;
+		char	   *s1,
+				   *s2;
 
 		/*
-		 * byVal = false Compare the bytes pointed by the pointers stored
-		 * in the datums.
+		 * Compare the bytes pointed by the pointers stored in the datums.
 		 */
-		size1 = datumGetSize(value1, type, byVal, len);
-		size2 = datumGetSize(value2, type, byVal, len);
+		size1 = datumGetSize(value1, typByVal, typLen);
+		size2 = datumGetSize(value2, typByVal, typLen);
 		if (size1 != size2)
 			return false;
 		s1 = (char *) DatumGetPointer(value1);
 		s2 = (char *) DatumGetPointer(value2);
-		if (!memcmp(s1, s2, size1))
-			return true;
-		else
-			return false;
+		res = (memcmp(s1, s2, size1) == 0);
 	}
+	return res;
 }
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8247e16812d..b5380a8c52a 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.63 2000/07/06 05:48:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.64 2000/07/12 02:37:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -436,25 +436,38 @@ textpos(PG_FUNCTION_ARGS)
 /*
  *		texteq			- returns true iff arguments are equal
  *		textne			- returns true iff arguments are not equal
+ *
+ * Note: btree indexes need these routines not to leak memory; therefore,
+ * be careful to free working copies of toasted datums.  Most places don't
+ * need to be so careful.
  */
 Datum
 texteq(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
-	int			len;
-	char	   *a1p,
-			   *a2p;
+	bool		result;
 
 	if (VARSIZE(arg1) != VARSIZE(arg2))
-		PG_RETURN_BOOL(false);
+		result = false;
+	else
+	{
+		int			len;
+		char	   *a1p,
+				   *a2p;
 
-	len = VARSIZE(arg1) - VARHDRSZ;
+		len = VARSIZE(arg1) - VARHDRSZ;
 
-	a1p = VARDATA(arg1);
-	a2p = VARDATA(arg2);
+		a1p = VARDATA(arg1);
+		a2p = VARDATA(arg2);
 
-	PG_RETURN_BOOL(memcmp(a1p, a2p, len) == 0);
+		result = (memcmp(a1p, a2p, len) == 0);
+	}
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
 }
 
 Datum
@@ -462,19 +475,28 @@ textne(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
-	int			len;
-	char	   *a1p,
-			   *a2p;
+	bool		result;
 
 	if (VARSIZE(arg1) != VARSIZE(arg2))
-		PG_RETURN_BOOL(true);
+		result = true;
+	else
+	{
+		int			len;
+		char	   *a1p,
+				   *a2p;
 
-	len = VARSIZE(arg1) - VARHDRSZ;
+		len = VARSIZE(arg1) - VARHDRSZ;
 
-	a1p = VARDATA(arg1);
-	a2p = VARDATA(arg2);
+		a1p = VARDATA(arg1);
+		a2p = VARDATA(arg2);
+
+		result = (memcmp(a1p, a2p, len) != 0);
+	}
 
-	PG_RETURN_BOOL(memcmp(a1p, a2p, len) != 0);
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
 }
 
 /* varstr_cmp()
@@ -545,6 +567,10 @@ text_cmp(text *arg1, text *arg2)
 
 /*
  * Comparison functions for text strings.
+ *
+ * Note: btree indexes need these routines not to leak memory; therefore,
+ * be careful to free working copies of toasted datums.  Most places don't
+ * need to be so careful.
  */
 
 Datum
@@ -552,8 +578,14 @@ text_lt(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
 
-	PG_RETURN_BOOL(text_cmp(arg1, arg2) < 0);
+	result = (text_cmp(arg1, arg2) < 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
 }
 
 Datum
@@ -561,8 +593,14 @@ text_le(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
+
+	result = (text_cmp(arg1, arg2) <= 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
 
-	PG_RETURN_BOOL(text_cmp(arg1, arg2) <= 0);
+	PG_RETURN_BOOL(result);
 }
 
 Datum
@@ -570,8 +608,14 @@ text_gt(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
 
-	PG_RETURN_BOOL(text_cmp(arg1, arg2) > 0);
+	result = (text_cmp(arg1, arg2) > 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
+
+	PG_RETURN_BOOL(result);
 }
 
 Datum
@@ -579,8 +623,14 @@ text_ge(PG_FUNCTION_ARGS)
 {
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	bool		result;
+
+	result = (text_cmp(arg1, arg2) >= 0);
+
+	PG_FREE_IF_COPY(arg1, 0);
+	PG_FREE_IF_COPY(arg2, 1);
 
-	PG_RETURN_BOOL(text_cmp(arg1, arg2) >= 0);
+	PG_RETURN_BOOL(result);
 }
 
 Datum
@@ -589,14 +639,8 @@ text_larger(PG_FUNCTION_ARGS)
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
 	text	   *result;
-	text	   *temp;
 
-	temp = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
-
-	/* Make a copy --- temporary hack until nodeAgg.c is smarter */
-
-	result = (text *) palloc(VARSIZE(temp));
-	memcpy((char *) result, (char *) temp, VARSIZE(temp));
+	result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
 
 	PG_RETURN_TEXT_P(result);
 }
@@ -607,14 +651,8 @@ text_smaller(PG_FUNCTION_ARGS)
 	text	   *arg1 = PG_GETARG_TEXT_P(0);
 	text	   *arg2 = PG_GETARG_TEXT_P(1);
 	text	   *result;
-	text	   *temp;
-
-	temp = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
-
-	/* Make a copy --- temporary hack until nodeAgg.c is smarter */
 
-	result = (text *) palloc(VARSIZE(temp));
-	memcpy((char *) result, (char *) temp, VARSIZE(temp));
+	result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
 
 	PG_RETURN_TEXT_P(result);
 }
diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c
index 7e9d18c7e27..ba34dfd03dc 100644
--- a/src/backend/utils/cache/fcache.c
+++ b/src/backend/utils/cache/fcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.33 2000/07/05 23:11:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.34 2000/07/12 02:37:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -75,6 +75,7 @@ init_fcache(Oid foid,
 
 	retval = (FunctionCachePtr) palloc(sizeof(FunctionCache));
 	MemSet(retval, 0, sizeof(FunctionCache));
+	retval->fcacheCxt = CurrentMemoryContext;
 
 	/* ----------------
 	 *	 get the procedure tuple corresponding to the given functionOid
@@ -256,22 +257,26 @@ init_fcache(Oid foid,
 void
 setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext)
 {
-	Func	   *fnode;
-	Oper	   *onode;
+	MemoryContext oldcontext;
 	FunctionCachePtr fcache;
 
+	/* Switch to a context long-lived enough for the fcache entry */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
 	fcache = init_fcache(foid, argList, econtext);
 
 	if (IsA(node, Oper))
 	{
-		onode = (Oper *) node;
+		Oper	   *onode = (Oper *) node;
 		onode->op_fcache = fcache;
 	}
 	else if (IsA(node, Func))
 	{
-		fnode = (Func *) node;
+		Func	   *fnode = (Func *) node;
 		fnode->func_fcache = fcache;
 	}
 	else
 		elog(ERROR, "init_fcache: node must be Oper or Func!");
+
+	MemoryContextSwitchTo(oldcontext);
 }
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index bf212ff7bb5..6c6f58b0222 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.29 2000/07/11 14:30:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.30 2000/07/12 02:37:23 tgl Exp $
  *
  * NOTE:
  *	This is a new (Feb. 05, 1999) implementation of the allocation set
@@ -349,20 +349,21 @@ AllocSetReset(MemoryContext context)
 		if (block == set->keeper)
 		{
 			/* Reset the block, but don't return it to malloc */
-			block->next = NULL;
-			block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
+			char   *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
+
 #ifdef CLOBBER_FREED_MEMORY
 			/* Wipe freed memory for debugging purposes */
-			memset(block->freeptr, 0x7F,
-				   ((char *) block->endptr) - ((char *) block->freeptr));
+			memset(datastart, 0x7F, ((char *) block->freeptr) - datastart);
 #endif
+			block->freeptr = datastart;
+			block->next = NULL;
 		}
 		else
 		{
 			/* Normal case, release the block */
 #ifdef CLOBBER_FREED_MEMORY
 			/* Wipe freed memory for debugging purposes */
-			memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
+			memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block));
 #endif
 			free(block);
 		}
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index dd495517d30..66b4f601be3 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: index.h,v 1.27 2000/07/04 06:11:54 tgl Exp $
+ * $Id: index.h,v 1.28 2000/07/12 02:37:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,9 +53,6 @@ extern void setRelhasindexInplace(Oid relid, bool hasindex, bool immediate);
 extern bool SetReindexProcessing(bool processing);
 extern bool IsReindexProcessing(void);
 
-extern void FillDummyExprContext(ExprContext *econtext, TupleTableSlot *slot,
-					 TupleDesc tupdesc, Buffer buffer);
-
 extern void index_build(Relation heapRelation, Relation indexRelation,
 						int numberOfAttributes, AttrNumber *attributeNumber,
 						FuncIndexInfo *funcInfo, PredInfo *predInfo,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 703c907e127..b835b290440 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.45 2000/06/18 22:44:28 tgl Exp $
+ * $Id: executor.h,v 1.46 2000/07/12 02:37:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,19 +72,16 @@ extern void ExecEndNode(Plan *node, Plan *parent);
 /*
  * prototypes from functions in execQual.c
  */
-extern bool execConstByVal;
-extern int	execConstLen;
-
-extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
-				  bool *isNull);
 extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
 			  bool *isNull);
-
 extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
 				  bool *isNull);
-extern char *GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull);
-extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
-			 bool *isDone);
+extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
+								bool *isNull);
+extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext,
+						  bool *isNull, bool *isDone);
+extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext,
+									   bool *isNull, bool *isDone);
 extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int	ExecTargetListLength(List *targetlist);
 extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
@@ -92,7 +89,9 @@ extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
 /*
  * prototypes from functions in execScan.c
  */
-extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot *(*accessMtd) ());
+typedef TupleTableSlot *(*ExecScanAccessMtd) (Scan *node);
+
+extern TupleTableSlot *ExecScan(Scan *node, ExecScanAccessMtd accessMtd);
 
 /*
  * prototypes from functions in execTuples.c
@@ -121,8 +120,6 @@ extern void SetChangedParamList(Plan *node, List *newchg);
  * prototypes from functions in execUtils.c
  */
 extern void ResetTupleCount(void);
-extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
-					   Plan *parent);
 extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
 extern void ExecAssignResultType(CommonState *commonstate,
 					 TupleDesc tupDesc);
@@ -133,7 +130,6 @@ extern TupleDesc ExecGetResultType(CommonState *commonstate);
 extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
 extern void ExecFreeProjectionInfo(CommonState *commonstate);
 extern void ExecFreeExprContext(CommonState *commonstate);
-extern void ExecFreeTypeInfo(CommonState *commonstate);
 extern TupleDesc ExecGetScanType(CommonScanState *csstate);
 extern void ExecAssignScanType(CommonScanState *csstate,
 				   TupleDesc tupDesc);
@@ -141,6 +137,13 @@ extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
 								CommonScanState *csstate);
 extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc);
 
+extern ExprContext *MakeExprContext(TupleTableSlot *slot,
+									MemoryContext queryContext);
+extern void FreeExprContext(ExprContext *econtext);
+
+#define ResetExprContext(econtext) \
+	MemoryContextReset((econtext)->ecxt_per_tuple_memory)
+
 extern void ExecOpenIndices(RelationInfo *resultRelationInfo);
 extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
 extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
diff --git a/src/include/executor/hashjoin.h b/src/include/executor/hashjoin.h
index 285bb314d3d..8d4cb98469f 100644
--- a/src/include/executor/hashjoin.h
+++ b/src/include/executor/hashjoin.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: hashjoin.h,v 1.17 2000/06/28 03:33:05 tgl Exp $
+ * $Id: hashjoin.h,v 1.18 2000/07/12 02:37:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,8 +21,8 @@
  *				hash-join hash table structures
  *
  * Each active hashjoin has a HashJoinTable control block which is
- * palloc'd in the executor's context.	All other storage needed for
- * the hashjoin is kept in private memory contexts, two for each hashjoin.
+ * palloc'd in the executor's per-query context.  All other storage needed
+ * for the hashjoin is kept in private memory contexts, two for each hashjoin.
  * This makes it easy and fast to release the storage when we don't need it
  * anymore.
  *
@@ -68,6 +68,14 @@ typedef struct HashTableData
 	long	   *innerBatchSize; /* count of tuples in each inner batch
 								 * file */
 
+	/*
+	 * Info about the datatype being hashed.  We assume that the inner
+	 * and outer sides of the hash are the same type, or at least
+	 * binary-compatible types.
+	 */
+	bool		typByVal;
+	int			typLen;
+
 	/*
 	 * During 1st scan of inner relation, we get tuples from executor. If
 	 * nbatch > 0 then tuples that don't belong in first nbuckets logical
diff --git a/src/include/executor/nodeGroup.h b/src/include/executor/nodeGroup.h
index 303516b167e..71bd3b52ff5 100644
--- a/src/include/executor/nodeGroup.h
+++ b/src/include/executor/nodeGroup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeGroup.h,v 1.16 2000/04/12 17:16:33 momjian Exp $
+ * $Id: nodeGroup.h,v 1.17 2000/07/12 02:37:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,8 @@ extern bool execTuplesMatch(HeapTuple tuple1,
 				TupleDesc tupdesc,
 				int numCols,
 				AttrNumber *matchColIdx,
-				FmgrInfo *eqfunctions);
+				FmgrInfo *eqfunctions,
+				MemoryContext evalContext);
 extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
 					   int numCols,
 					   AttrNumber *matchColIdx);
diff --git a/src/include/executor/nodeHash.h b/src/include/executor/nodeHash.h
index b61ced7cdc1..5fabe7d253a 100644
--- a/src/include/executor/nodeHash.h
+++ b/src/include/executor/nodeHash.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeHash.h,v 1.16 2000/04/18 05:43:00 tgl Exp $
+ * $Id: nodeHash.h,v 1.17 2000/07/12 02:37:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,10 +25,12 @@ extern int	ExecCountSlotsHash(Hash *node);
 extern void ExecEndHash(Hash *node);
 extern HashJoinTable ExecHashTableCreate(Hash *node);
 extern void ExecHashTableDestroy(HashJoinTable hashtable);
-extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
-					Var *hashkey);
-extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
-				  Var *hashkey);
+extern void ExecHashTableInsert(HashJoinTable hashtable,
+								ExprContext *econtext,
+								Node *hashkey);
+extern int ExecHashGetBucket(HashJoinTable hashtable,
+							 ExprContext *econtext,
+							 Node *hashkey);
 extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
 				   ExprContext *econtext);
 extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index a56ae216f5e..f7c46c3ca92 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -13,7 +13,7 @@ extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext,
 			bool *isNull);
 extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
 extern void ExecReScanSetParamPlan(SubPlan *node, Plan *parent);
-extern void ExecSetParamPlan(SubPlan *node);
+extern void ExecSetParamPlan(SubPlan *node, ExprContext *econtext);
 extern void ExecEndSubPlan(SubPlan *node);
 
 #endif	 /* NODESUBPLAN_H */
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index b0aea5df09e..03d13e3d4ed 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fmgr.h,v 1.7 2000/07/06 05:48:17 tgl Exp $
+ * $Id: fmgr.h,v 1.8 2000/07/12 02:37:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -119,6 +119,21 @@ extern struct varlena * pg_detoast_datum_copy(struct varlena * datum);
 #define PG_DETOAST_DATUM_COPY(datum) \
 	pg_detoast_datum_copy((struct varlena *) DatumGetPointer(datum))
 
+/*
+ * Support for cleaning up detoasted copies of inputs.  This must only
+ * be used for pass-by-ref datatypes, and normally would only be used
+ * for toastable types.  If the given pointer is different from the
+ * original argument, assume it's a palloc'd detoasted copy, and pfree it.
+ * NOTE: most functions on toastable types do not have to worry about this,
+ * but we currently require that support functions for indexes not leak
+ * memory.
+ */
+#define PG_FREE_IF_COPY(ptr,n) \
+	do { \
+		if ((Pointer) (ptr) != PG_GETARG_POINTER(n)) \
+			pfree(ptr); \
+	} while (0)
+
 /* Macros for fetching arguments of standard types */
 
 #define PG_GETARG_DATUM(n)   (fcinfo->arg[n])
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a74f16348d2..2c21dba9c23 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.42 2000/06/18 22:44:29 tgl Exp $
+ * $Id: execnodes.h,v 1.43 2000/07/12 02:37:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,29 +76,46 @@ typedef struct RelationInfo
  *		to an attribute in the current inner tuple then we need to know
  *		what the current inner tuple is and so we look at the expression
  *		context.
+ *
+ *	There are two memory contexts associated with an ExprContext:
+ *	* ecxt_per_query_memory is a relatively long-lived context (such as
+ *	  TransactionCommandContext); typically it's the same context the
+ *	  ExprContext node itself is allocated in.  This context can be
+ *	  used for purposes such as storing operator/function fcache nodes.
+ *	* ecxt_per_tuple_memory is a short-term context for expression results.
+ *	  As the name suggests, it will typically be reset once per tuple,
+ *	  before we begin to evaluate expressions for that tuple.  Each
+ *	  ExprContext normally has its very own per-tuple memory context.
+ *	CurrentMemoryContext should be set to ecxt_per_tuple_memory before
+ *	calling ExecEvalExpr() --- see ExecEvalExprSwitchContext().
  * ----------------
  */
 typedef struct ExprContext
 {
 	NodeTag		type;
+	/* Tuples that Var nodes in expression may refer to */
 	TupleTableSlot *ecxt_scantuple;
 	TupleTableSlot *ecxt_innertuple;
 	TupleTableSlot *ecxt_outertuple;
-	Relation	ecxt_relation;
-	Index		ecxt_relid;
-	ParamListInfo ecxt_param_list_info;
-	ParamExecData *ecxt_param_exec_vals;		/* this is for subselects */
-	List	   *ecxt_range_table;
+	/* Memory contexts for expression evaluation --- see notes above */
+	MemoryContext ecxt_per_query_memory;
+	MemoryContext ecxt_per_tuple_memory;
+	/* Values to substitute for Param nodes in expression */
+	ParamExecData *ecxt_param_exec_vals;	/* for PARAM_EXEC params */
+	ParamListInfo ecxt_param_list_info;		/* for other param types */
+	/* Values to substitute for Aggref nodes in expression */
 	Datum	   *ecxt_aggvalues; /* precomputed values for Aggref nodes */
 	bool	   *ecxt_aggnulls;	/* null flags for Aggref nodes */
+	/* Range table that Vars in expression refer to --- seldom needed */
+	List	   *ecxt_range_table;
 } ExprContext;
 
 /* ----------------
  *		ProjectionInfo node information
  *
- *		This is all the information needed to preform projections
+ *		This is all the information needed to perform projections
  *		on a tuple.  Nodes which need to do projections create one
- *		of these.  In theory, when a node wants to preform a projection
+ *		of these.  In theory, when a node wants to perform a projection
  *		it should just update this information as necessary and then
  *		call ExecProject().  -cim 6/3/91
  *
@@ -122,18 +139,16 @@ typedef struct ProjectionInfo
 /* ----------------
  *	  JunkFilter
  *
- *	  this class is used to store information regarding junk attributes.
+ *	  This class is used to store information regarding junk attributes.
  *	  A junk attribute is an attribute in a tuple that is needed only for
  *	  storing intermediate information in the executor, and does not belong
- *	  in the tuple proper.	For example, when we do a delete or replace
- *	  query, the planner adds an entry to the targetlist so that the tuples
- *	  returned to ExecutePlan() contain an extra attribute: the t_ctid of
- *	  the tuple to be deleted/replaced.  This is needed for amdelete() and
- *	  amreplace().	In doing a delete this does not make much of a
- *	  difference, but in doing a replace we have to make sure we disgard
- *	  all the junk in a tuple before calling amreplace().  Otherwise the
- *	  inserted tuple will not have the correct schema.	This solves a
- *	  problem with hash-join and merge-sort replace plans.	-cim 10/10/90
+ *	  in emitted tuples.	For example, when we do an UPDATE query,
+ *	  the planner adds a "junk" entry to the targetlist so that the tuples
+ *	  returned to ExecutePlan() contain an extra attribute: the ctid of
+ *	  the tuple to be updated.  This is needed to do the update, but we
+ *	  don't want the ctid to be part of the stored new tuple!  So, we
+ *	  apply a "junk filter" to remove the junk attributes and form the
+ *	  real output tuple.
  *
  *	  targetList:		the original target list (including junk attributes).
  *	  length:			the length of 'targetList'.
@@ -174,10 +189,6 @@ typedef struct JunkFilter
  *		param_list_info					information needed to transform
  *										Param nodes into Const nodes
  *
- *		BaseId							during InitPlan(), each node is
- *										given a number.  this is the next
- *										number to be assigned.
- *
  *		tupleTable						this is a pointer to an array
  *										of pointers to tuples used by
  *										the executor at any given moment.
@@ -185,11 +196,6 @@ typedef struct JunkFilter
  *		junkFilter						contains information used to
  *										extract junk attributes from a tuple.
  *										(see JunkFilter above)
- *
- *		refcount						local buffer refcounts used in
- *										an ExecMain cycle.	this is introduced
- *										to avoid ExecStart's unpinning each
- *										other's buffers when called recursively
  * ----------------
  */
 typedef struct EState
@@ -203,7 +209,6 @@ typedef struct EState
 	Relation	es_into_relation_descriptor;
 	ParamListInfo es_param_list_info;
 	ParamExecData *es_param_exec_vals;	/* this is for subselects */
-	int			es_BaseId;
 	TupleTable	es_tupleTable;
 	JunkFilter *es_junkFilter;
 	uint32		es_processed;	/* # of tuples processed */
@@ -249,27 +254,21 @@ typedef struct EState
  * ----------------------------------------------------------------
  */
 
-/* BaseNode removed -- base_id moved into CommonState		- jolly */
-
 /* ----------------
  *	 CommonState information
  *
- *|		this is a bogus class used to hold slots so other
- *|		nodes can inherit them...
+ *		Superclass for all executor node-state object types.
  *
  *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
  *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
+ *		ExprContext		   node's expression-evaluation context
  *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
- *
+ *		TupFromTlist	   state flag used by some node types (why kept here?)
  * ----------------
  */
 typedef struct CommonState
 {
 	NodeTag		type;			/* its first field is NodeTag */
-	int			cs_base_id;
 	TupleTableSlot *cs_OuterTupleSlot;
 	TupleTableSlot *cs_ResultTupleSlot;
 	ExprContext *cs_ExprContext;
@@ -288,15 +287,6 @@ typedef struct CommonState
  *
  *		done			   flag which tells us to quit when we
  *						   have already returned a constant tuple.
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct ResultState
@@ -319,15 +309,6 @@ typedef struct ResultState
  *		rtentries		range table for the current plan
  *		result_relation_info_list  array of each subplan's result relation info
  *		junkFilter_list  array of each subplan's junk filter
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct AppendState
@@ -349,22 +330,15 @@ typedef struct AppendState
 /* ----------------
  *	 CommonScanState information
  *
- *		CommonScanState is a class like CommonState, but is used more
- *		by the nodes like SeqScan and Sort which want to
- *		keep track of an underlying relation.
+ *		CommonScanState extends CommonState for node types that represent
+ *		scans of an underlying relation.  It can also be used for nodes
+ *		that scan the output of an underlying plan node --- in that case,
+ *		only ScanTupleSlot is actually useful, and it refers to the tuple
+ *		retrieved from the subplan.
  *
- *		currentRelation    relation being scanned
- *		currentScanDesc    current scan descriptor for scan
+ *		currentRelation    relation being scanned (NULL if none)
+ *		currentScanDesc    current scan descriptor for scan (NULL if none)
  *		ScanTupleSlot	   pointer to slot in tuple table holding scan tuple
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct CommonScanState
@@ -375,41 +349,39 @@ typedef struct CommonScanState
 	TupleTableSlot *css_ScanTupleSlot;
 } CommonScanState;
 
+/*
+ * SeqScan uses a bare CommonScanState as its state item, since it needs
+ * no additional fields.
+ */
+
 /* ----------------
  *	 IndexScanState information
  *
- *|		index scans don't use CommonScanState because
- *|		the underlying AM abstractions for heap scans and
- *|		index scans are too different..  It would be nice
- *|		if the current abstraction was more useful but ... -cim 10/15/89
+ *		Note that an IndexScan node *also* has a CommonScanState state item.
+ *		IndexScanState stores the info needed specifically for indexing.
+ *		There's probably no good reason why this is a separate node type
+ *		rather than an extension of CommonScanState.
  *
- *		IndexPtr		   current index in use
  *		NumIndices		   number of indices in this scan
+ *		IndexPtr		   current index in use
  *		ScanKeys		   Skey structures to scan index rels
  *		NumScanKeys		   array of no of keys in each Skey struct
  *		RuntimeKeyInfo	   array of array of flags for Skeys evaled at runtime
+ *		RuntimeContext	   expr context for evaling runtime Skeys
  *		RelationDescs	   ptr to array of relation descriptors
  *		ScanDescs		   ptr to array of scan descriptors
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct IndexScanState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	NodeTag		type;
 	int			iss_NumIndices;
 	int			iss_IndexPtr;
 	int			iss_MarkIndexPtr;
 	ScanKey    *iss_ScanKeys;
 	int		   *iss_NumScanKeys;
-	Pointer		iss_RuntimeKeyInfo;
+	int		  **iss_RuntimeKeyInfo;
+	ExprContext *iss_RuntimeContext;
 	RelationPtr iss_RelationDescs;
 	IndexScanDescPtr iss_ScanDescs;
 	HeapTupleData iss_htup;
@@ -418,28 +390,18 @@ typedef struct IndexScanState
 /* ----------------
  *	 TidScanState information
  *
- *|		tid scans don't use CommonScanState because
- *|		the underlying AM abstractions for heap scans and
- *|		tid scans are too different..  It would be nice
- *|		if the current abstraction was more useful but ... -cim 10/15/89
+ *		Note that a TidScan node *also* has a CommonScanState state item.
+ *		There's probably no good reason why this is a separate node type
+ *		rather than an extension of CommonScanState.
  *
- *		TidPtr		   current tid in use
  *		NumTids		   number of tids in this scan
- *		tidList		   evaluated item pointers
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
+ *		TidPtr		   current tid in use
+ *		TidList		   evaluated item pointers
  * ----------------
  */
 typedef struct TidScanState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	NodeTag		type;
 	int			tss_NumTids;
 	int			tss_TidPtr;
 	int			tss_MarkTidPtr;
@@ -455,39 +417,19 @@ typedef struct TidScanState
 /* ----------------
  *	 JoinState information
  *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
+ *		Superclass for state items of join nodes.
+ *		Currently this is the same as CommonState.
  * ----------------
  */
 typedef CommonState JoinState;
 
 /* ----------------
  *	 NestLoopState information
- *
- *		PortalFlag		   Set to enable portals to work.
- *
- *	 JoinState information
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct NestLoopState
 {
 	JoinState	jstate;			/* its first field is NodeTag */
-	bool		nl_PortalFlag;
 } NestLoopState;
 
 /* ----------------
@@ -497,17 +439,6 @@ typedef struct NestLoopState
  *		InnerSkipQual	   outerKey1 > innerKey1 ...
  *		JoinState		   current "state" of join. see executor.h
  *		MarkedTupleSlot    pointer to slot in tuple table for marked tuple
- *
- *	 JoinState information
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct MergeJoinState
@@ -531,17 +462,6 @@ typedef struct MergeJoinState
  *		hj_InnerHashKey			the inner hash key in the hashjoin condition
  *		hj_OuterTupleSlot		tuple slot for outer tuples
  *		hj_HashTupleSlot		tuple slot for hashed tuples
- *
- *	 JoinState information
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct HashJoinState
@@ -550,7 +470,7 @@ typedef struct HashJoinState
 	HashJoinTable hj_HashTable;
 	int			hj_CurBucketNo;
 	HashJoinTuple hj_CurTuple;
-	Var		   *hj_InnerHashKey;
+	Node	   *hj_InnerHashKey;
 	TupleTableSlot *hj_OuterTupleSlot;
 	TupleTableSlot *hj_HashTupleSlot;
 } HashJoinState;
@@ -567,22 +487,9 @@ typedef struct HashJoinState
  *		materialize nodes are used to materialize the results
  *		of a subplan into a temporary file.
  *
- *		tuplestorestate		private state of tuplestore.c
- *
- *	 CommonScanState information
- *
- *		currentRelation    relation descriptor of sorted relation
- *		currentScanDesc    current scan descriptor for scan
- *		ScanTupleSlot	   pointer to slot in tuple table holding scan tuple
- *
- *	 CommonState information
+ *		csstate.css_ScanTupleSlot refers to output of underlying plan.
  *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
+ *		tuplestorestate		private state of tuplestore.c
  * ----------------
  */
 typedef struct MaterialState
@@ -594,12 +501,14 @@ typedef struct MaterialState
 /* ---------------------
  *	AggregateState information
  *
+ *	csstate.css_ScanTupleSlot refers to output of underlying plan.
+ *
  *	Note: the associated ExprContext contains ecxt_aggvalues and ecxt_aggnulls
  *	arrays, which hold the computed agg values for the current input group
  *	during evaluation of an Agg node's output tuple(s).
  * -------------------------
  */
-typedef struct AggStatePerAggData *AggStatePerAgg;		/* private in nodeAgg.c */
+typedef struct AggStatePerAggData *AggStatePerAgg;	/* private in nodeAgg.c */
 
 typedef struct AggState
 {
@@ -607,12 +516,14 @@ typedef struct AggState
 	List	   *aggs;			/* all Aggref nodes in targetlist & quals */
 	int			numaggs;		/* length of list (could be zero!) */
 	AggStatePerAgg peragg;		/* per-Aggref working state */
+	MemoryContext tup_cxt;		/* context for per-output-tuple expressions */
+	MemoryContext agg_cxt[2];	/* pair of expression eval memory contexts */
+	int			which_cxt;		/* 0 or 1, indicates current agg_cxt */
 	bool		agg_done;		/* indicates completion of Agg scan */
 } AggState;
 
 /* ---------------------
  *	GroupState information
- *
  * -------------------------
  */
 typedef struct GroupState
@@ -630,21 +541,6 @@ typedef struct GroupState
  *		sort_Done		indicates whether sort has been performed yet
  *		sort_Keys		scan key structures describing the sort keys
  *		tuplesortstate	private state of tuplesort.c
- *
- *	 CommonScanState information
- *
- *		currentRelation    relation descriptor of sorted relation
- *		currentScanDesc    current scan descriptor for scan
- *		ScanTupleSlot	   pointer to slot in tuple table holding scan tuple
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct SortState
@@ -664,15 +560,6 @@ typedef struct SortState
  *		with the previously fetched tuple stored in priorTuple.
  *		If the two are identical in all interesting fields, then
  *		we just fetch another tuple from the sort and try again.
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct UniqueState
@@ -680,6 +567,7 @@ typedef struct UniqueState
 	CommonState cstate;			/* its first field is NodeTag */
 	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
 	HeapTuple	priorTuple;		/* most recently returned tuple, or NULL */
+	MemoryContext tempContext;	/* short-term context for comparisons */
 } UniqueState;
 
 
@@ -687,15 +575,6 @@ typedef struct UniqueState
  *	 HashState information
  *
  *		hashtable			hash table for the hashjoin
- *
- *	 CommonState information
- *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's current expression context
- *		ProjInfo		   info this node uses to form tuple projections
- *		NumScanAttributes  size of ScanAttributes array
- *		ScanAttributes	   attribute numbers of interest in this tuple
  * ----------------
  */
 typedef struct HashState
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 37006e621f4..e348d25b2ba 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.40 2000/06/18 22:44:31 tgl Exp $
+ * $Id: plannodes.h,v 1.41 2000/07/12 02:37:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -318,7 +318,7 @@ typedef struct Unique
 typedef struct Hash
 {
 	Plan		plan;
-	Var		   *hashkey;
+	Node	   *hashkey;
 	HashState  *hashstate;
 } Hash;
 
@@ -370,7 +370,7 @@ typedef struct SubPlan
 	 * Remaining fields are working state for executor; not used in
 	 * planning
 	 */
-	bool		shutdown;		/* TRUE = need to shutdown plan */
+	bool		needShutdown;	/* TRUE = need to shutdown subplan */
 	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
 } SubPlan;
 
diff --git a/src/include/utils/datum.h b/src/include/utils/datum.h
index 9079919fa06..199cf3109e1 100644
--- a/src/include/utils/datum.h
+++ b/src/include/utils/datum.h
@@ -1,64 +1,49 @@
 /*-------------------------------------------------------------------------
  *
  * datum.h
- *	  POSTGRES abstract data type datum representation definitions.
+ *	  POSTGRES Datum (abstract data type) manipulation routines.
  *
+ * These routines are driven by the 'typbyval' and 'typlen' information,
+ * which must previously have been obtained by the caller for the datatype
+ * of the Datum.  (We do it this way because in most situations the caller
+ * can look up the info just once and use it for many per-datum operations.)
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: datum.h,v 1.10 2000/01/26 05:58:37 momjian Exp $
+ * $Id: datum.h,v 1.11 2000/07/12 02:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef DATUM_H
 #define DATUM_H
 
-
-/*--------------------------------------------------------
- * SOME NOT VERY PORTABLE ROUTINES ???
- *--------------------------------------------------------
- *
- * In the implementation of the next routines we assume the following:
- *
- * A) if a type is "byVal" then all the information is stored in the
- * Datum itself (i.e. no pointers involved!). In this case the
- * length of the type is always greater than zero and less than
- * "sizeof(Datum)"
- * B) if a type is not "byVal" and it has a fixed length, then
- * the "Datum" always contain a pointer to a stream of bytes.
- * The number of significant bytes are always equal to the length of thr
- * type.
- * C) if a type is not "byVal" and is of variable length (i.e. it has
- * length == -1) then "Datum" always points to a "struct varlena".
- * This varlena structure has information about the actual length of this
- * particular instance of the type and about its value.
- */
-
-/*---------------
- * datumGetSize
- * find the "real" length of a datum
+/*
+ * datumGetSize - find the "real" length of a datum
  */
-extern Size datumGetSize(Datum value, Oid type, bool byVal, Size len);
+extern Size datumGetSize(Datum value, bool typByVal, int typLen);
 
-/*---------------
- * datumCopy
- * make a copy of a datum.
+/*
+ * datumCopy - make a copy of a datum.
+ *
+ * If the datatype is pass-by-reference, memory is obtained with palloc().
  */
-extern Datum datumCopy(Datum value, Oid type, bool byVal, Size len);
+extern Datum datumCopy(Datum value, bool typByVal, int typLen);
 
-/*---------------
- * datumFree
- * free space that *might* have been palloced by "datumCopy"
+/*
+ * datumFree - free a datum previously allocated by datumCopy, if any.
+ *
+ * Does nothing if datatype is pass-by-value.
  */
-extern void datumFree(Datum value, Oid type, bool byVal, Size len);
+extern void datumFree(Datum value, bool typByVal, int typLen);
 
-/*---------------
+/*
  * datumIsEqual
- * return true if thwo datums are equal, false otherwise.
+ * return true if two datums of the same type are equal, false otherwise.
+ *
  * XXX : See comments in the code for restrictions!
  */
-extern bool datumIsEqual(Datum value1, Datum value2, Oid type,
-			 bool byVal, Size len);
+extern bool datumIsEqual(Datum value1, Datum value2,
+						 bool typByVal, int typLen);
 
 #endif	 /* DATUM_H */
diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h
index db3a05baf4b..59f35867a72 100644
--- a/src/include/utils/fcache.h
+++ b/src/include/utils/fcache.h
@@ -1,13 +1,17 @@
 /*-------------------------------------------------------------------------
  *
  * fcache.h
+ *		Declarations for function cache records.
  *
- *
+ * The first time any Oper or Func node is evaluated, we compute a cache
+ * record for the function being invoked, and save a pointer to the cache
+ * record in the Oper or Func node.  This saves repeated lookup of info
+ * about the function.
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fcache.h,v 1.11 2000/05/28 17:56:20 tgl Exp $
+ * $Id: fcache.h,v 1.12 2000/07/12 02:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +35,11 @@ typedef struct
 								 * expr whose argument is func returning a
 								 * set ugh! */
 
+	/* If additional info is added to an existing fcache, be sure to
+	 * allocate it in the fcacheCxt.
+	 */
+	MemoryContext fcacheCxt;	/* context the fcache lives in */
+
 	int			nargs;			/* actual number of arguments */
 	Oid		   *argOidVect;		/* oids of all the argument types */
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5b8c6d773f4..8268299f2f3 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.24 2000/07/05 23:11:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.25 2000/07/12 02:37:39 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -47,17 +47,17 @@
 #include "plpgsql.h"
 #include "pl.tab.h"
 
+#include "access/heapam.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/trigger.h"
 #include "executor/spi.h"
 #include "executor/spi_priv.h"
-#include "commands/trigger.h"
-#include "utils/builtins.h"
 #include "fmgr.h"
-#include "access/heapam.h"
-
 #include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
 
 
 static PLpgSQL_function *error_info_func = NULL;
@@ -2205,7 +2205,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
 	 * Create a simple expression context to hold the arguments
 	 * ----------
 	 */
-	econtext = makeNode(ExprContext);
+	econtext = MakeExprContext(NULL, TransactionCommandContext);
 	paramLI = (ParamListInfo) palloc((expr->nparams + 1) *
 									 sizeof(ParamListInfoData));
 	econtext->ecxt_param_list_info = paramLI;
@@ -2286,12 +2286,20 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
 	 * ----------
 	 */
 	SPI_push();
-	retval = ExecEvalExpr(expr->plan_simple_expr,
-						  econtext,
-						  isNull,
-						  &isdone);
+	retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
+									   econtext,
+									   isNull,
+									   &isdone);
 	SPI_pop();
 
+	/*
+	 * Copy the result out of the expression-evaluation memory context,
+	 * so that we can free the expression context.
+	 */
+	retval = datumCopy(retval, get_typbyval(*rettype), get_typlen(*rettype));
+
+	FreeExprContext(econtext);
+
 	/* ----------
 	 * That's it.
 	 * ----------
-- 
GitLab