diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml
index 9658c7b7da0fff4492b4bc0b487a698b3672e22d..49d380e238b1c30583a5c75f7c0ca19d13349111 100644
--- a/doc/src/sgml/ref/declare.sgml
+++ b/doc/src/sgml/ref/declare.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.22 2003/04/06 22:41:52 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.23 2003/04/29 03:21:28 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -70,8 +70,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term>NO SCROLL</term>
       <listitem>
        <para>
-    Specifies that the cursor cannot be used to retrieve rows in a
-    nonsequential fashion (e.g., backward).
+        Specifies that the cursor cannot be used to retrieve rows in a
+        nonsequential fashion (e.g., backward).
        </para>
       </listitem>
      </varlistentry>
@@ -83,7 +83,7 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
 	Specifies that the cursor may be used to retrieve rows in a
 	nonsequential fashion (e.g., backward). Depending upon the
 	complexity of the query's execution plan, specifying
-	<literal>SCROLL</literal> may impose a slight performance penalty
+	<literal>SCROLL</literal> may impose a performance penalty
 	on the query's execution time.
        </para>
       </listitem>
@@ -96,7 +96,7 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
         Specifies that the cursor cannot be used outside of the
         transaction that created it. If neither <literal>WITHOUT
         HOLD</literal> nor <literal>WITH HOLD</literal> is specified,
-        <literal>WITH HOLD</literal> is the default.
+        <literal>WITHOUT HOLD</literal> is the default.
        </para>
       </listitem>
      </varlistentry>
@@ -105,8 +105,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term>WITH HOLD</term>
       <listitem>
        <para>
-        Specifies that the cursor may be used after the transaction
-        that creates it successfully commits.
+        Specifies that the cursor may continue to be used after the
+	transaction that creates it successfully commits.
        </para>
       </listitem>
      </varlistentry>
@@ -163,7 +163,7 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
 
    <para>
     The <literal>BINARY</literal>, <literal>INSENSITIVE</literal>,
-    <literal>SCROLL</literal> keywords may appear in any order.
+    and <literal>SCROLL</literal> keywords may appear in any order.
    </para>
   </refsect2>
 
@@ -296,11 +296,14 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    <para>
     If <literal>WITH HOLD</literal> is specified and the transaction
     that created the cursor successfully commits, the cursor can be
-    accessed outside the creating transaction. If the creating
-    transaction is aborted, the cursor is removed. A cursor created
+    continue to be accessed by subsequent transactions in the same session.
+    (But if the creating
+    transaction is aborted, the cursor is removed.) A cursor created
     with <literal>WITH HOLD</literal> is closed when an explicit
-    <command>CLOSE</command> command is issued on it, or the client
-    connection is terminated.
+    <command>CLOSE</command> command is issued on it, or when the client
+    connection is terminated.  In the current implementation, the rows
+    represented by a held cursor are copied into a temporary file or
+    memory area so that they remain available for subsequent transactions.
    </para>
 
    <para>
@@ -312,7 +315,8 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
     plan is simple enough that no extra overhead is needed to support
     it. However, application developers are advised not to rely on
     using backward fetches from a cursor that has not been created
-    with <literal>SCROLL</literal>.
+    with <literal>SCROLL</literal>.  If <literal>NO SCROLL</> is specified,
+    then backward fetches are disallowed in any case.
    </para>
 
    <para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 8c1f0bbf639e321be775bcdf8d6db8367e248147..33c80918f5868680c3baf0232c420eac31022acd 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -95,7 +95,7 @@ E121	Basic cursor support	06	Positioned UPDATE statement	NO
 E121	Basic cursor support	07	Positioned DELETE statement	NO	
 E121	Basic cursor support	08	CLOSE statement	YES	
 E121	Basic cursor support	10	FETCH statement implicit NEXT	YES	
-E121	Basic cursor support	17	WITH HOLD cursors	NO	
+E121	Basic cursor support	17	WITH HOLD cursors	YES	
 E131	Null value support (nulls in lieu of values)			YES	
 E141	Basic integrity constraints			YES	
 E141	Basic integrity constraints	01	NOT NULL constraints	YES	
@@ -214,12 +214,12 @@ F401	Extended joined table	03	UNION JOIN	YES
 F401	Extended joined table	04	CROSS JOIN	YES	
 F411	Time zone specification			YES	
 F421	National character			YES	
-F431	Read-only scrollable cursors			NO	
+F431	Read-only scrollable cursors			YES	
 F431	Read-only scrollable cursors	01	FETCH with explicit NEXT	YES	
-F431	Read-only scrollable cursors	02	FETCH FIRST	NO	
-F431	Read-only scrollable cursors	03	FETCH LAST	NO	
+F431	Read-only scrollable cursors	02	FETCH FIRST	YES	
+F431	Read-only scrollable cursors	03	FETCH LAST	YES	
 F431	Read-only scrollable cursors	04	FETCH PRIOR	YES	
-F431	Read-only scrollable cursors	05	FETCH ABSOLUTE	NO	
+F431	Read-only scrollable cursors	05	FETCH ABSOLUTE	YES	
 F431	Read-only scrollable cursors	06	FETCH RELATIVE	YES	
 F441	Extended set function support			YES	
 F451	Character set definition			NO	
@@ -319,7 +319,7 @@ T211	Basic trigger capability	04	FOR EACH ROW triggers	YES
 T211	Basic trigger capability	05	Ability to specify a search condition that must be true before the trigger is invoked	NO	
 T211	Basic trigger capability	06	Support for run-time rules for the interaction of triggers and constraints	NO	
 T211	Basic trigger capability	07	TRIGGER privilege	YES	
-T211	Basic trigger capability	08	Multiple triggers for the same the event are executed in the order in which they were created	NO	
+T211	Basic trigger capability	08	Multiple triggers for the same event are executed in the order in which they were created	NO	intentionally omitted
 T212	Enhanced trigger capability			YES	
 T231	SENSITIVE cursors			YES	
 T241	START TRANSACTION statement			YES	
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 7eabc58d49599c55cef10035f068cf0c567027f6..9dc7583f06c70bbaa1eab90e4895cbcacefcdef4 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.11 2003/03/27 16:51:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.12 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,14 +24,12 @@
 #include "rewrite/rewriteHandler.h"
 #include "utils/memutils.h"
 
