diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 42643a87529..22d5e44dc3f 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.96 2005/01/27 23:23:49 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.97 2005/03/14 04:41:12 tgl Exp $
  *
  * NOTES
  *	  The old interface functions have been converted to macros
@@ -23,6 +23,7 @@
 #include "access/heapam.h"
 #include "access/tuptoaster.h"
 #include "catalog/pg_type.h"
+#include "executor/tuptable.h"
 
 
 /* ----------------------------------------------------------------
@@ -751,6 +752,7 @@ heap_deformtuple(HeapTuple tuple,
 				 char *nulls)
 {
 	HeapTupleHeader tup = tuple->t_data;
+	bool		hasnulls = HeapTupleHasNulls(tuple);
 	Form_pg_attribute *att = tupleDesc->attrs;
 	int			tdesc_natts = tupleDesc->natts;
 	int			natts;			/* number of atts to extract */
@@ -775,7 +777,9 @@ heap_deformtuple(HeapTuple tuple,
 
 	for (attnum = 0; attnum < natts; attnum++)
 	{
-		if (HeapTupleHasNulls(tuple) && att_isnull(attnum, bp))
+		Form_pg_attribute thisatt = att[attnum];
+
+		if (hasnulls && att_isnull(attnum, bp))
 		{
 			values[attnum] = (Datum) 0;
 			nulls[attnum] = 'n';
@@ -785,21 +789,21 @@ heap_deformtuple(HeapTuple tuple,
 
 		nulls[attnum] = ' ';
 
-		if (!slow && att[attnum]->attcacheoff >= 0)
-			off = att[attnum]->attcacheoff;
+		if (!slow && thisatt->attcacheoff >= 0)
+			off = thisatt->attcacheoff;
 		else
 		{
-			off = att_align(off, att[attnum]->attalign);
+			off = att_align(off, thisatt->attalign);
 
 			if (!slow)
-				att[attnum]->attcacheoff = off;
+				thisatt->attcacheoff = off;
 		}
 
-		values[attnum] = fetchatt(att[attnum], tp + off);
+		values[attnum] = fetchatt(thisatt, tp + off);
 
-		off = att_addlength(off, att[attnum]->attlen, tp + off);
+		off = att_addlength(off, thisatt->attlen, tp + off);
 
-		if (att[attnum]->attlen <= 0)
+		if (thisatt->attlen <= 0)
 			slow = true;		/* can't use attcacheoff anymore */
 	}
 
@@ -814,6 +818,177 @@ heap_deformtuple(HeapTuple tuple,
 	}
 }
 
+/* ----------------
+ *		slot_deformtuple
+ *
+ *		Given a TupleTableSlot, extract data into cache_values array 
+ *		from the slot's tuple.
+ *
+ *		This is essentially an incremental version of heap_deformtuple:
+ *		on each call we extract attributes up to the one needed, without
+ *		re-computing information about previously extracted attributes.
+ *		slot->cache_natts is the number of attributes already extracted.
+ *
+ *		This only gets called from slot_getattr.  Note that slot_getattr
+ *		must check for a null attribute since we don't create an array
+ *		of null indicators.
+ * ----------------
+ */
+static void
+slot_deformtuple(TupleTableSlot *slot, int natts)
+{
+	HeapTuple		tuple = slot->val;
+	TupleDesc		tupleDesc = slot->ttc_tupleDescriptor;
+	Datum	   *values = slot->cache_values;
+	HeapTupleHeader	tup = tuple->t_data;
+	bool		hasnulls = HeapTupleHasNulls(tuple);
+	Form_pg_attribute *att = tupleDesc->attrs;
+	int			attnum;
+	char	   *tp;					/* ptr to tuple data */
+	long		off;				/* offset in tuple data */
+	bits8	   *bp = tup->t_bits;	/* ptr to null bitmask in tuple */
+	bool		slow;				/* can we use/set attcacheoff? */
+
+	/*
+	 * Check whether the first call for this tuple, and initialize or
+	 * restore loop state.
+	 */
+	attnum = slot->cache_natts;
+	if (attnum == 0)
+	{
+		/* Start from the first attribute */
+		off = 0;
+		slow = false;
+	}
+	else
+	{
+		/* Restore state from previous execution */
+		off = slot->cache_off;
+		slow = slot->cache_slow;
+	}
+
+	tp = (char *) tup + tup->t_hoff;
+
+	for (; attnum < natts; attnum++)
+	{
+		Form_pg_attribute thisatt = att[attnum];
+
+		if (hasnulls && att_isnull(attnum, bp))
+		{
+			values[attnum] = (Datum) 0;
+			slow = true;        /* can't use attcacheoff anymore */
+			continue;
+		}
+
+		if (!slow && thisatt->attcacheoff >= 0)
+			off = thisatt->attcacheoff;
+		else
+		{
+			off = att_align(off, thisatt->attalign);
+
+			if (!slow)
+				thisatt->attcacheoff = off;
+		}
+
+		values[attnum] = fetchatt(thisatt, tp + off);
+
+		off = att_addlength(off, thisatt->attlen, tp + off);
+
+		if (thisatt->attlen <= 0)
+			slow = true;        /* can't use attcacheoff anymore */
+	}
+
+	/*
+	 * Save state for next execution
+	 */
+	slot->cache_natts = attnum;
+	slot->cache_off = off;
+	slot->cache_slow = slow;
+}
+
+/* --------------------------------
+ *		slot_getattr
+ *
+ *		This function fetches an attribute of the slot's current tuple.
+ *		It is functionally equivalent to heap_getattr, but fetches of
+ *		multiple attributes of the same tuple will be optimized better,
+ *		because we avoid O(N^2) behavior from multiple calls of
+ *		nocachegetattr(), even when attcacheoff isn't usable.
+ * --------------------------------
+ */
+Datum
+slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+	HeapTuple		tuple = slot->val;
+	TupleDesc		tupleDesc = slot->ttc_tupleDescriptor;
+	HeapTupleHeader	tup;
+
+	/*
+	 * system attributes are handled by heap_getsysattr
+	 */
+	if (attnum <= 0)
+		return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
+
+	/*
+	 * check if attnum is out of range according to either the tupdesc
+	 * or the tuple itself; if so return NULL
+	 */
+	tup = tuple->t_data;
+
+	if (attnum > tup->t_natts || attnum > tupleDesc->natts)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+
+	/*
+	 * check if target attribute is null
+	 */
+	if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+
+	/*
+	 * If the attribute's column has been dropped, we force a NULL
+	 * result. This case should not happen in normal use, but it could
+	 * happen if we are executing a plan cached before the column was
+	 * dropped.
+	 */
+	if (tupleDesc->attrs[attnum - 1]->attisdropped)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+
+	/*
+	 * If attribute wasn't already extracted, extract it and preceding
+	 * attributes.
+	 */
+	if (attnum > slot->cache_natts)
+	{
+		/*
+		 * If first time for this TupleTableSlot, allocate the cache
+		 * workspace.  It must have the same lifetime as the slot, so allocate
+		 * it in the slot's own context.  We size the array according to what
+		 * the tupdesc says, NOT the tuple.
+		 */
+		if (slot->cache_values == NULL)
+			slot->cache_values = (Datum *)
+				MemoryContextAlloc(slot->ttc_mcxt,
+								   tupleDesc->natts * sizeof(Datum));
+
+		slot_deformtuple(slot, attnum);
+	}
+
+	/*
+	 * The result is acquired from cache_values array.
+	 */
+	*isnull = false;
+	return slot->cache_values[attnum - 1];
+}
+
 /* ----------------
  *		heap_freetuple
  * ----------------
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c06b3b8d36a..44ed126fca2 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.47 2005/01/01 05:43:06 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.48 2005/03/14 04:41:12 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1197,9 +1197,9 @@ toast_fetch_datum(varattrib *attr)
 		/*
 		 * Have a chunk, extract the sequence number and the data
 		 */
-		residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+		residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
 		Assert(!isnull);
-		chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+		chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
 		Assert(!isnull);
 		chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
 
@@ -1372,9 +1372,9 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 		/*
 		 * Have a chunk, extract the sequence number and the data
 		 */
-		residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull));
+		residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
 		Assert(!isnull);
