diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index f5f751e72f..7ab363972e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -934,19 +934,16 @@ get_crosstab_tuplestore(char *sql,
 				 */
 				if (lastrowid != NULL)
 				{
-					/*
-					 * switch to appropriate context while storing the tuple
-					 */
-					SPIcontext = MemoryContextSwitchTo(per_query_ctx);
-
 					/* rowid changed, flush the previous output row */
 					tuple = BuildTupleFromCStrings(attinmeta, values);
+
+					/* switch to appropriate context while storing the tuple */
+					SPIcontext = MemoryContextSwitchTo(per_query_ctx);
 					tuplestore_puttuple(tupstore, tuple);
+					MemoryContextSwitchTo(SPIcontext);
+
 					for (j = 0; j < result_ncols; j++)
 						xpfree(values[j]);
-
-					/* now reset the context */
-					MemoryContextSwitchTo(SPIcontext);
 				}
 
 				values[0] = rowid;
@@ -970,16 +967,13 @@ get_crosstab_tuplestore(char *sql,
 			lastrowid = pstrdup(rowid);
 		}
 
-		/* switch to appropriate context while storing the tuple */
-		SPIcontext = MemoryContextSwitchTo(per_query_ctx);
-
 		/* flush the last output row */
 		tuple = BuildTupleFromCStrings(attinmeta, values);
+
+		/* switch to appropriate context while storing the tuple */
+		SPIcontext = MemoryContextSwitchTo(per_query_ctx);
 		tuplestore_puttuple(tupstore, tuple);
-
-		/* now reset the context */
 		MemoryContextSwitchTo(SPIcontext);
-
 	}
 
 	if (SPI_finish() != SPI_OK_FINISH)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 9d77ab2779..7ec314379b 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.106 2006/03/05 15:58:20 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.107 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1295,6 +1295,8 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
 	{
 		if (tuple == NULL)		/* internal error */
 			elog(ERROR, "cannot extract system attribute from virtual tuple");
+		if (slot->tts_mintuple)	/* internal error */
+			elog(ERROR, "cannot extract system attribute from minimal tuple");
 		return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
 	}
 
@@ -1479,6 +1481,8 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
 	{
 		if (tuple == NULL)		/* internal error */
 			elog(ERROR, "cannot extract system attribute from virtual tuple");
+		if (slot->tts_mintuple)	/* internal error */
+			elog(ERROR, "cannot extract system attribute from minimal tuple");
 		return heap_attisnull(tuple, attnum);
 	}
 
@@ -1505,9 +1509,8 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
 	return heap_attisnull(tuple, attnum);
 }
 
-/* ----------------
- *		heap_freetuple
- * ----------------
+/*
+ * heap_freetuple
  */
 void
 heap_freetuple(HeapTuple htup)
@@ -1516,6 +1519,173 @@ heap_freetuple(HeapTuple htup)
 }
 
 