+
 static long DoRelativeFetch(Portal portal,
 							bool forward,
 							long count,
 							CommandDest dest);
-static long DoRelativeStoreFetch(Portal portal,
-								 bool forward,
-								 long count,
-								 CommandDest dest);
+static uint32 RunFromStore(Portal portal, ScanDirection direction, long count);
 static void DoPortalRewind(Portal portal);
 static Portal PreparePortal(DeclareCursorStmt *stmt);
 
@@ -52,11 +50,9 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
 	QueryDesc  *queryDesc;
 
 	/*
-	 * If this is a non-holdable cursor, we ensure that this statement
+	 * If this is a non-holdable cursor, we require that this statement
 	 * has been executed inside a transaction block (or else, it would
 	 * have no user-visible effect).
-	 *
-	 * XXX: surely there is a better way to check this?
 	 */
 	if (!(stmt->options & CURSOR_OPT_HOLD))
 		RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
@@ -106,7 +102,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
 	ExecutorStart(queryDesc);
 
 	/* Arrange to shut down the executor if portal is dropped */
-	PortalSetQuery(portal, queryDesc, PortalCleanup);
+	PortalSetQuery(portal, queryDesc);
 
 	/*
 	 * We're done; the query won't actually be run until PerformPortalFetch
@@ -352,15 +348,11 @@ DoRelativeFetch(Portal portal,
 				CommandDest dest)
 {
 	QueryDesc  *queryDesc;
-	EState	   *estate;
-	ScanDirection direction;
 	QueryDesc	temp_queryDesc;
-
-	if (portal->holdStore)
-		return DoRelativeStoreFetch(portal, forward, count, dest);
+	ScanDirection direction;
+	uint32		nprocessed;
 
 	queryDesc = PortalGetQueryDesc(portal);
-	estate = queryDesc->estate;
 
 	/*
 	 * If the requested destination is not the same as the query's
@@ -403,19 +395,26 @@ DoRelativeFetch(Portal portal,
 		if (count == FETCH_ALL)
 			count = 0;
 
-		ExecutorRun(queryDesc, direction, count);
+		if (portal->holdStore)
+			nprocessed = RunFromStore(portal, direction, count);
+		else
+		{
+			Assert(portal->executorRunning);
+			ExecutorRun(queryDesc, direction, count);
+			nprocessed = queryDesc->estate->es_processed;
+		}
 
 		if (direction != NoMovementScanDirection)
 		{
 			long	oldPos;
 
-			if (estate->es_processed > 0)
+			if (nprocessed > 0)
 				portal->atStart = false;	/* OK to go backward now */
 			if (count == 0 ||
-				(unsigned long) estate->es_processed < (unsigned long) count)
+				(unsigned long) nprocessed < (unsigned long) count)
 				portal->atEnd = true;		/* we retrieved 'em all */
 			oldPos = portal->portalPos;
-			portal->portalPos += estate->es_processed;
+			portal->portalPos += nprocessed;
 			/* portalPos doesn't advance when we fall off the end */
 			if (portal->portalPos < oldPos)
 				portal->posOverflow = true;