-		chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull));
+		chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
 		Assert(!isnull);
 		chunksize = VARATT_SIZE(chunk) - VARHDRSZ;
 
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index 5087c7bfc39..f747976acfa 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.46 2004/12/31 21:59:45 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.47 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -209,20 +209,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
 					 Datum *value,
 					 bool *isNull)
 {
-	List	   *targetList;
 	ListCell   *t;
-	AttrNumber	resno;
-	TupleDesc	tupType;
-	HeapTuple	tuple;
 
 	/*
-	 * first look in the junkfilter's target list for an attribute with
+	 * Look in the junkfilter's target list for an attribute with
 	 * the given name
 	 */
-	resno = InvalidAttrNumber;
-	targetList = junkfilter->jf_targetList;
-
-	foreach(t, targetList)
+	foreach(t, junkfilter->jf_targetList)
 	{
 		TargetEntry *tle = lfirst(t);
 		Resdom	   *resdom = tle->resdom;
@@ -231,26 +224,13 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
 			(strcmp(resdom->resname, attrName) == 0))
 		{
 			/* We found it ! */
-			resno = resdom->resno;
-			break;
+			*value = slot_getattr(slot, resdom->resno, isNull);
+			return true;
 		}
 	}
 
-	if (resno == InvalidAttrNumber)
-	{
-		/* Ooops! We couldn't find this attribute... */
-		return false;
-	}
-
-	/*
-	 * Now extract the attribute value from the tuple.
-	 */
-	tuple = slot->val;
-	tupType = slot->ttc_tupleDescriptor;
-
-	*value = heap_getattr(tuple, resno, tupType, isNull);
-
-	return true;
+	/* Ooops! We couldn't find this attribute... */
+	return false;
 }
 
 /*
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 0199df0f719..87cc201a3e3 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.171 2004/12/31 21:59:45 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.172 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -438,11 +438,8 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 			bool *isNull, ExprDoneCond *isDone)
 {
 	Var		   *variable = (Var *) exprstate->expr;
-	Datum		result;
 	TupleTableSlot *slot;
 	AttrNumber	attnum;
-	HeapTuple	heapTuple;
-	TupleDesc	tuple_type;
 
 	if (isDone)
 		*isDone = ExprSingleResult;
@@ -475,35 +472,19 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 			break;
 	}
 
-	/*
-	 * extract tuple information from the slot
-	 */
-	heapTuple = slot->val;
-	tuple_type = slot->ttc_tupleDescriptor;
-
+#ifdef USE_ASSERT_CHECKING
 	/*
 	 * Some checks that are only applied for user attribute numbers (bogus
-	 * system attnums will be caught inside heap_getattr).
+	 * system attnums will be caught inside slot_getattr).
 	 */
 	if (attnum > 0)
 	{
+		TupleDesc	tuple_type = slot->ttc_tupleDescriptor;
+
 		/*
 		 * This assert checks that the attnum is valid.
 		 */
-		Assert(attnum <= tuple_type->natts &&
-			   tuple_type->attrs[attnum - 1] != NULL);
-
-		/*
-		 * If the attribute's column has been dropped, we force a NULL
-		 * result. This case should not happen in normal use, but it could
-		 * happen if we are executing a plan cached before the column was
-		 * dropped.
-		 */
-		if (tuple_type->attrs[attnum - 1]->attisdropped)
-		{
-			*isNull = true;
-			return (Datum) 0;
-		}
+		Assert(attnum <= tuple_type->natts);
 
 		/*
 		 * This assert checks that the datatype the plan expects to get
@@ -515,16 +496,12 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 		 * Note that we can't check dropped columns, since their atttypid has
 		 * been zeroed.
 		 */
-		Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid);
+		Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid ||
+			   tuple_type->attrs[attnum - 1]->attisdropped);
 	}