+/*
+ * heap_form_minimal_tuple
+ *		construct a MinimalTuple from the given values[] and isnull[] arrays,
+ *		which are of the length indicated by tupleDescriptor->natts
+ *
+ * This is exactly like heap_form_tuple() except that the result is a
+ * "minimal" tuple lacking a HeapTupleData header as well as room for system
+ * columns.
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+heap_form_minimal_tuple(TupleDesc tupleDescriptor,
+						Datum *values,
+						bool *isnull)
+{
+	MinimalTuple tuple;			/* return tuple */
+	unsigned long len;
+	int			hoff;
+	bool		hasnull = false;
+	Form_pg_attribute *att = tupleDescriptor->attrs;
+	int			numberOfAttributes = tupleDescriptor->natts;
+	int			i;
+
+	if (numberOfAttributes > MaxTupleAttributeNumber)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_COLUMNS),
+				 errmsg("number of columns (%d) exceeds limit (%d)",
+						numberOfAttributes, MaxTupleAttributeNumber)));
+
+	/*
+	 * Check for nulls and embedded tuples; expand any toasted attributes in
+	 * embedded tuples.  This preserves the invariant that toasting can only
+	 * go one level deep.
+	 *
+	 * We can skip calling toast_flatten_tuple_attribute() if the attribute
+	 * couldn't possibly be of composite type.  All composite datums are
+	 * varlena and have alignment 'd'; furthermore they aren't arrays. Also,
+	 * if an attribute is already toasted, it must have been sent to disk
+	 * already and so cannot contain toasted attributes.
+	 */
+	for (i = 0; i < numberOfAttributes; i++)
+	{
+		if (isnull[i])
+			hasnull = true;
+		else if (att[i]->attlen == -1 &&
+				 att[i]->attalign == 'd' &&
+				 att[i]->attndims == 0 &&
+				 !VARATT_IS_EXTENDED(values[i]))
+		{
+			values[i] = toast_flatten_tuple_attribute(values[i],
+													  att[i]->atttypid,
+													  att[i]->atttypmod);
+		}
+	}
+
+	/*
+	 * Determine total space needed
+	 */
+	len = offsetof(MinimalTupleData, t_bits);
+
+	if (hasnull)
+		len += BITMAPLEN(numberOfAttributes);
+
+	if (tupleDescriptor->tdhasoid)
+		len += sizeof(Oid);
+
+	hoff = len = MAXALIGN(len); /* align user data safely */
+
+	len += heap_compute_data_size(tupleDescriptor, values, isnull);
+
+	/*
+	 * Allocate and zero the space needed.
+	 */
+	tuple = (MinimalTuple) palloc0(len);
+
+	/*
+	 * And fill in the information.
+	 */
+	tuple->t_len = len;
+	tuple->t_natts = numberOfAttributes;
+	tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
+
+	if (tupleDescriptor->tdhasoid)		/* else leave infomask = 0 */
+		tuple->t_infomask = HEAP_HASOID;
+
+	heap_fill_tuple(tupleDescriptor,
+					values,
+					isnull,
+					(char *) tuple + hoff,
+					&tuple->t_infomask,
+					(hasnull ? tuple->t_bits : NULL));
+
+	return tuple;
+}
+
+/*
+ * heap_free_minimal_tuple
+ */
+void
+heap_free_minimal_tuple(MinimalTuple mtup)
+{
+	pfree(mtup);
+}
+
+/*
+ * heap_copy_minimal_tuple
+ *		copy a MinimalTuple
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+heap_copy_minimal_tuple(MinimalTuple mtup)
+{
+	MinimalTuple result;
+
+	result = (MinimalTuple) palloc(mtup->t_len);
+	memcpy(result, mtup, mtup->t_len);
+	return result;
+}
+
+/*
+ * heap_tuple_from_minimal_tuple
+ *		create a HeapTuple by copying from a MinimalTuple;
+ *		system columns are filled with zeroes
+ *
+ * The result is allocated in the current memory context.
+ * The HeapTuple struct, tuple header, and tuple data are all allocated
+ * as a single palloc() block.
+ */
+HeapTuple
+heap_tuple_from_minimal_tuple(MinimalTuple mtup)
+{
+	HeapTuple	result;
+	uint32		len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+
+	result = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+	result->t_len = len;
+	ItemPointerSetInvalid(&(result->t_self));
+	result->t_tableOid = InvalidOid;
+	result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
+	memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
+	memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_natts));
+	return result;
+}
+
+/*
+ * minimal_tuple_from_heap_tuple
+ *		create a MinimalTuple by copying from a HeapTuple
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+minimal_tuple_from_heap_tuple(HeapTuple htup)
+{
+	MinimalTuple result;
+	uint32		len;
+
+	Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
+	len = htup->t_len - MINIMAL_TUPLE_OFFSET;
+	result = (MinimalTuple) palloc(len);
+	memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
+	result->t_len = len;
+	return result;
+}
+
+
 /* ----------------
  *		heap_addheader
  *
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 403b5c7064..c4c55c9cf3 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.46 2006/03/05 15:58:24 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.47 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -411,17 +411,8 @@ PersistHoldablePortal(Portal portal)
 
 			for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
 			{
-				HeapTuple	tup;
-				bool		should_free;
-
-				tup = tuplestore_gettuple(portal->holdStore, true,
-										  &should_free);
-
-				if (tup == NULL)
+				if (!tuplestore_advance(portal->holdStore, true))
 					elog(ERROR, "unexpected end of tuple stream");
-
-				if (should_free)
-					pfree(tup);
 			}
 		}
 	}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 971822525f..f03d738619 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.94 2006/06/16 18:42:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.95 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,8 @@
  *	 TABLE CREATE/DELETE
  *		ExecCreateTupleTable	- create a new tuple table
  *		ExecDropTupleTable		- destroy a table
+ *		MakeSingleTupleTableSlot - make a single-slot table
+ *		ExecDropSingleTupleTableSlot - destroy same
  *
  *	 SLOT RESERVATION
  *		ExecAllocTableSlot		- find an available slot in the table
@@ -32,9 +34,11 @@
  *	 SLOT ACCESSORS
  *		ExecSetSlotDescriptor	- set a slot's tuple descriptor
  *		ExecStoreTuple			- store a physical tuple in the slot
+ *		ExecStoreMinimalTuple	- store a minimal physical tuple in the slot
  *		ExecClearTuple			- clear contents of a slot
  *		ExecStoreVirtualTuple	- mark slot as containing a virtual tuple
  *		ExecCopySlotTuple		- build a physical tuple from a slot
+ *		ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot
  *		ExecMaterializeSlot		- convert virtual to physical storage
  *		ExecCopySlot			- copy one slot's contents to another
  *
@@ -150,6 +154,7 @@ ExecCreateTupleTable(int tableSize)
 		slot->tts_nvalid = 0;
 		slot->tts_values = NULL;
 		slot->tts_isnull = NULL;
+		slot->tts_mintuple = NULL;
 	}
 
 	return newtable;
@@ -227,6 +232,7 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
 	slot->tts_nvalid = 0;
 	slot->tts_values = NULL;
 	slot->tts_isnull = NULL;
+	slot->tts_mintuple = NULL;
 
 	ExecSetSlotDescriptor(slot, tupdesc);
 
@@ -405,7 +411,12 @@ ExecStoreTuple(HeapTuple tuple,
 	 * Free any old physical tuple belonging to the slot.
 	 */
 	if (slot->tts_shouldFree)