@@ -436,17 +435,24 @@ DoRelativeFetch(Portal portal,
 		if (count == FETCH_ALL)
 			count = 0;
 
-		ExecutorRun(queryDesc, direction, count);
+		if (portal->holdStore)
+			nprocessed = RunFromStore(portal, direction, count);
+		else
+		{
+			Assert(portal->executorRunning);
+			ExecutorRun(queryDesc, direction, count);
+			nprocessed = queryDesc->estate->es_processed;
+		}
 
 		if (direction != NoMovementScanDirection)
 		{
-			if (estate->es_processed > 0 && portal->atEnd)
+			if (nprocessed > 0 && portal->atEnd)
 			{
 				portal->atEnd = false;		/* OK to go forward now */
 				portal->portalPos++;		/* adjust for endpoint case */
 			}
 			if (count == 0 ||
-				(unsigned long) estate->es_processed < (unsigned long) count)
+				(unsigned long) nprocessed < (unsigned long) count)
 			{
 				portal->atStart = true;		/* we retrieved 'em all */
 				portal->portalPos = 0;
@@ -457,7 +463,7 @@ DoRelativeFetch(Portal portal,
 				long	oldPos;
 
 				oldPos = portal->portalPos;
-				portal->portalPos -= estate->es_processed;
+				portal->portalPos -= nprocessed;
 				if (portal->portalPos > oldPos ||
 					portal->portalPos <= 0)
 					portal->posOverflow = true;
@@ -465,76 +471,75 @@ DoRelativeFetch(Portal portal,
 		}
 	}
 
-	return estate->es_processed;
+	return nprocessed;
 }
 
 /*
- * DoRelativeStoreFetch
- *		Do fetch for a simple N-rows-forward-or-backward case, getting
- *		the results from the portal's tuple store.
+ * RunFromStore
+ *		Fetch tuples from the portal's tuple store.
+ *
+ * Calling conventions are similar to ExecutorRun, except that we
+ * do not depend on having an estate, and therefore return the number
+ * of tuples processed as the result, not in estate->es_processed.
+ *
+ * One difference from ExecutorRun is that the destination receiver functions
+ * are run in the caller's memory context (since we have no estate).  Watch
+ * out for memory leaks.
  */
-static long
-DoRelativeStoreFetch(Portal portal,
-					 bool forward,
-					 long count,
-					 CommandDest dest)
+static uint32
+RunFromStore(Portal portal, ScanDirection direction, long count)
 {
+	QueryDesc *queryDesc = PortalGetQueryDesc(portal);
 	DestReceiver *destfunc;
-	QueryDesc *queryDesc = portal->queryDesc;
-	long rows_fetched = 0;
+	long		current_tuple_count = 0;
 
-	if (!forward && portal->scrollType == DISABLE_SCROLL)
-			elog(ERROR, "Cursor can only scan forward"
-				 "\n\tDeclare it with SCROLL option to enable backward scan");
-
-	destfunc = DestToFunction(dest);
+	destfunc = DestToFunction(queryDesc->dest);
 	(*destfunc->setup) (destfunc, queryDesc->operation,
-						portal->name, queryDesc->tupDesc);
+						queryDesc->portalName, queryDesc->tupDesc);
 
-	for (;;)
+	if (direction == NoMovementScanDirection)
 	{
-		HeapTuple tup;
-		bool should_free;
+		/* do nothing except start/stop the destination */
+	}
+	else
+	{
+		bool	forward = (direction == ForwardScanDirection);
 
-		if (rows_fetched >= count)
-			break;
-		if (portal->atEnd && forward)
-			break;
-		if (portal->atStart && !forward)
-			break;
+		for (;;)
+		{
+			MemoryContext oldcontext;
+			HeapTuple tup;
+			bool should_free;
 
-		tup = tuplestore_getheaptuple(portal->holdStore, forward, &should_free);
+			oldcontext = MemoryContextSwitchTo(portal->holdContext);
 
-		if (tup == NULL)
-		{
-			if (forward)
-				portal->atEnd = true;
-			else
-				portal->atStart = true;
+			tup = tuplestore_getheaptuple(portal->holdStore, forward,
+										  &should_free);
 
-			break;
-		}
+			MemoryContextSwitchTo(oldcontext);
 
-		(*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
+			if (tup == NULL)
+				break;
 
-		rows_fetched++;
-		if (forward)
-			portal->portalPos++;
-		else
-			portal->portalPos--;
+			(*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
 
-		if (forward && portal->atStart)
-			portal->atStart = false;
-		if (!forward && portal->atEnd)
-			portal->atEnd = false;
+			if (should_free)
+				pfree(tup);
 
-		if (should_free)
-			pfree(tup);
+			/*
+			 * check our tuple count.. if we've processed the proper number
+			 * then quit, else loop again and process more tuples.  Zero
+			 * count means no limit.
+			 */
+			current_tuple_count++;
+			if (count && count == current_tuple_count)
+				break;
+		}
 	}
 
 	(*destfunc->cleanup) (destfunc);
 
-	return rows_fetched;
+	return (uint32) current_tuple_count;
 }
 
 /*
@@ -544,9 +549,17 @@ static void
 DoPortalRewind(Portal portal)
 {
 	if (portal->holdStore)
+	{
+		MemoryContext oldcontext;
+
+		oldcontext = MemoryContextSwitchTo(portal->holdContext);
 		tuplestore_rescan(portal->holdStore);
-	else
+		MemoryContextSwitchTo(oldcontext);
+	}
+	if (portal->executorRunning)
+	{
 		ExecutorRewind(PortalGetQueryDesc(portal));
+	}
 
 	portal->atStart = true;
 	portal->atEnd = false;
@@ -630,12 +643,11 @@ PreparePortal(DeclareCursorStmt *stmt)
 /*
  * PortalCleanup
  *
- * Clean up a portal when it's dropped.  Since this mainly exists to run
- * ExecutorEnd(), it should not be set as the cleanup hook until we have
- * called ExecutorStart() on the portal's query.
+ * Clean up a portal when it's dropped.  This is the standard cleanup hook
+ * for portals.
  */
 void
-PortalCleanup(Portal portal)
+PortalCleanup(Portal portal, bool isError)
 {
 	/*
 	 * sanity checks
@@ -643,11 +655,31 @@ PortalCleanup(Portal portal)
 	AssertArg(PortalIsValid(portal));
 	AssertArg(portal->cleanup == PortalCleanup);
 
+	/*
+	 * Delete tuplestore if present.  (Note: portalmem.c is responsible
+	 * for removing holdContext.)  We should do this even under error
+	 * conditions.
+	 */
 	if (portal->holdStore)
-		tuplestore_end(portal->holdStore);
-	else
-		ExecutorEnd(PortalGetQueryDesc(portal));
+	{
+		MemoryContext oldcontext;
 
+		oldcontext = MemoryContextSwitchTo(portal->holdContext);
+		tuplestore_end(portal->holdStore);
+		MemoryContextSwitchTo(oldcontext);
+		portal->holdStore = NULL;
+	}
+	/*
+	 * Shut down executor, if still running.  We skip this during error
+	 * abort, since other mechanisms will take care of releasing executor
+	 * resources, and we can't be sure that ExecutorEnd itself wouldn't fail.
+	 */
+	if (portal->executorRunning)
+	{
+		portal->executorRunning = false;
+		if (!isError)
+			ExecutorEnd(PortalGetQueryDesc(portal));
+	}
 }
 
 /*
@@ -661,8 +693,10 @@ PortalCleanup(Portal portal)
 void
 PersistHoldablePortal(Portal portal)
 {
-	MemoryContext oldcxt;
 	QueryDesc *queryDesc = PortalGetQueryDesc(portal);
+	MemoryContext oldcxt;
+	CommandDest olddest;
+	TupleDesc	tupdesc;
 
 	/*
 	 * If we're preserving a holdable portal, we had better be
@@ -672,17 +706,15 @@ PersistHoldablePortal(Portal portal)
 	Assert(portal->holdStore == NULL);
 
 	/*
-	 * This context is used to store portal data that needs to persist
-	 * between transactions.
+	 * This context is used to store the tuple set.
+	 * Caller must have created it already.
 	 */
+	Assert(portal->holdContext != NULL);
 	oldcxt = MemoryContextSwitchTo(portal->holdContext);
 
 	/* XXX: Should SortMem be used for this? */
 	portal->holdStore = tuplestore_begin_heap(true, true, SortMem);
 
-	/* Set the destination to output to the tuplestore */
-	queryDesc->dest = Tuplestore;
-
 	/*
 	 * Rewind the executor: we need to store the entire result set in
 	 * the tuplestore, so that subsequent backward FETCHs can be
@@ -690,8 +722,29 @@ PersistHoldablePortal(Portal portal)
 	 */
 	ExecutorRewind(queryDesc);
 
+	/* Set the destination to output to the tuplestore */
+	olddest = queryDesc->dest;
+	queryDesc->dest = Tuplestore;
+
 	/* Fetch the result set into the tuplestore */
-	ExecutorRun(queryDesc, ForwardScanDirection, 0);
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	queryDesc->dest = olddest;
+
+	/*
+	 * Before closing down the executor, we must copy the tupdesc, since
+	 * it was created in executor memory.
+	 */
+	tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
+
+	/*
+	 * Now shut down the inner executor.
+	 */
+	portal->executorRunning = false;
+	ExecutorEnd(queryDesc);
+
+	/* ExecutorEnd clears this, so must wait to save copied pointer */
+	queryDesc->tupDesc = tupdesc;
 
 	/*
 	 * Reset the position in the result set: ideally, this could be
@@ -702,69 +755,26 @@ PersistHoldablePortal(Portal portal)
 	 */
 	if (!portal->atEnd)
 	{
-		int store_pos = 0;
-		bool should_free;
+		long	store_pos;
 
 		tuplestore_rescan(portal->holdStore);
 
-		while (store_pos < portal->portalPos)
+		for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
 		{
-			HeapTuple tmp = tuplestore_gettuple(portal->holdStore,
-												true, &should_free);
+			HeapTuple tup;
+			bool should_free;
 
-			if (tmp == NULL)
+			tup = tuplestore_gettuple(portal->holdStore, true,
+									  &should_free);
+
+			if (tup == NULL)
 				elog(ERROR,
 					 "PersistHoldablePortal: unexpected end of tuple stream");
 
-			store_pos++;
-
-			/*
-			 * This could probably be optimized by creating and then
-			 * deleting a separate memory context for this series of
-			 * operations.
-			 */
 			if (should_free)
-				pfree(tmp);
+				pfree(tup);
 		}
 	}
 
-	/*
-	 * The current Portal structure contains some data that will be
-	 * needed by the holdable cursor, but it has been allocated in a
-	 * memory context that is not sufficiently long-lived: we need to
-	 * copy it into the portal's long-term memory context.
-	 */
-	{
-		TupleDesc tupDescCopy;
-		QueryDesc *queryDescCopy;
-
-		/*
-		 * We need to use this order as ExecutorEnd invalidates the
-		 * queryDesc's tuple descriptor
-		 */
-		tupDescCopy = CreateTupleDescCopy(queryDesc->tupDesc);
-
-		ExecutorEnd(queryDesc);
-
-		queryDescCopy = palloc(sizeof(*queryDescCopy));
-
-		/*
-		 * This doesn't copy all the dependant data in the QueryDesc,
-		 * but that's okay -- the only complex field we need to keep is
-		 * the query's tupledesc, which we've copied ourselves.
-		 */
-		memcpy(queryDescCopy, queryDesc, sizeof(*queryDesc));
-
-		FreeQueryDesc(queryDesc);
-
-		queryDescCopy->tupDesc = tupDescCopy;
-		portal->queryDesc = queryDescCopy;
-	}
-
-	/*
-	 * We no longer need the portal's short-term memory context.
-	 */
-	MemoryContextDelete(PortalGetHeapMemory(portal));
-
-	PortalGetHeapMemory(portal) = NULL;
+	MemoryContextSwitchTo(oldcxt);
 }
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 11ed2914de5f9502d39d278c7f99868394147c68..218b56a601315088f8d339fa44118c5fedca6f15 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.91 2003/04/27 20:09:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.92 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -809,7 +809,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
 	ExecutorStart(queryDesc);
 
 	/* Arrange to shut down the executor if portal is dropped */
-	PortalSetQuery(portal, queryDesc, PortalCleanup);
+	PortalSetQuery(portal, queryDesc);
 
 	/* Switch back to the callers memory context */
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index fe82cc8c92c8c60b54d316fd8723012b91c207f4..01fffed8168168d6b31f9509ba38c411169b132c 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
- *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.1 2003/03/27 16:53:15 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.2 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,8 +27,9 @@ typedef struct
 	MemoryContext		cxt;
 } TStoreState;
 