+#endif /* USE_ASSERT_CHECKING */
 
-	result = heap_getattr(heapTuple,	/* tuple containing attribute */
-						  attnum,		/* attribute number of desired
-										 * attribute */
-						  tuple_type,	/* tuple descriptor of tuple */
-						  isNull);		/* return: is attribute null? */
-
-	return result;
+	return slot_getattr(slot, attnum, isNull);
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 2864589ebb3..8574efc95d8 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.83 2004/12/31 21:59:45 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.84 2005/03/14 04:41:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,13 +31,11 @@
  *
  *	 SLOT ACCESSORS
  *		ExecStoreTuple			- store a tuple in the table
- *		ExecFetchTuple			- fetch a tuple from the table
  *		ExecClearTuple			- clear contents of a table slot
  *		ExecSetSlotDescriptor	- set a slot's tuple descriptor
- *		ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
  *
  *	 SLOT STATUS PREDICATES
- *		TupIsNull				- true when slot contains no tuple(Macro)
+ *		TupIsNull				- true when slot contains no tuple (macro)
  *
  *	 CONVENIENCE INITIALIZATION ROUTINES
  *		ExecInitResultTupleSlot    \	convenience routines to initialize
@@ -60,7 +58,7 @@
  *		- ExecInitSeqScan() calls ExecInitScanTupleSlot() and
  *		  ExecInitResultTupleSlot() to reserve places in the tuple
  *		  table for the tuples returned by the access methods and the