-		heap_freetuple(slot->tts_tuple);
+	{
+		if (slot->tts_mintuple)
+			heap_free_minimal_tuple(slot->tts_mintuple);
+		else
+			heap_freetuple(slot->tts_tuple);
+	}
 
 	/*
 	 * Store the new tuple into the specified slot.
@@ -413,6 +424,7 @@ ExecStoreTuple(HeapTuple tuple,
 	slot->tts_isempty = false;
 	slot->tts_shouldFree = shouldFree;
 	slot->tts_tuple = tuple;
+	slot->tts_mintuple = NULL;
 
 	/* Mark extracted state invalid */
 	slot->tts_nvalid = 0;
@@ -438,6 +450,63 @@ ExecStoreTuple(HeapTuple tuple,
 	return slot;
 }
 
+/* --------------------------------
+ *		ExecStoreMinimalTuple
+ *
+ *		Like ExecStoreTuple, but insert a "minimal" tuple into the slot.
+ *
+ * No 'buffer' parameter since minimal tuples are never stored in relations.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecStoreMinimalTuple(MinimalTuple mtup,
+					  TupleTableSlot *slot,
+					  bool shouldFree)
+{
+	/*
+	 * sanity checks
+	 */
+	Assert(mtup != NULL);
+	Assert(slot != NULL);
+	Assert(slot->tts_tupleDescriptor != NULL);
+
+	/*
+	 * Free any old physical tuple belonging to the slot.
+	 */
+	if (slot->tts_shouldFree)
+	{
+		if (slot->tts_mintuple)
+			heap_free_minimal_tuple(slot->tts_mintuple);
+		else
+			heap_freetuple(slot->tts_tuple);
+	}
+
+	/*
+	 * Drop the pin on the referenced buffer, if there is one.
+	 */
+	if (BufferIsValid(slot->tts_buffer))
+		ReleaseBuffer(slot->tts_buffer);
+
+	slot->tts_buffer = InvalidBuffer;
+
+	/*
+	 * Store the new tuple into the specified slot.
+	 */
+	slot->tts_isempty = false;
+	slot->tts_shouldFree = shouldFree;
+	slot->tts_tuple = &slot->tts_minhdr;
+	slot->tts_mintuple = mtup;
+
+	slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+	slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
+	/* no need to set t_self or t_tableOid since we won't allow access */
+
+	/* Mark extracted state invalid */
+	slot->tts_nvalid = 0;
+
+	return slot;
+}
+
 /* --------------------------------
  *		ExecClearTuple
  *
@@ -458,9 +527,15 @@ ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 	 * Free the old physical tuple if necessary.
 	 */
 	if (slot->tts_shouldFree)
-		heap_freetuple(slot->tts_tuple);
+	{
+		if (slot->tts_mintuple)
+			heap_free_minimal_tuple(slot->tts_mintuple);
+		else
+			heap_freetuple(slot->tts_tuple);
+	}
 
 	slot->tts_tuple = NULL;
+	slot->tts_mintuple = NULL;
 	slot->tts_shouldFree = false;
 
 	/*
@@ -540,10 +615,10 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
 
 /* --------------------------------
  *		ExecCopySlotTuple
- *			Obtain a copy of a slot's physical tuple.  The copy is
+ *			Obtain a copy of a slot's regular physical tuple.  The copy is
  *			palloc'd in the current memory context.
  *
- *		This works even if the slot contains a virtual tuple;
+ *		This works even if the slot contains a virtual or minimal tuple;
  *		however the "system columns" of the result will not be meaningful.
  * --------------------------------
  */
@@ -560,7 +635,12 @@ ExecCopySlotTuple(TupleTableSlot *slot)
 	 * If we have a physical tuple then just copy it.
 	 */
 	if (slot->tts_tuple)