+
 /*
- * Receive a tuple from the executor and store it in the tuplestore.
+ * Prepare to receive tuples from executor.
  *
  * XXX: As currently implemented, this routine is a hack: there should
  * be no tie between this code and the portal system. Instead, the
@@ -56,6 +57,9 @@ tstoreSetupReceiver(DestReceiver *self, int operation,
 	myState->cxt = portal->holdContext;
 }
 
+/*
+ * Receive a tuple from the executor and store it in the tuplestore.
+ */
 static void
 tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
@@ -67,12 +71,18 @@ tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 	MemoryContextSwitchTo(oldcxt);
 }
 
+/*
+ * Clean up
+ */
 static void
 tstoreCleanupReceiver(DestReceiver *self)
 {
-	; /* do nothing */
+	/* do nothing */
 }
 
+/*
+ * Initially create a DestReceiver object.
+ */
 DestReceiver *
 tstoreReceiverCreateDR(void)
 {
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 64cd5603acdc549351d6896b69b5289bc025cf7a..5dc3d82bea8f695dfb01818356f02d0355854024 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.266 2003/03/27 16:51:28 momjian Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.267 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2299,7 +2299,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 	 */
 	if ((stmt->options & CURSOR_OPT_SCROLL) &&
 		(stmt->options & CURSOR_OPT_NO_SCROLL))
-		elog(ERROR, "Both SCROLL and NO SCROLL cannot be specified.");
+		elog(ERROR, "Cannot specify both SCROLL and NO SCROLL");
 
 	stmt->query = (Node *) transformStmt(pstate, stmt->query,
 										 &extras_before, &extras_after);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5e98788765806f58e4306b9516c5adf6c34615b7..bd67dae811f720193c4dd6264f8b131011880015 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.411 2003/04/08 23:20:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.412 2003/04/29 03:21:29 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -4236,10 +4236,8 @@ DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
 					n->portalname = $2;
 					n->options = $3;
 					n->query = $7;
-
 					if ($5)
 						n->options |= CURSOR_OPT_HOLD;
-
 					$$ = (Node *)n;
 				}
 		;
@@ -7191,6 +7189,7 @@ unreserved_keyword:
 			| FUNCTION
 			| GLOBAL
 			| HANDLER
+			| HOLD
 			| HOUR_P
 			| IMMEDIATE
 			| IMMUTABLE
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 90b185cd5dec24169b6012980b5acccafe5443e2..e479bfe22b78fee19623e2fa5be87b03479a13d1 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.15 2003/03/27 16:51:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.16 2003/04/29 03:21:29 tgl Exp $
  *
  * NOTES:
  *
@@ -64,7 +64,7 @@ struct BufFile
 	 */
 
 	bool		isTemp;			/* can only add files if this is TRUE */