- *		  tuples resulting from preforming target list projections.
+ *		  tuples resulting from performing target list projections.
  *
  *		During ExecRun()
  *		----------------
@@ -71,8 +69,8 @@
  *		  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
- *		  is then returned.
+ *		  the slot passed to it (by direct access to slot->val, which is
+ *		  ugly but not worth changing).  this tuple is then returned.
  *
  *		At ExecEnd()
  *		----------------
@@ -87,23 +85,6 @@
  *		this information is also kept in the ExprContext of each node.
  *		Soon the executor will be redesigned and ExprContext's will contain
  *		only slot pointers.  -cim 3/14/91
- *
- *	 NOTES
- *		The tuple table stuff is relatively new, put here to alleviate
- *		the process growth problems in the executor.  The other routines
- *		are old (from the original lisp system) and may someday become
- *		obsolete.  -cim 6/23/90
- *
- *		In the implementation of nested-dot queries such as
- *		"retrieve (EMP.hobbies.all)", a single scan may return tuples
- *		of many types, so now we return pointers to tuple descriptors
- *		along with tuples returned via the tuple table.  This means
- *		we now have a bunch of routines to diddle the slot descriptors
- *		too.  -cim 1/18/90
- *
- *		The tuple table stuff depends on the executor/tuptable.h macros,
- *		and the TupleTableSlot node in execnodes.h.
- *
  */
 #include "postgres.h"
 
@@ -124,43 +105,52 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList,
  *				  tuple table create/delete functions
  * ----------------------------------------------------------------
  */
+
 /* --------------------------------
  *		ExecCreateTupleTable
  *
- *		This creates a new tuple table of the specified initial
- *		size.  If the size is insufficient, ExecAllocTableSlot()
- *		will grow the table as necessary.
+ *		This creates a new tuple table of the specified size.
  *
  *		This should be used by InitPlan() to allocate the table.
  *		The table's address will be stored in the EState structure.
  * --------------------------------
  */
-TupleTable						/* return: address of table */
-ExecCreateTupleTable(int initialSize)	/* initial number of slots in
-										 * table */
+TupleTable
+ExecCreateTupleTable(int tableSize)
 {
-	TupleTable	newtable;		/* newly allocated table */
-	TupleTableSlot *array;		/* newly allocated slot array */
+	TupleTable	newtable;
+	int			i;
 
 	/*
 	 * sanity checks
 	 */
-	Assert(initialSize >= 1);
+	Assert(tableSize >= 1);
 
 	/*
-	 * Now allocate our new table along with space for the pointers to the
-	 * tuples.	Zero out the slots.
+	 * allocate the table itself
 	 */
-
-	newtable = (TupleTable) palloc(sizeof(TupleTableData));
-	array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
-
-	/*
-	 * initialize the new table and return it to the caller.
-	 */
-	newtable->size = initialSize;
+	newtable = (TupleTable) palloc(sizeof(TupleTableData) +
+								   (tableSize - 1) * sizeof(TupleTableSlot));
+	newtable->size = tableSize;
 	newtable->next = 0;
-	newtable->array = array;
+
+	/*
+	 * initialize all the slots to empty states
+	 */
+	for (i = 0; i < tableSize; i++)
+	{
+		TupleTableSlot *slot = &(newtable->array[i]);
+
+		slot->type = T_TupleTableSlot;
+		slot->val = NULL;
+		slot->ttc_tupleDescriptor = NULL;
+		slot->ttc_shouldFree = false;
+		slot->ttc_shouldFreeDesc = false;
+		slot->ttc_buffer = InvalidBuffer;
+		slot->ttc_mcxt = CurrentMemoryContext;
+		slot->cache_values = NULL;
+		slot->cache_natts = 0;	/* mark slot_getattr state invalid */
+	}
 
 	return newtable;
 }