-		return heap_copytuple(slot->tts_tuple);
+	{
+		if (slot->tts_mintuple)
+			return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
+		else
+			return heap_copytuple(slot->tts_tuple);
+	}
 
 	/*
 	 * Otherwise we need to build a tuple from the Datum array.
@@ -570,12 +650,47 @@ ExecCopySlotTuple(TupleTableSlot *slot)
 						   slot->tts_isnull);
 }
 
+/* --------------------------------
+ *		ExecCopySlotMinimalTuple
+ *			Obtain a copy of a slot's minimal physical tuple.  The copy is
+ *			palloc'd in the current memory context.
+ * --------------------------------
+ */
+MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+	/*
+	 * sanity checks
+	 */
+	Assert(slot != NULL);
+	Assert(!slot->tts_isempty);
+
+	/*
+	 * If we have a physical tuple then just copy it.
+	 */
+	if (slot->tts_tuple)
+	{
+		if (slot->tts_mintuple)
+			return heap_copy_minimal_tuple(slot->tts_mintuple);
+		else
+			return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+	}
+
+	/*
+	 * Otherwise we need to build a tuple from the Datum array.
+	 */
+	return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+								   slot->tts_values,
+								   slot->tts_isnull);
+}
+
 /* --------------------------------
  *		ExecFetchSlotTuple
- *			Fetch the slot's physical tuple.
+ *			Fetch the slot's regular physical tuple.
  *
  *		If the slot contains a virtual tuple, we convert it to physical
  *		form.  The slot retains ownership of the physical tuple.
+ *		Likewise, if it contains a minimal tuple we convert to regular form.
  *
  * The difference between this and ExecMaterializeSlot() is that this
  * does not guarantee that the contained tuple is local storage.
@@ -592,9 +707,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
 	Assert(!slot->tts_isempty);
 
 	/*
-	 * If we have a physical tuple then just return it.
+	 * If we have a regular physical tuple then just return it.
 	 */
-	if (slot->tts_tuple)
+	if (slot->tts_tuple && slot->tts_mintuple == NULL)
 		return slot->tts_tuple;
 
 	/*
@@ -629,10 +744,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
 	Assert(!slot->tts_isempty);
 
 	/*
-	 * If we have a physical tuple, and it's locally palloc'd, we have nothing
-	 * to do.
+	 * If we have a regular physical tuple, and it's locally palloc'd,
+	 * we have nothing to do.
 	 */
-	if (slot->tts_tuple && slot->tts_shouldFree)
+	if (slot->tts_tuple && slot->tts_shouldFree && slot->tts_mintuple == NULL)
 		return slot->tts_tuple;
 
 	/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 0c77e82169..90ab018874 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.39 2006/06/16 18:42:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.40 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,8 +48,6 @@ FunctionNext(FunctionScanState *node)
 	EState	   *estate;
 	ScanDirection direction;
 	Tuplestorestate *tuplestorestate;
-	bool		should_free;
-	HeapTuple	heapTuple;
 
 	/*
 	 * get information from the estate and scan state
@@ -86,14 +84,11 @@ FunctionNext(FunctionScanState *node)
 	/*
 	 * Get the next tuple from tuplestore. Return NULL if no more tuples.
 	 */
-	heapTuple = tuplestore_getheaptuple(tuplestorestate,
-										ScanDirectionIsForward(direction),
-										&should_free);
 	slot = node->ss.ss_ScanTupleSlot;
-	if (heapTuple)
-		return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
-	else
-		return ExecClearTuple(slot);
+	(void) tuplestore_gettupleslot(tuplestorestate,
+								   ScanDirectionIsForward(direction),
+								   slot);
+	return slot;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index eca769a86c..94a0701385 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.54 2006/03/05 15:58:26 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.55 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,8 +44,6 @@ ExecMaterial(MaterialState *node)
 	ScanDirection dir;
 	bool		forward;
 	Tuplestorestate *tuplestorestate;
-	HeapTuple	heapTuple = NULL;
-	bool		should_free = false;
 	bool		eof_tuplestore;
 	TupleTableSlot *slot;
 
@@ -80,27 +78,25 @@ ExecMaterial(MaterialState *node)
 		{
 			/*
 			 * When reversing direction at tuplestore EOF, the first
-			 * getheaptuple call will fetch the last-added tuple; but we want
+			 * gettupleslot call will fetch the last-added tuple; but we want
 			 * to return the one before that, if possible. So do an extra
 			 * fetch.
 			 */
-			heapTuple = tuplestore_getheaptuple(tuplestorestate,
-												forward,
-												&should_free);
-			if (heapTuple == NULL)
+			if (!tuplestore_advance(tuplestorestate, forward))
 				return NULL;	/* the tuplestore must be empty */
-			if (should_free)
-				heap_freetuple(heapTuple);
 		}
 		eof_tuplestore = false;
 	}
 
+	/*
+	 * If we can fetch another tuple from the tuplestore, return it.
+	 */
+	slot = node->ss.ps.ps_ResultTupleSlot;
 	if (!eof_tuplestore)
 	{
-		heapTuple = tuplestore_getheaptuple(tuplestorestate,
-											forward,
-											&should_free);
-		if (heapTuple == NULL && forward)
+		if (tuplestore_gettupleslot(tuplestorestate, forward, slot))
+			return slot;
+		if (forward)
 			eof_tuplestore = true;
 	}
 
@@ -128,26 +124,26 @@ ExecMaterial(MaterialState *node)
 			node->eof_underlying = true;
 			return NULL;
 		}
-		heapTuple = ExecFetchSlotTuple(outerslot);
-		should_free = false;
 
 		/*
-		 * Append returned tuple to tuplestore, too.  NOTE: because the
+		 * Append returned tuple to tuplestore.  NOTE: because the
 		 * tuplestore is certainly in EOF state, its read position will move
 		 * forward over the added tuple.  This is what we want.
 		 */
 		if (tuplestorestate)