-	bool		isInterTxn;		/* keep open over transactions? */
+	bool		isInterXact;	/* keep open over transactions? */
 	bool		dirty;			/* does buffer need to be written? */
 
 	/*
@@ -119,7 +119,7 @@ extendBufFile(BufFile *file)
 	File		pfile;
 
 	Assert(file->isTemp);
-	pfile = OpenTemporaryFile(file->isInterTxn);
+	pfile = OpenTemporaryFile(file->isInterXact);
 	Assert(pfile >= 0);
 
 	file->files = (File *) repalloc(file->files,
@@ -135,19 +135,22 @@ extendBufFile(BufFile *file)
  * Create a BufFile for a new temporary file (which will expand to become
  * multiple temporary files if more than MAX_PHYSICAL_FILESIZE bytes are
  * written to it).
+ *
+ * Note: if interXact is true, the caller had better be calling us in a
+ * memory context that will survive across transaction boundaries.
  */
 BufFile *
-BufFileCreateTemp(bool interTxn)
+BufFileCreateTemp(bool interXact)
 {
 	BufFile    *file;
 	File		pfile;
 
-	pfile = OpenTemporaryFile(interTxn);
+	pfile = OpenTemporaryFile(interXact);
 	Assert(pfile >= 0);
 
 	file = makeBufFile(pfile);
 	file->isTemp = true;
-	file->isInterTxn = interTxn;
+	file->isInterXact = interXact;
 
 	return file;
 }
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index b84e2cb82d1c41a388cbb7c7ea5be7c944ac675e..5552a043399321faf2809e4894558cb65e1da81f 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.97 2003/04/04 20:42:12 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.98 2003/04/29 03:21:29 tgl Exp $
  *
  * NOTES:
  *
@@ -113,8 +113,8 @@ int			max_files_per_process = 1000;
 #define FileUnknownPos (-1L)
 
 /* these are the assigned bits in fdstate below: */
-#define FD_TEMPORARY		(1 << 0)
-#define FD_TXN_TEMPORARY	(1 << 1)
+#define FD_TEMPORARY		(1 << 0)		/* T = delete when closed */
+#define FD_XACT_TEMPORARY	(1 << 1)		/* T = delete at eoXact */
 
 typedef struct vfd
 {
@@ -156,7 +156,7 @@ static int	numAllocatedFiles = 0;
 static FILE *allocatedFiles[MAX_ALLOCATED_FILES];
 
 /*
- * Number of temporary files opened during the current transaction;
+ * Number of temporary files opened during the current session;
  * this is used in generation of tempfile names.
  */
 static long tempFileCounter = 0;
@@ -205,6 +205,9 @@ static int	FileAccess(File file);
 static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
 static char *filepath(const char *filename);
 static long pg_nofile(void);
+static void AtProcExit_Files(void);
+static void CleanupTempFiles(bool isProcExit);
+
 
 /*
  * pg_fsync --- same as fsync except does nothing if enableFsync is off
@@ -522,7 +525,7 @@ AllocateVfd(void)
 		 * register proc-exit call to ensure temp files are dropped at
 		 * exit
 		 */
-		on_proc_exit(AtEOXact_Files, 0);
+		on_proc_exit(AtProcExit_Files, 0);
 	}
 
 	if (VfdCache[0].nextFree == 0)
@@ -751,21 +754,21 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
  * There's no need to pass in fileFlags or fileMode either, since only
  * one setting makes any sense for a temp file.
  *
- * keepOverTxn: if true, don't close the file at end-of-transaction. In
+ * interXact: if true, don't close the file at end-of-transaction. In
  * most cases, you don't want temporary files to outlive the transaction
  * that created them, so this should be false -- but if you need
  * "somewhat" temporary storage, this might be useful. In either case,
- * the file is removed when the File is explicitely closed.
+ * the file is removed when the File is explicitly closed.
  */
 File
-OpenTemporaryFile(bool keepOverTxn)
+OpenTemporaryFile(bool interXact)
 {
-	char		tempfilepath[128];
+	char		tempfilepath[MAXPGPATH];
 	File		file;
 
 	/*
-	 * Generate a tempfile name that's unique within the current
-	 * transaction and database instance.
+	 * Generate a tempfile name that should be unique within the current
+	 * database instance.
 	 */
 	snprintf(tempfilepath, sizeof(tempfilepath),
 			 "%s/%s%d.%ld", PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
@@ -798,15 +801,16 @@ OpenTemporaryFile(bool keepOverTxn)
 								O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
 								0600);
 		if (file <= 0)
-			elog(ERROR, "Failed to create temporary file %s", tempfilepath);
+			elog(ERROR, "Failed to create temporary file %s: %m",
+				 tempfilepath);
 	}
 
 	/* Mark it for deletion at close */
 	VfdCache[file].fdstate |= FD_TEMPORARY;
 
 	/* Mark it for deletion at EOXact */
-	if (!keepOverTxn)
-		VfdCache[file].fdstate |= FD_TXN_TEMPORARY;
+	if (!interXact)
+		VfdCache[file].fdstate |= FD_XACT_TEMPORARY;
 
 	return file;
 }
@@ -1108,44 +1112,79 @@ closeAllVfds(void)
 /*
  * AtEOXact_Files
  *
- * This routine is called during transaction commit or abort or backend
- * exit (it doesn't particularly care which).  All still-open temporary-file
- * VFDs are closed, which also causes the underlying files to be deleted.
- * Furthermore, all "allocated" stdio files are closed.
+ * This routine is called during transaction commit or abort (it doesn't
+ * particularly care which).  All still-open per-transaction temporary file
+ * VFDs are closed, which also causes the underlying files to be
+ * deleted. Furthermore, all "allocated" stdio files are closed.
  */
 void
 AtEOXact_Files(void)
 {
-	Index		i;
+	CleanupTempFiles(false);
+}
+
+/*
+ * AtProcExit_Files
+ *
+ * on_proc_exit hook to clean up temp files during backend shutdown.
+ * Here, we want to clean up *all* temp files including interXact ones.
+ */
+static void
+AtProcExit_Files(void)
+{
+	CleanupTempFiles(true);
+}
+
+/*
+ * Close temporary files and delete their underlying files.
+ *
+ * isProcExit: if true, this is being called as the backend process is
+ * exiting. If that's the case, we should remove all temporary files; if
+ * that's not the case, we are being called for transaction commit/abort
+ * and should only remove transaction-local temp files.  In either case,
+ * also clean up "allocated" stdio files.
+ */
+static void
+CleanupTempFiles(bool isProcExit)
+{
+	Index i;
 
 	if (SizeVfdCache > 0)
 	{
 		Assert(FileIsNotOpen(0));		/* Make sure ring not corrupted */
 		for (i = 1; i < SizeVfdCache; i++)
 		{
-			if ((VfdCache[i].fdstate & FD_TEMPORARY) &&
-				(VfdCache[i].fdstate & FD_TXN_TEMPORARY) &&
-				VfdCache[i].fileName != NULL)
-				FileClose(i);
+			unsigned short fdstate = VfdCache[i].fdstate;
+
+			if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL)
+			{
+				/*
+				 * If we're in the process of exiting a backend process,
+				 * close all temporary files. Otherwise, only close
+				 * temporary files local to the current transaction.
+				 */
+				if (isProcExit || (fdstate & FD_XACT_TEMPORARY))
+					FileClose(i);
+			}
 		}
 	}
 
 	while (numAllocatedFiles > 0)
 		FreeFile(allocatedFiles[0]);