@@ -178,21 +168,11 @@ ExecDropTupleTable(TupleTable table,	/* tuple table */
 				   bool shouldFree)		/* true if we should free slot
 										 * contents */
 {
-	int			next;			/* next available slot */
-	TupleTableSlot *array;		/* start of table array */
-	int			i;				/* counter */
-
 	/*
 	 * sanity checks
 	 */
 	Assert(table != NULL);
 
-	/*
-	 * get information from the table
-	 */
-	array = table->array;
-	next = table->next;
-
 	/*
 	 * first free all the valid pointers in the tuple array and drop
 	 * refcounts of any referenced buffers, if that's what the caller
@@ -201,86 +181,27 @@ ExecDropTupleTable(TupleTable table,	/* tuple table */
 	 */
 	if (shouldFree)
 	{
+		int			next = table->next;
+		int			i;
+
 		for (i = 0; i < next; i++)
 		{
-			ExecClearTuple(&array[i]);
-			if (array[i].ttc_shouldFreeDesc &&
-				array[i].ttc_tupleDescriptor != NULL)
-				FreeTupleDesc(array[i].ttc_tupleDescriptor);
+			TupleTableSlot *slot = &(table->array[i]);
+
+			ExecClearTuple(slot);
+			if (slot->ttc_shouldFreeDesc)
+				FreeTupleDesc(slot->ttc_tupleDescriptor);
+			if (slot->cache_values)
+				pfree(slot->cache_values);
 		}
 	}
 
 	/*
-	 * finally free the tuple array and the table itself.
+	 * finally free the tuple table itself.
 	 */
-	pfree(array);
 	pfree(table);
 }
 
-
-/* ----------------------------------------------------------------
- *				  tuple table slot reservation functions
- * ----------------------------------------------------------------
- */
-/* --------------------------------
- *		ExecAllocTableSlot
- *
- *		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)
- *		once per slot needed by the node.  Not all nodes need
- *		slots (some just pass tuples around).
- * --------------------------------
- */
-TupleTableSlot *
-ExecAllocTableSlot(TupleTable table)
-{
-	int			slotnum;		/* new slot number */
-	TupleTableSlot *slot;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(table != NULL);
-
-	/*
-	 * if our table is full we have to allocate a larger size table. Since
-	 * ExecAllocTableSlot() is only called before the table is ever used
-	 * to store tuples, we don't have to worry about the contents of the
-	 * old table. If this changes, then we will have to preserve the
-	 * contents. -cim 6/23/90
-	 *
-	 * Unfortunately, we *cannot* do this.	All of the nodes in the plan that
-	 * have already initialized their slots will have 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.	Fail if it does.
-	 * -mer 4 Aug 1992
-	 */
-	if (table->next >= table->size)
-		elog(ERROR, "plan requires more slots than are available");
-
-	/*
-	 * at this point, space in the table is guaranteed so we reserve the
-	 * next slot, initialize and return it.
-	 */
-	slotnum = table->next;
-	table->next++;
-
-	slot = &(table->array[slotnum]);
-
-	/* Make sure the allocated slot is valid (and empty) */
-	slot->type = T_TupleTableSlot;
-	slot->val = NULL;
-	slot->ttc_shouldFree = true;
-	slot->ttc_descIsNew = true;
-	slot->ttc_shouldFreeDesc = true;
-	slot->ttc_tupleDescriptor = NULL;
-	slot->ttc_buffer = InvalidBuffer;
-
-	return slot;
-}
-
 /* --------------------------------
  *		MakeTupleTableSlot
  *
@@ -295,17 +216,59 @@ MakeTupleTableSlot(void)
 {
 	TupleTableSlot *slot = makeNode(TupleTableSlot);
 
-	/* This should match ExecAllocTableSlot() */
+	/* This should match ExecCreateTupleTable() */
 	slot->val = NULL;