-			tuplestore_puttuple(tuplestorestate, (void *) heapTuple);
+			tuplestore_puttupleslot(tuplestorestate, outerslot);
+
+		/*
+		 * And return a copy of the tuple.  (XXX couldn't we just return
+		 * the outerslot?)
+		 */
+		return ExecCopySlot(slot, outerslot);
 	}
 
 	/*
-	 * Return the obtained tuple, if any.
+	 * Nothing left ...
 	 */
-	slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
-	if (heapTuple)
-		return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
-	else
-		return ExecClearTuple(slot);
+	return ExecClearTuple(slot);
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index dffc8899b2..8ebf5b7fd1 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.16 2006/03/05 15:58:27 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.17 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@ tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
 	TStoreState *myState = (TStoreState *) self;
 	MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
 
-	tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
+	tuplestore_puttupleslot(myState->tstore, slot);
 
 	MemoryContextSwitchTo(oldcxt);
 }
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 060ce56792..c79d0eae18 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.101 2006/03/05 15:58:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.102 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -911,21 +911,17 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 		for (;;)
 		{
 			MemoryContext oldcontext;
-			HeapTuple	tup;
-			bool		should_free;
+			bool		ok;
 
 			oldcontext = MemoryContextSwitchTo(portal->holdContext);
 
-			tup = tuplestore_getheaptuple(portal->holdStore, forward,
-										  &should_free);
+			ok = tuplestore_gettupleslot(portal->holdStore, forward, slot);
 
 			MemoryContextSwitchTo(oldcontext);
 
-			if (tup == NULL)
+			if (!ok)
 				break;
 
-			ExecStoreTuple(tup, slot, InvalidBuffer, should_free);
-
 			(*dest->receiveSlot) (slot, dest);
 
 			ExecClearTuple(slot);
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 6ff6b72082..a2ed330ccc 100644
--- a/src/backend/utils/sort/tuplestore.c
+++ b/src/backend/utils/sort/tuplestore.c
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.27 2006/03/05 15:58:49 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.28 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -195,6 +195,7 @@ struct Tuplestorestate
 static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
 						bool interXact,
 						int maxKBytes);
+static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple);
 static void dumptuples(Tuplestorestate *state);
 static unsigned int getlen(Tuplestorestate *state, bool eofOK);
 static void *copytup_heap(Tuplestorestate *state, void *tup);
@@ -304,15 +305,45 @@ tuplestore_ateof(Tuplestorestate *state)
  * If the read status is currently "AT EOF" then it remains so (the read
  * pointer advances along with the write pointer); otherwise the read
  * pointer is unchanged.  This is for the convenience of nodeMaterial.c.
+ *
+ * tuplestore_puttupleslot() is a convenience routine to collect data from
+ * a TupleTableSlot without an extra copy operation.
  */
 void