-
-	/*
-	 * Reset the tempfile name counter to 0; not really necessary, but
-	 * helps keep the names from growing unreasonably long.
-	 */
-	tempFileCounter = 0;
 }
 
 
 /*
- * Remove old temporary files
+ * Remove temporary files left over from a prior postmaster session
  *
  * This should be called during postmaster startup.  It will forcibly
  * remove any leftover files created by OpenTemporaryFile.
+ *
+ * NOTE: we could, but don't, call this during a post-backend-crash restart
+ * cycle.  The argument for not doing it is that someone might want to examine
+ * the temp files for debugging purposes.  This does however mean that
+ * OpenTemporaryFile had better allow for collision with an existing temp
+ * file name.
  */
 void
 RemovePgTempFiles(void)
@@ -1194,15 +1233,9 @@ RemovePgTempFiles(void)
 								strlen(PG_TEMP_FILE_PREFIX)) == 0)
 						unlink(rm_path);
 					else
-					{
-						/*
-						 * would prefer to use elog here, but it's not up
-						 * and running during postmaster startup...
-						 */
-						fprintf(stderr,
-								"Unexpected file found in temporary-files directory: %s\n",
-								rm_path);
-					}
+						elog(LOG,
+							 "Unexpected file found in temporary-files directory: %s",
+							 rm_path);
 				}
 				closedir(temp_dir);
 			}
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index e6486d4b16dcc89957f6b437b3e0f4b3944712e2..0b093b2613c492213f64d7d8137cbdb566145deb 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.54 2003/03/27 16:51:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.55 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,7 @@
 
 #include "postgres.h"
 
+#include "commands/portalcmds.h"
 #include "executor/executor.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -156,14 +157,14 @@ GetPortalByName(const char *name)
 
 /*
  * PortalSetQuery
- *		Attaches a "query" to the specified portal. Note that in the
- *		case of DECLARE CURSOR, some Portal options have already been
- *		set based upon the parsetree of the original DECLARE statement.
+ *		Attaches a QueryDesc to the specified portal.  This should be
+ *		called only after successfully doing ExecutorStart for the query.
+ *
+ * Note that in the case of DECLARE CURSOR, some Portal options have
+ * already been set in portalcmds.c's PreparePortal().  This is grotty.
  */
 void