-	slot->ttc_shouldFree = true;
-	slot->ttc_descIsNew = true;
-	slot->ttc_shouldFreeDesc = true;
 	slot->ttc_tupleDescriptor = NULL;
+	slot->ttc_shouldFree = false;
+	slot->ttc_shouldFreeDesc = false;
 	slot->ttc_buffer = InvalidBuffer;
+	slot->ttc_mcxt = CurrentMemoryContext;
+	slot->cache_values = NULL;
+	slot->cache_natts = 0;	/* mark slot_getattr state invalid */
 
 	return slot;
 }
 
+
+/* ----------------------------------------------------------------
+ *				  tuple table slot reservation functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *		ExecAllocTableSlot
+ *
+ *		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)
+ *		once per slot needed by the node.  Not all nodes need
+ *		slots (some just pass tuples around).
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecAllocTableSlot(TupleTable table)
+{
+	int			slotnum;		/* new slot number */
+
+	/*
+	 * sanity checks
+	 */
+	Assert(table != NULL);
+
+	/*
+	 * We expect that the table was made big enough to begin with.
+	 * We cannot reallocate it on the fly since previous plan nodes
+	 * have already got pointers to individual entries.
+	 */
+	if (table->next >= table->size)
+		elog(ERROR, "plan requires more slots than are available");
+
+	slotnum = table->next;
+	table->next++;
+
+	return &(table->array[slotnum]);
+}
+
 /* ----------------------------------------------------------------
  *				  tuple table slot accessor functions
  * ----------------------------------------------------------------
@@ -356,21 +319,22 @@ ExecStoreTuple(HeapTuple tuple,
 	/* passing shouldFree=true for a tuple on a disk page is not sane */
 	Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
 
-	/* clear out any old contents of the slot */
+	/*
+	 * clear out any old contents of the slot
+	 */
 	ExecClearTuple(slot);
 
 	/*
-	 * store the new tuple into the specified slot and return the slot
-	 * into which we stored the tuple.
+	 * store the new tuple into the specified slot.
 	 */
 	slot->val = tuple;
-	slot->ttc_buffer = buffer;
 	slot->ttc_shouldFree = shouldFree;
 
 	/*
 	 * If tuple is on a disk page, keep the page pinned as long as we hold
-	 * a pointer into it.
+	 * a pointer into it.  We assume the caller already has such a pin.
 	 */
+	slot->ttc_buffer = buffer;
 	if (BufferIsValid(buffer))
 		IncrBufferRefCount(buffer);
 
@@ -388,27 +352,23 @@ ExecStoreTuple(HeapTuple tuple,
 TupleTableSlot *				/* return: slot passed */
 ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 {
-	HeapTuple	oldtuple;		/* prior contents of slot */
-
 	/*
 	 * sanity checks
 	 */
 	Assert(slot != NULL);
 
 	/*
-	 * get information from the tuple table
+	 * Free the old contents of the specified slot if necessary.  (Note:
+	 * we allow slot->val to be null even when shouldFree is true, because
+	 * there are a few callers of ExecStoreTuple that are too lazy to
+	 * distinguish whether they are passing a NULL tuple, and always pass
+	 * shouldFree = true.)
 	 */
-	oldtuple = slot->val;
-
-	/*
-	 * free the old contents of the specified slot if necessary.
-	 */
-	if (slot->ttc_shouldFree && oldtuple != NULL)
-		heap_freetuple(oldtuple);
+	if (slot->ttc_shouldFree && slot->val != NULL)
+		heap_freetuple(slot->val);
 
 	slot->val = NULL;
-
-	slot->ttc_shouldFree = true;	/* probably useless code... */
+	slot->ttc_shouldFree = false;
 
 	/*
 	 * Drop the pin on the referenced buffer, if there is one.
@@ -418,6 +378,11 @@ ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 
 	slot->ttc_buffer = InvalidBuffer;
 
+	/*
+	 * mark slot_getattr state invalid
+	 */
+	slot->cache_natts = 0;
+
 	return slot;
 }
 
@@ -433,36 +398,31 @@ ExecSetSlotDescriptor(TupleTableSlot *slot,		/* slot to change */
 					  TupleDesc tupdesc,		/* new tuple descriptor */
 					  bool shouldFree)	/* is desc owned by slot? */
 {
-	if (slot->ttc_shouldFreeDesc &&
-		slot->ttc_tupleDescriptor != NULL)
+	if (slot->ttc_shouldFreeDesc)
 		FreeTupleDesc(slot->ttc_tupleDescriptor);
 
 	slot->ttc_tupleDescriptor = tupdesc;
 	slot->ttc_shouldFreeDesc = shouldFree;
+
+	/*
+	 * mark slot_getattr state invalid
+	 */
+	slot->cache_natts = 0;
+
+	/*
+	 * release any old cache array since tupledesc's natts may have changed
+	 */
+	if (slot->cache_values)
+		pfree(slot->cache_values);
+	slot->cache_values = NULL;
 }
 
-/* --------------------------------
- *		ExecSetSlotDescriptorIsNew
- *
- *		This function is used to change the setting of the "isNew" flag
- * --------------------------------
- */
-void
-ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,		/* slot to change */
-						   bool isNew)	/* "isNew" setting */
-{
-	slot->ttc_descIsNew = isNew;
-}
-
-/* ----------------------------------------------------------------
- *				  tuple table slot status predicates
- * ----------------------------------------------------------------
- */
 
 /* ----------------------------------------------------------------
  *				convenience initialization routines
  * ----------------------------------------------------------------
  */