-tuplestore_puttuple(Tuplestorestate *state, void *tuple)
+tuplestore_puttupleslot(Tuplestorestate *state,
+						TupleTableSlot *slot)
+{
+	MinimalTuple tuple;
+
+	/*
+	 * Form a MinimalTuple in working memory
+	 */
+	tuple = ExecCopySlotMinimalTuple(slot);
+	USEMEM(state, GetMemoryChunkSpace(tuple));
+
+	tuplestore_puttuple_common(state, (void *) tuple);
+}
+
+/*
+ * "Standard" case to copy from a HeapTuple.  This is actually now somewhat
+ * deprecated, but not worth getting rid of in view of the number of callers.
+ * (Consider adding something that takes a tupdesc+values/nulls arrays so
+ * that we can use heap_form_minimal_tuple() and avoid a copy step.)
+ */
+void
+tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
 {
 	/*
 	 * Copy the tuple.	(Must do this even in WRITEFILE case.)
 	 */
 	tuple = COPYTUP(state, tuple);
 
+	tuplestore_puttuple_common(state, (void *) tuple);
+}
+
+static void
+tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
+{
 	switch (state->status)
 	{
 		case TSS_INMEM:
@@ -389,7 +420,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
  * Returns NULL if no more tuples.	If should_free is set, the
  * caller must pfree the returned tuple when done with it.
  */
-void *
+static void *
 tuplestore_gettuple(Tuplestorestate *state, bool forward,
 					bool *should_free)
 {
@@ -525,6 +556,59 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
 	}
 }
 
+/*
+ * tuplestore_gettupleslot - exported function to fetch a MinimalTuple
+ *
+ * If successful, put tuple in slot and return TRUE; else, clear the slot
+ * and return FALSE.
+ */
+bool
+tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
+						TupleTableSlot *slot)
+{
+	MinimalTuple tuple;
+	bool		should_free;
+
+	tuple = (MinimalTuple) tuplestore_gettuple(state, forward, &should_free);
+
+	if (tuple)
+	{
+		ExecStoreMinimalTuple(tuple, slot, should_free);
+		return true;
+	}
+	else
+	{
+		ExecClearTuple(slot);
+		return false;
+	}
+}
+
+/*
+ * tuplestore_advance - exported function to adjust position without fetching
+ *
+ * We could optimize this case to avoid palloc/pfree overhead, but for the
+ * moment it doesn't seem worthwhile.
+ */
+bool
+tuplestore_advance(Tuplestorestate *state, bool forward)
+{
+	void	   *tuple;
+	bool		should_free;
+
+	tuple = tuplestore_gettuple(state, forward, &should_free);
+
+	if (tuple)
+	{
+		if (should_free)
+			pfree(tuple);
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
 /*
  * dumptuples - remove tuples from memory and write to tape
  *
@@ -672,34 +756,31 @@ getlen(Tuplestorestate *state, bool eofOK)
 
 /*
  * Routines specialized for HeapTuple case
+ *
+ * The stored form is actually a MinimalTuple, but for largely historical
+ * reasons we allow COPYTUP to work from a HeapTuple.
+ *
+ * Since MinimalTuple already has length in its first word, we don't need
+ * to write that separately.
  */
 
 static void *
 copytup_heap(Tuplestorestate *state, void *tup)
 {
-	HeapTuple	tuple = (HeapTuple) tup;
+	MinimalTuple tuple;
 
-	tuple = heap_copytuple(tuple);
+	tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup);
 	USEMEM(state, GetMemoryChunkSpace(tuple));
 	return (void *) tuple;
 }
 
-/*
- * We don't bother to write the HeapTupleData part of the tuple.
- */
-
 static void
 writetup_heap(Tuplestorestate *state, void *tup)
 {
-	HeapTuple	tuple = (HeapTuple) tup;
-	unsigned int tuplen;
+	MinimalTuple tuple = (MinimalTuple) tup;
+	unsigned int tuplen = tuple->t_len;
 
-	tuplen = tuple->t_len + sizeof(tuplen);
-	if (BufFileWrite(state->myfile, (void *) &tuplen,
-					 sizeof(tuplen)) != sizeof(tuplen))
-		elog(ERROR, "write failed");
-	if (BufFileWrite(state->myfile, (void *) tuple->t_data,
-					 tuple->t_len) != (size_t) tuple->t_len)
+	if (BufFileWrite(state->myfile, (void *) tuple, tuplen) != (size_t) tuplen)
 		elog(ERROR, "write failed");
 	if (state->randomAccess)	/* need trailing length word? */
 		if (BufFileWrite(state->myfile, (void *) &tuplen,
@@ -707,23 +788,20 @@ writetup_heap(Tuplestorestate *state, void *tup)
 			elog(ERROR, "write failed");
 
 	FREEMEM(state, GetMemoryChunkSpace(tuple));
-	heap_freetuple(tuple);
+	heap_free_minimal_tuple(tuple);
 }
 
 static void *
 readtup_heap(Tuplestorestate *state, unsigned int len)
 {
-	unsigned int tuplen = len - sizeof(unsigned int) + HEAPTUPLESIZE;
-	HeapTuple	tuple = (HeapTuple) palloc(tuplen);
+	MinimalTuple tuple = (MinimalTuple) palloc(len);
+	unsigned int tuplen;
 
 	USEMEM(state, GetMemoryChunkSpace(tuple));
-	/* reconstruct the HeapTupleData portion */
-	tuple->t_len = len - sizeof(unsigned int);
-	ItemPointerSetInvalid(&(tuple->t_self));
-	tuple->t_data = (HeapTupleHeader) (((char *) tuple) + HEAPTUPLESIZE);
 	/* read in the tuple proper */
-	if (BufFileRead(state->myfile, (void *) tuple->t_data,
-					tuple->t_len) != (size_t) tuple->t_len)
+	tuple->t_len = len;
+	if (BufFileRead(state->myfile, (void *) ((char *) tuple + sizeof(int)),
+					len - sizeof(int)) != (size_t) (len - sizeof(int)))
 		elog(ERROR, "unexpected end of data");
 	if (state->randomAccess)	/* need trailing length word? */
 		if (BufFileRead(state->myfile, (void *) &tuplen,
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index e016a52740..7301be4ae3 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.111 2006/05/10 23:18:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.112 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -217,7 +217,13 @@ extern void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
 				  Datum *values, bool *isnull);
 extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
 				 Datum *values, char *nulls);
-extern void heap_freetuple(HeapTuple tuple);
+extern void heap_freetuple(HeapTuple htup);
+extern MinimalTuple heap_form_minimal_tuple(TupleDesc tupleDescriptor,
+				Datum *values, bool *isnull);
+extern void heap_free_minimal_tuple(MinimalTuple mtup);
+extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup);
+extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup);
+extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
 extern HeapTuple heap_addheader(int natts, bool withoid,
 			   Size structlen, void *structure);
 
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 57db9f8493..170ad657fb 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.82 2006/05/10 23:18:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.83 2006/06/27 02:51:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,7 @@
  */
 #define MaxTupleAttributeNumber 1664	/* 8 * 208 */
 
-/*----------
+/*
  * MaxHeapAttributeNumber limits the number of (user) columns in a table.
  * This should be somewhat less than MaxTupleAttributeNumber.  It must be
  * at least one less, else we will fail to do UPDATEs on a maximal-width
@@ -42,11 +42,10 @@
  * In any case, depending on column data types you will likely be running
  * into the disk-block-based limit on overall tuple size if you have more
  * than a thousand or so columns.  TOAST won't help.
- *----------
  */
 #define MaxHeapAttributeNumber	1600	/* 8 * 200 */
 
-/*----------
+/*
  * Heap tuple header.  To avoid wasting space, the fields should be
  * layed out in such a way to avoid structure padding.
  *
@@ -101,7 +100,6 @@
  * t_infomask), then it is stored just before the user data, which begins at
  * the offset shown by t_hoff.	Note that t_hoff must be a multiple of
  * MAXALIGN.
- *----------
  */
 
 typedef struct HeapTupleFields
@@ -141,6 +139,8 @@ typedef struct HeapTupleHeaderData
 
 	ItemPointerData t_ctid;		/* current TID of this or newer tuple */
 
+	/* Fields below here must match MinimalTupleData! */
+
 	int16		t_natts;		/* number of attributes */
 
 	uint16		t_infomask;		/* various flag bits, see below */
@@ -354,6 +354,62 @@ do { \
 #define FirstLowInvalidHeapAttributeNumber		(-8)
 
 
+/*
+ * MinimalTuple is an alternate representation that is used for transient
+ * tuples inside the executor, in places where transaction status information
+ * is not required, the tuple rowtype is known, and shaving off a few bytes
+ * is worthwhile because we need to store many tuples.  The representation
+ * is chosen so that tuple access routines can work with either full or
+ * minimal tuples via a HeapTupleData pointer structure.  The access routines
+ * see no difference, except that they must not access the transaction status
+ * or t_ctid fields because those aren't there.
+ *
+ * For the most part, MinimalTuples should be accessed via TupleTableSlot
+ * routines.  These routines will prevent access to the "system columns"
+ * and thereby prevent accidental use of the nonexistent fields.
+ *
+ * MinimalTupleData contains a length word, some padding, and fields matching
+ * HeapTupleHeaderData beginning with t_natts.  The padding is chosen so that
+ * offsetof(t_natts) is the same modulo MAXIMUM_ALIGNOF in both structs.
+ * This makes data alignment rules equivalent in both cases.
+ *
+ * When a minimal tuple is accessed via a HeapTupleData pointer, t_data is
+ * set to point MINIMAL_TUPLE_OFFSET bytes before the actual start of the
+ * minimal tuple --- that is, where a full tuple matching the minimal tuple's
+ * data would start.  This trick is what makes the structs seem equivalent.
+ *
+ * Note that t_hoff is computed the same as in a full tuple, hence it includes
+ * the MINIMAL_TUPLE_OFFSET distance.  t_len does not include that, however.
+ */
+#define MINIMAL_TUPLE_OFFSET \
+	((offsetof(HeapTupleHeaderData, t_natts) - sizeof(uint32)) / MAXIMUM_ALIGNOF * MAXIMUM_ALIGNOF)
+#define MINIMAL_TUPLE_PADDING \
+	((offsetof(HeapTupleHeaderData, t_natts) - sizeof(uint32)) % MAXIMUM_ALIGNOF)
+
+typedef struct MinimalTupleData
+{
+	uint32		t_len;			/* actual length of minimal tuple */
+
+	char		mt_padding[MINIMAL_TUPLE_PADDING];
+
+	/* Fields below here must match HeapTupleHeaderData! */
+
+	int16		t_natts;		/* number of attributes */
+
+	uint16		t_infomask;		/* various flag bits, see below */
+
+	uint8		t_hoff;			/* sizeof header incl. bitmap, padding */
+
+	/* ^ - 27 bytes - ^ */
+
+	bits8		t_bits[1];		/* bitmap of NULLs -- VARIABLE LENGTH */
+
+	/* MORE DATA FOLLOWS AT END OF STRUCT */
+} MinimalTupleData;
+
+typedef MinimalTupleData *MinimalTuple;
+
+
 /*
  * HeapTupleData is an in-memory data structure that points to a tuple.
  *
@@ -376,6 +432,11 @@ do { \
  *	 it's difficult to tell apart from case #1.  It should be used only in
  *	 limited contexts where the code knows that case #1 will never apply.)
  *
+ * * Separately allocated minimal tuple: t_data points MINIMAL_TUPLE_OFFSET
+ *	 bytes before the start of a MinimalTuple.  As with the previous case,
+ *	 this can't be told apart from case #1 by inspection; code setting up
+ *	 or destroying this representation has to know what it's doing.
+ *
  * t_len should always be valid, except in the pointer-to-nothing case.
  * t_self and t_tableOid should be valid if the HeapTupleData points to
  * a disk buffer, or if it represents a copy of a tuple on disk.  They
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index b506651fbb..8531835134 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.31 2006/06/16 18:42:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.32 2006/06/27 02:51:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,8 @@
  * independent TupleTableSlots.  There are several cases we need to handle:
  *		1. physical tuple in a disk buffer page
  *		2. physical tuple constructed in palloc'ed memory
- *		3. "virtual" tuple consisting of Datum/isnull arrays
+ *		3. "minimal" physical tuple constructed in palloc'ed memory
+ *		4. "virtual" tuple consisting of Datum/isnull arrays
  *
  * The first two cases are similar in that they both deal with "materialized"
  * tuples, but resource management is different.  For a tuple in a disk page
@@ -31,6 +32,11 @@
  * to the tuple is dropped; while for a palloc'd tuple we usually want the
  * tuple pfree'd when the TupleTableSlot's reference is dropped.
  *
+ * A "minimal" tuple is handled similarly to a palloc'd regular tuple.
+ * At present, minimal tuples never are stored in buffers, so there is no
+ * parallel to case 1.  Note that a minimal tuple has no "system columns".
+ * (Actually, it could have an OID, but we have no need to access the OID.)
+ *
  * A "virtual" tuple is an optimization used to minimize physical data
  * copying in a nest of plan nodes.  Any pass-by-reference Datums in the
  * tuple point to storage that is not directly associated with the
@@ -83,6 +89,15 @@
  * tts_values/tts_isnull are allocated when a descriptor is assigned to the
  * slot; they are of length equal to the descriptor's natts.
  *
+ * tts_mintuple must always be NULL if the slot does not hold a "minimal"
+ * tuple.  When it does, tts_mintuple points to the actual MinimalTupleData
+ * object (the thing to be pfree'd if tts_shouldFree is true).  In this case
+ * tts_tuple points at tts_minhdr and the fields of that are set correctly
+ * for access to the minimal tuple; in particular, tts_minhdr.t_data points
+ * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple.  (tts_mintuple is therefore
+ * redundant, but for code simplicity we store it explicitly anyway.)  This
+ * case otherwise behaves identically to the regular-physical-tuple case.
+ *
  * tts_slow/tts_off are saved state for slot_deform_tuple, and should not
  * be touched by any other code.
  *----------
@@ -100,6 +115,8 @@ typedef struct TupleTableSlot
 	int			tts_nvalid;		/* # of valid values in tts_values */
 	Datum	   *tts_values;		/* current per-attribute values */
 	bool	   *tts_isnull;		/* current per-attribute isnull flags */
+	MinimalTuple tts_mintuple;	/* set if it's a minimal tuple, else NULL */
+	HeapTupleData tts_minhdr;	/* workspace if it's a minimal tuple */
 	long		tts_off;		/* saved state for slot_deform_tuple */
 } TupleTableSlot;
 
@@ -133,10 +150,14 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
 			   TupleTableSlot *slot,
 			   Buffer buffer,
 			   bool shouldFree);
+extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
+			   TupleTableSlot *slot,
+			   bool shouldFree);
 extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
 extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
 extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
 extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
 extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
 extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