-PortalSetQuery(Portal portal,
-			   QueryDesc *queryDesc,
-			   void (*cleanup) (Portal portal))
+PortalSetQuery(Portal portal, QueryDesc *queryDesc)
 {
 	AssertArg(PortalIsValid(portal));
 
@@ -174,18 +175,15 @@ PortalSetQuery(Portal portal,
 	 */
 	if (portal->scrollType == DEFAULT_SCROLL)
 	{
-		bool backwardPlan;
-
-		backwardPlan = ExecSupportsBackwardScan(queryDesc->plantree);
-
-		if (backwardPlan)
+		if (ExecSupportsBackwardScan(queryDesc->plantree))
 			portal->scrollType = ENABLE_SCROLL;
 		else
 			portal->scrollType = DISABLE_SCROLL;
 	}
 
 	portal->queryDesc = queryDesc;
-	portal->cleanup = cleanup;
+	portal->executorRunning = true;	/* now need to shut down executor */
+
 	portal->atStart = true;
 	portal->atEnd = false;		/* allow fetches */
 	portal->portalPos = 0;
@@ -228,12 +226,13 @@ CreatePortal(const char *name)
 
 	/* initialize portal query */
 	portal->queryDesc = NULL;
-	portal->cleanup = NULL;
+	portal->cleanup = PortalCleanup;
 	portal->scrollType = DEFAULT_SCROLL;
+	portal->executorRunning = false;
 	portal->holdOpen = false;
+	portal->createXact = GetCurrentTransactionId();
 	portal->holdStore = NULL;
 	portal->holdContext = NULL;
-	portal->createXact = GetCurrentTransactionId();
 	portal->atStart = true;
 	portal->atEnd = true;		/* disallow fetches until query is set */
 	portal->portalPos = 0;
@@ -249,67 +248,34 @@ CreatePortal(const char *name)
  * PortalDrop
  *		Destroy the portal.
  *
- *		keepHoldable: if true, holdable portals should not be removed by
- *		this function. More specifically, invoking this function with
- *		keepHoldable = true on a holdable portal prepares the portal for
- *		access outside of its creating transaction.
+ *		isError: if true, we are destroying portals at the end of a failed
+ *		transaction.  (This causes PortalCleanup to skip unneeded steps.)
  */
 void
-PortalDrop(Portal portal, bool persistHoldable)
+PortalDrop(Portal portal, bool isError)
 {
 	AssertArg(PortalIsValid(portal));
 
-	if (portal->holdOpen && persistHoldable)
-	{
-		/*
-		 * We're "dropping" a holdable portal, but what we really need
-		 * to do is prepare the portal for access outside of its
-		 * creating transaction.
-		 */
-
-		/*
-		 * Create the memory context that is used for storage of
-		 * long-term (cross transaction) data needed by the holdable
-		 * portal.
-		 */
-		portal->holdContext =
-			AllocSetContextCreate(PortalMemory,
-								  "PortalHeapMemory",
-								  ALLOCSET_DEFAULT_MINSIZE,
-								  ALLOCSET_DEFAULT_INITSIZE,
-								  ALLOCSET_DEFAULT_MAXSIZE);
-
-		/*
-		 * Note that PersistHoldablePortal() releases any resources used
-		 * by the portal that are local to the creating txn.
-		 */
-		PersistHoldablePortal(portal);
-
-		return;
-	}
-
-	/* remove portal from hash table */
+	/*
+	 * Remove portal from hash table.  Because we do this first, we will
+	 * not come back to try to remove the portal again if there's any error
+	 * in the subsequent steps.  Better to leak a little memory than to get
+	 * into an infinite error-recovery loop.
+	 */
 	PortalHashTableDelete(portal);
 
-	/* reset portal */
+	/* let portalcmds.c clean up the state it knows about */
 	if (PointerIsValid(portal->cleanup))
-		(*portal->cleanup) (portal);
+		(*portal->cleanup) (portal, isError);
 
-	/*
-	 * delete short-term memory context; in the case of a holdable
-	 * portal, this has already been done
-	 */
-	if (PortalGetHeapMemory(portal))
-		MemoryContextDelete(PortalGetHeapMemory(portal));
-
-	/*
-	 * delete long-term memory context; in the case of a non-holdable
-	 * portal, this context has never been created, so we don't need to
-	 * do anything
-	 */
+	/* delete tuplestore storage, if any */
 	if (portal->holdContext)
 		MemoryContextDelete(portal->holdContext);
 
+	/* release subsidiary storage */
+	if (PortalGetHeapMemory(portal))
+		MemoryContextDelete(PortalGetHeapMemory(portal));
+
 	/* release name and portal data (both are in PortalMemory) */
 	pfree(portal->name);
 	pfree(portal);
@@ -320,8 +286,8 @@ PortalDrop(Portal portal, bool persistHoldable)
  * transaction was aborted, all the portals created in this transaction
  * should be removed. If the transaction was successfully committed, any
  * holdable cursors created in this transaction need to be kept
- * open. Only cursors created in the current transaction should be
- * removed in this fashion.
+ * open. In any case, portals remaining from prior transactions should
+ * be left untouched.
  *
  * XXX This assumes that portals can be deleted in a random order, ie,
  * no portal has a reference to any other (at least not one that will be
@@ -340,7 +306,42 @@ AtEOXact_portals(bool isCommit)
 
 	while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
 	{
-		if (hentry->portal->createXact == xact)
-			PortalDrop(hentry->portal, isCommit);
+		Portal portal = hentry->portal;
+
+		if (portal->createXact != xact)
+			continue;
+
+		if (portal->holdOpen && isCommit)
+		{
+			/*
+			 * We are exiting the transaction that created a holdable
+			 * cursor.  Instead of dropping the portal, prepare it for
+			 * access by later transactions.
+			 */
+
+			/*
+			 * Create the memory context that is used for storage of
+			 * the held cursor's tuple set.
+			 */
+			portal->holdContext =
+				AllocSetContextCreate(PortalMemory,
+									  "PortalHeapMemory",
+									  ALLOCSET_DEFAULT_MINSIZE,
+									  ALLOCSET_DEFAULT_INITSIZE,
+									  ALLOCSET_DEFAULT_MAXSIZE);
+
+			/*
+			 * Transfer data into the held tuplestore.
+			 *
+			 * Note that PersistHoldablePortal() must release all
+			 * resources used by the portal that are local to the creating
+			 * transaction.
+			 */
+			PersistHoldablePortal(portal);
+		}
+		else
+		{
+			PortalDrop(portal, !isCommit);
+		}
 	}
 }
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 89970b2495b3fedfd28fd8fa1b3971fdaac363ce..b67090abad78fe39e8c3b29eef2a6518627e863e 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
- *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.12 2003/03/27 16:51:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.13 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,7 +65,7 @@ struct Tuplestorestate
 {
 	TupStoreStatus status;		/* enumerated value as shown above */
 	bool		randomAccess;	/* did caller request random access? */
-	bool		interTxn;		/* keep open through transactions? */
+	bool		interXact;		/* keep open through transactions? */
 	long		availMem;		/* remaining memory available, in bytes */
 	BufFile    *myfile;			/* underlying file, or NULL if none */
 
@@ -191,7 +191,7 @@ struct Tuplestorestate
 
 
 static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
-												bool interTxn,
+												bool interXact,
 												int maxKBytes);
 static void dumptuples(Tuplestorestate *state);
 static unsigned int getlen(Tuplestorestate *state, bool eofOK);
@@ -205,9 +205,8 @@ static void *readtup_heap(Tuplestorestate *state, unsigned int len);
  *
  * Initialize for a tuple store operation.
  */
-
 static Tuplestorestate *
-tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
+tuplestore_begin_common(bool randomAccess, bool interXact, int maxKBytes)
 {
 	Tuplestorestate *state;
 
@@ -215,7 +214,7 @@ tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
 
 	state->status = TSS_INMEM;
 	state->randomAccess = randomAccess;
-	state->interTxn = interTxn;
+	state->interXact = interXact;
 	state->availMem = maxKBytes * 1024L;
 	state->myfile = NULL;
 
@@ -244,17 +243,21 @@ tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
  * randomAccess: if true, both forward and backward accesses to the
  * tuple store are allowed.
  *
- * interTxn: if true, the files used by on-disk storage persist beyond
- * the end of the current transaction.
+ * interXact: if true, the files used for on-disk storage persist beyond the
+ * end of the current transaction.  NOTE: It's the caller's responsibility to
+ * create such a tuplestore in a memory context that will also survive
+ * transaction boundaries, and to ensure the tuplestore is closed when it's
+ * no longer wanted.
  *
  * maxKBytes: how much data to store in memory (any data beyond this
  * amount is paged to disk).
  */
 Tuplestorestate *
-tuplestore_begin_heap(bool randomAccess, bool interTxn, int maxKBytes)
+tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
 {
 	Tuplestorestate *state = tuplestore_begin_common(randomAccess,
-													 interTxn, maxKBytes);
+													 interXact,
+													 maxKBytes);
 
 	state->copytup = copytup_heap;
 	state->writetup = writetup_heap;
@@ -341,7 +344,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
 			/*
 			 * Nope; time to switch to tape-based operation.
 			 */
-			state->myfile = BufFileCreateTemp(state->interTxn);
+			state->myfile = BufFileCreateTemp(state->interXact);
 			state->status = TSS_WRITEFILE;
 			dumptuples(state);
 			break;
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index 74855ddf60545c2d8f29bd03f99c9725ac3bcfff..66bfca2b8b6b81c549ae8ddcfd1b22716d03f8cf 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portalcmds.h,v 1.6 2003/03/11 19:40:23 tgl Exp $
+ * $Id: portalcmds.h,v 1.7 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,6 @@ extern long DoPortalFetch(Portal portal,
 
 extern void PerformPortalClose(char *name);
 
-extern void PortalCleanup(Portal portal);
+extern void PortalCleanup(Portal portal, bool isError);
 
 #endif   /* PORTALCMDS_H */
diff --git a/src/include/storage/buffile.h b/src/include/storage/buffile.h
index c456b74d0f6ad4f40d2152f3d31a189420396b49..85f8ff2203e7883cb88d7dce9b5d81de60d04079 100644
--- a/src/include/storage/buffile.h
+++ b/src/include/storage/buffile.h
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: buffile.h,v 1.13 2003/03/27 16:51:29 momjian Exp $
+ * $Id: buffile.h,v 1.14 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@ typedef struct BufFile BufFile;
  * prototypes for functions in buffile.c
  */
 
-extern BufFile *BufFileCreateTemp(bool interTxn);
+extern BufFile *BufFileCreateTemp(bool interXact);
 extern void BufFileClose(BufFile *file);
 extern size_t BufFileRead(BufFile *file, void *ptr, size_t size);
 extern size_t BufFileWrite(BufFile *file, void *ptr, size_t size);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 261e6314e91a9a60b613a6df4032330973ffc93f..0cec593032de751154bf28b4725b18c95383b748 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fd.h,v 1.37 2003/03/27 16:51:29 momjian Exp $
+ * $Id: fd.h,v 1.38 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,7 @@ extern int	max_files_per_process;
 /* Operations on virtual Files --- equivalent to Unix kernel file ops */
 extern File FileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
 extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode);
-extern File OpenTemporaryFile(bool keepOverTxn);
+extern File OpenTemporaryFile(bool interXact);
 extern void FileClose(File file);
 extern void FileUnlink(File file);
 extern int	FileRead(File file, char *buffer, int amount);
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0201b0684c378c2f21402525fb35edaa36ec378b..2615db0490d56d623be7359483c9c82dfdc53c53 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.40 2003/03/27 16:51:29 momjian Exp $
+ * $Id: portal.h,v 1.41 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,14 +48,15 @@ typedef struct PortalData *Portal;
 typedef struct PortalData
 {
 	char	   *name;			/* Portal's name */
-	MemoryContext heap;			/* memory for storing short-term data */
+	MemoryContext heap;			/* subsidiary memory */
 	QueryDesc  *queryDesc;		/* Info about query associated with portal */
-	void		(*cleanup) (Portal);	/* Cleanup routine (optional) */
+	void		(*cleanup) (Portal portal, bool isError);	/* Cleanup hook */
 	ScrollType	scrollType;		/* Allow backward fetches? */
-	bool		holdOpen;		/* hold open after txn ends? */
-	TransactionId createXact;	/* the xid of the creating txn */
+	bool		executorRunning;	/* T if we need to call ExecutorEnd */
+	bool		holdOpen;		/* hold open after xact ends? */
+	TransactionId createXact;	/* the xid of the creating xact */
 	Tuplestorestate *holdStore;	/* store for holdable cursors */
-	MemoryContext holdContext;  /* memory for long-term data */
+	MemoryContext holdContext;  /* memory containing holdStore */
 
 	/*
 	 * atStart, atEnd and portalPos indicate the current cursor position.
@@ -88,10 +89,9 @@ typedef struct PortalData
 extern void EnablePortalManager(void);
 extern void AtEOXact_portals(bool isCommit);
 extern Portal CreatePortal(const char *name);
-extern void PortalDrop(Portal portal, bool persistHoldable);
+extern void PortalDrop(Portal portal, bool isError);
 extern Portal GetPortalByName(const char *name);
-extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
-						   void (*cleanup) (Portal portal));
+extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc);
 extern void PersistHoldablePortal(Portal portal);
 
 #endif   /* PORTAL_H */
diff --git a/src/include/utils/tuplestore.h b/src/include/utils/tuplestore.h
index dbc47ef29d61d87862c8b92850e74e80b5c03771..6a021ba52fd3dd3af293a8c3c35c32778b5b5b89 100644
--- a/src/include/utils/tuplestore.h
+++ b/src/include/utils/tuplestore.h
@@ -17,7 +17,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tuplestore.h,v 1.10 2003/03/27 16:51:29 momjian Exp $
+ * $Id: tuplestore.h,v 1.11 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@ typedef struct Tuplestorestate Tuplestorestate;
  */
 
 extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
-											  bool interTxn,
+											  bool interXact,
 											  int maxKBytes);
 
 extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out
index 7520653924e17213d159707fad2defaed72530d3..1e69cff96963be1c710091870910fa18e3c1fe3c 100644
--- a/src/test/regress/expected/portals.out
+++ b/src/test/regress/expected/portals.out
@@ -675,7 +675,7 @@ CLOSE foo9;
 CLOSE foo10;
 CLOSE foo11;
 CLOSE foo12;
--- is there a reason why we don't close the rest of the open cursors?
+-- leave some cursors open, to test that auto-close works.
 END;
 --
 -- NO SCROLL disallows backward fetching
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index 2df820a30a79c28b39c33f6e11db1bc6e76691a9..807c847bc3c1da72e71f7836d079a44092ef4634 100644
--- a/src/test/regress/sql/portals.sql
+++ b/src/test/regress/sql/portals.sql
@@ -166,7 +166,7 @@ CLOSE foo11;
 
 CLOSE foo12;
 
--- is there a reason why we don't close the rest of the open cursors?
+-- leave some cursors open, to test that auto-close works.
 
 END;
 
@@ -217,4 +217,4 @@ DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
 ROLLBACK;
 
 -- should fail
-FETCH FROM foo26;
\ No newline at end of file
+FETCH FROM foo26;