+
 /* --------------------------------
  *		ExecInit{Result,Scan,Extra}TupleSlot
  *
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 6fa3bbf4550..3b73d5ea8a1 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.94 2005/01/27 23:24:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.95 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,9 +40,6 @@
  * ----------------
  */
 
-extern Datum nocachegetattr(HeapTuple tup, int attnum,
-			   TupleDesc att, bool *isnull);
-
 #if !defined(DISABLE_COMPLEX_MACRO)
 
 #define fastgetattr(tup, attnum, tupleDesc, isnull)					\
@@ -115,9 +112,6 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
 	) \
 )
 
-extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
-				bool *isnull);
-
 
 /* ----------------
  *		function prototypes for heap access method
@@ -191,6 +185,8 @@ extern void DataFill(char *data, TupleDesc tupleDesc,
 extern int	heap_attisnull(HeapTuple tup, int attnum);
 extern Datum nocachegetattr(HeapTuple tup, int attnum,
 			   TupleDesc att, bool *isnull);
+extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
+				bool *isnull);
 extern HeapTuple heap_copytuple(HeapTuple tuple);
 extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
 extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index f07bdd89d18..8eb357636ad 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.115 2004/12/31 22:03:29 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.116 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -158,10 +158,10 @@ extern void ExecAssignScanProjectionInfo(ScanState *node);
 /*
  * prototypes from functions in execTuples.c
  */
-extern TupleTable ExecCreateTupleTable(int initialSize);
+extern TupleTable ExecCreateTupleTable(int tableSize);
 extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
-extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
 extern TupleTableSlot *MakeTupleTableSlot(void);
+extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
 extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 			   TupleTableSlot *slot,
 			   Buffer buffer,
@@ -169,7 +169,6 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
 					  TupleDesc tupdesc, bool shouldFree);
-extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
 extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate);
 extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index e496a666ce4..90791366ffb 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -7,11 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.26 2004/12/31 22:03:29 pgsql Exp $
- *
- * NOTES
- *	  The tuple table interface is getting pretty ugly.
- *	  It should be redesigned soon.
+ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.27 2005/03/14 04:41:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,65 +16,60 @@
 
 #include "access/htup.h"
 