diff --git a/src/include/utils/tuplestore.h b/src/include/utils/tuplestore.h
index a671bf438f..6227934414 100644
--- a/src/include/utils/tuplestore.h
+++ b/src/include/utils/tuplestore.h
@@ -14,17 +14,23 @@
  * A temporary file is used to handle the data if it exceeds the
  * space limit specified by the caller.
  *
+ * Beginning in Postgres 8.2, what is stored is just MinimalTuples;
+ * callers cannot expect valid system columns in regurgitated tuples.
+ * Also, we have changed the API to return tuples in TupleTableSlots,
+ * so that there is a check to prevent attempted access to system columns.
+ *
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.17 2006/03/05 15:59:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.18 2006/06/27 02:51:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef TUPLESTORE_H
 #define TUPLESTORE_H
 
-#include "access/htup.h"
+#include "executor/tuptable.h"
+
 
 /* Tuplestorestate is an opaque type whose details are not known outside
  * tuplestore.c.
@@ -32,7 +38,7 @@
 typedef struct Tuplestorestate Tuplestorestate;
 
 /*
- * Currently we only need to store HeapTuples, but it would be easy
+ * Currently we only need to store MinimalTuples, but it would be easy
  * to support the same behavior for IndexTuples and/or bare Datums.
  */
 
@@ -40,17 +46,17 @@ extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
 					  bool interXact,
 					  int maxKBytes);
 
-extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
+extern void tuplestore_puttupleslot(Tuplestorestate *state,
+									TupleTableSlot *slot);
+extern void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple);
 
 /* tuplestore_donestoring() used to be required, but is no longer used */
 #define tuplestore_donestoring(state)	((void) 0)
 
 /* backwards scan is only allowed if randomAccess was specified 'true' */
-extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward,
-					bool *should_free);
-
-#define tuplestore_getheaptuple(state, forward, should_free) \
-	((HeapTuple) tuplestore_gettuple(state, forward, should_free))
+extern bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
+									TupleTableSlot *slot);
+extern bool tuplestore_advance(Tuplestorestate *state, bool forward);
 
 extern void tuplestore_end(Tuplestorestate *state);