-/* ----------------
- *		The executor tuple table is managed and manipulated by special
- *		code in executor/execTuples.c.
+
+/*
+ * The executor stores pointers to tuples in a "tuple table"
+ * which is composed of TupleTableSlots.  Sometimes the tuples
+ * are pointers to buffer pages, while others are pointers to
+ * palloc'ed memory; the shouldFree variable tells us whether
+ * we may call pfree() on a tuple.  When shouldFree is true,
+ * the tuple is "owned" by the TupleTableSlot and should be
+ * freed when the slot's reference to the tuple is dropped.
  *
- *		TupleTableSlot information
+ * shouldFreeDesc is similar to shouldFree: if it's true, then the
+ * tupleDescriptor is "owned" by the TupleTableSlot and should be
+ * freed when the slot's reference to the descriptor is dropped.
  *
- *			val					current tuple, or NULL if no tuple
- *			shouldFree			boolean - should we pfree() tuple
- *			descIsNew			boolean - true when tupleDescriptor changes
- *			tupleDescriptor		type information for the tuple data
- *			shouldFreeDesc		boolean - should we free tupleDescriptor
- *			buffer				the buffer for tuples pointing to disk pages
+ * If buffer is not InvalidBuffer, then the slot is holding a pin
+ * on the indicated buffer page; drop the pin when we release the
+ * slot's reference to that buffer.  (shouldFree should always be
+ * false in such a case, since presumably val is pointing at the
+ * buffer page.)
  *
- *		The executor stores pointers to tuples in a ``tuple table''
- *		which is composed of TupleTableSlots.  Sometimes the tuples
- *		are pointers to buffer pages, while others are pointers to
- *		palloc'ed memory; the shouldFree variable tells us when
- *		we may call pfree() on a tuple.  -cim 9/23/90
- *
- *		If buffer is not InvalidBuffer, then the slot is holding a pin
- *		on the indicated buffer page; drop the pin when we release the
- *		slot's reference to that buffer.
- *
- *		In the implementation of nested-dot queries such as
- *		"retrieve (EMP.hobbies.all)", a single scan may return tuples
- *		of many types, so now we return pointers to tuple descriptors
- *		along with tuples returned via the tuple table.  -cim 1/18/90
- *
- *		shouldFreeDesc is similar to shouldFree: if it's true, then the
- *		tupleDescriptor is "owned" by the TupleTableSlot and should be
- *		freed when the slot's reference to the descriptor is dropped.
- *
- *		See executor.h for decls of functions defined in execTuples.c
- *		-jolly
- *
- * ----------------
+ * The slot_getattr() routine allows extraction of attribute values from
+ * a TupleTableSlot's current tuple.  It is equivalent to heap_getattr()
+ * except that it can optimize fetching of multiple values more efficiently.
+ * The cache_xxx fields of TupleTableSlot are support for slot_getattr().
  */
 typedef struct TupleTableSlot
 {
-	NodeTag		type;
-	HeapTuple	val;
-	bool		ttc_shouldFree;
-	bool		ttc_descIsNew;
-	bool		ttc_shouldFreeDesc;
-	TupleDesc	ttc_tupleDescriptor;
-	Buffer		ttc_buffer;
+	NodeTag		type;			/* vestigial ... allows IsA tests */
+	HeapTuple	val;			/* current tuple, or NULL if none */
+	TupleDesc	ttc_tupleDescriptor;	/* tuple's descriptor */
+	bool		ttc_shouldFree;			/* should pfree tuple? */
+	bool		ttc_shouldFreeDesc;		/* should pfree descriptor? */
+	Buffer		ttc_buffer;		/* tuple's buffer, or InvalidBuffer */
+	MemoryContext ttc_mcxt;		/* slot itself is in this context */
+	Datum	   *cache_values;	/* currently extracted values */
+	int			cache_natts;	/* # of valid values in cache_values */
+	bool		cache_slow;		/* saved state for slot_getattr */
+	long		cache_off;		/* saved state for slot_getattr */
 } TupleTableSlot;
 
-/* ----------------
- *		tuple table data structure
- * ----------------
+/*
+ * Tuple table data structure: an array of TupleTableSlots.
  */
 typedef struct TupleTableData
 {
-	int			size;			/* size of the table */
+	int			size;			/* size of the table (number of slots) */
 	int			next;			/* next available slot number */
-	TupleTableSlot *array;		/* array of TupleTableSlot's */
-} TupleTableData;
+	TupleTableSlot array[1];	/* VARIABLE LENGTH ARRAY - must be last */
+} TupleTableData;				/* VARIABLE LENGTH STRUCT */
 
 typedef TupleTableData *TupleTable;
 
+
+/* in access/common/heaptuple.c */
+extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
+
 #endif   /* TUPTABLE_H */