diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 735e889dbdb31ccf659f37f958d3a086145e9035..81f68119f89fe9c05f57ea5d65d52296aa64ab7a 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -853,7 +853,7 @@ get_crosstab_tuplestore(char *sql,
 	MemoryContext		SPIcontext;
 
 	/* initialize our tuplestore */
-	tupstore = tuplestore_begin_heap(true, SortMem);
+	tupstore = tuplestore_begin_heap(true, false, SortMem);
 
 	/* Connect to SPI manager */
 	if ((ret = SPI_connect()) < 0)
@@ -1124,7 +1124,7 @@ connectby(char *relname,
 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
 
 	/* initialize our tuplestore */
-	tupstore = tuplestore_begin_heap(true, SortMem);
+	tupstore = tuplestore_begin_heap(true, false, SortMem);
 
 	MemoryContextSwitchTo(oldcontext);
 
diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 8aa5b90a9ed8220f9df7032c736a8c229ea01c23..389b455fde73fe7782d8efb495c064f026ca9f1b 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.40 2002/09/21 18:32:54 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.41 2003/03/27 16:51:27 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -133,9 +133,10 @@ COPY <replaceable class="parameter">table</replaceable> [ ( <replaceable class="
        </para>
        <note>
         <para>
-         On a copy in, any data item that matches this string will be stored as
-         a NULL value, so you should make sure that you use the same string
-         as you used on copy out.
+         On a <command>COPY FROM</command>, any data item that matches
+         this string will be stored as a NULL value, so you should
+         make sure that you use the same string as you used with
+         <command>COPY TO</command>.
         </para>
        </note>
       </listitem>
@@ -210,7 +211,8 @@ ERROR: <replaceable>reason</replaceable>
    or write to a file. The file must be accessible to the backend and
    the name must be specified from the viewpoint of the backend. When
    <filename>stdin</filename> or <filename>stdout</filename> is
-   specified, data flows through the client frontend to the backend.
+   specified, data is transmitted via the connection between the
+   client frontend and the backend.
     
     <tip>
      <para>
@@ -234,8 +236,8 @@ ERROR: <replaceable>reason</replaceable>
     Notes
    </title>
    <para>
-   <command>COPY</command> can only be used with plain tables, not with
-   views.
+    <command>COPY</command> can only be used with plain tables, not
+    with views.
    </para>
 
    <para>
diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml
index 2801aeeabb2e2dc88c127843140f749d80b0ca7f..f0673c44b67f9c5fbc7e159d5bb4cf1120bdd2cb 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.20 2003/03/21 17:11:46 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.21 2003/03/27 16:51:27 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -21,8 +21,8 @@ PostgreSQL documentation
    <date>1999-07-20</date>
   </refsynopsisdivinfo>
   <synopsis>
-DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INSENSITIVE ] [ SCROLL ]
-    CURSOR FOR <replaceable class="parameter">query</replaceable>
+DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
+    CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="parameter">query</replaceable>
     [ FOR { READ ONLY | UPDATE [ OF <replaceable class="parameter">column</replaceable> [, ...] ] ]
   </synopsis>
   <refsect2 id="R2-SQL-DECLARE-1">
@@ -38,7 +38,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term><replaceable class="parameter">cursorname</replaceable></term>
       <listitem>
        <para>
-	The name of the cursor to be used in subsequent FETCH operations.
+	The name of the cursor to be used in subsequent
+	<command>FETCH</command> operations.
        </para>
       </listitem>
      </varlistentry>
@@ -57,8 +58,20 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <listitem>
        <para>
 	<acronym>SQL92</acronym> keyword indicating that data retrieved
-	from the cursor should be unaffected by updates from other processes or cursors.
-	By default, all cursors are insensitive.  This keyword has no effect.
+	from the cursor should be unaffected by updates from other
+	processes or cursors.  By default, all cursors are insensitive.
+	This keyword currently has no effect and is present for
+	compatibility with the SQL standard.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>NO SCROLL</term>
+      <listitem>
+       <para>
+    Specifies that the cursor cannot be used to retrieve rows in a
+    nonsequential fashion (e.g., backward).
        </para>
       </listitem>
      </varlistentry>
@@ -67,8 +80,33 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term>SCROLL</term>
       <listitem>
        <para>
-	Specifies that the cursor may be used to retrieve rows
-	in a nonsequential fashion (e.g., backwards).
+	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
+	on the query's execution time.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>WITHOUT HOLD</term>
+      <listitem>
+       <para>
+        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.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>WITH HOLD</term>
+      <listitem>
+       <para>
+        Specifies that the cursor may be used after the transaction
+        that creates it successfully commits.
        </para>
       </listitem>
      </varlistentry>
@@ -124,7 +162,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
    </para>
 
    <para>
-    The BINARY, INSENSITIVE, and SCROLL keywords may appear in any order.
+    The <literal>BINARY</literal>, <literal>INSENSITIVE</literal>,
+    <literal>SCROLL</literal> keywords may appear in any order.
    </para>
   </refsect2>
 
@@ -144,7 +183,7 @@ DECLARE CURSOR
        </computeroutput></term>
       <listitem>
        <para>
-	The message returned if the SELECT is run successfully.
+	The message returned if the <command>SELECT</command> is run successfully.
        </para>
       </listitem>
      </varlistentry>
@@ -155,9 +194,8 @@ WARNING:  Closing pre-existing portal "<replaceable class="parameter">cursorname
        </computeroutput></term>
       <listitem>
        <para>
-	This message is reported if the same cursor name was already declared
-	in the current transaction block.  The previous definition is
-	discarded.
+	This message is reported if a cursor with the same name already
+	exists. The previous definition is discarded.
        </para>
       </listitem>
      </varlistentry>
@@ -168,7 +206,9 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
        </computeroutput></term>
       <listitem>
        <para>
-	This error occurs if the cursor is not declared within a transaction block.
+	This error occurs if the cursor is not declared within a
+	transaction block, and <literal>WITH HOLD</literal> is not
+	specified.
        </para>
       </listitem>
      </varlistentry>     
@@ -193,16 +233,14 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
   </para>
 
   <para>
-   Normal cursors return data in text format, the same as a <command>SELECT</>
-   would produce.  Since
-   data is stored natively in binary format, the system must
-   do a conversion to produce the text format. In addition,
-   text formats are often larger in size than the corresponding binary format.
-   Once the information comes back in text form,  the client
-   application may need to convert it to a binary format to
-   manipulate it.
-   BINARY cursors give you back the data in the native binary
-   representation.
+   Normal cursors return data in text format, the same as a
+   <command>SELECT</> would produce.  Since data is stored natively in
+   binary format, the system must do a conversion to produce the text
+   format. In addition, text formats are often larger in size than the
+   corresponding binary format.  Once the information comes back in
+   text form, the client application may need to convert it to a
+   binary format to manipulate it.  BINARY cursors give you back the
+   data in the native binary representation.
   </para>
 
   <para>
@@ -245,7 +283,9 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    </title>
 
    <para>
-    Cursors are only available within transactions. Use
+    If <literal>WITH HOLD</literal> is not specified, the cursor
+    created by this command can only be used within the current
+    transaction. Use
     <xref linkend="sql-begin" endterm="sql-begin-title">,
     <xref linkend="sql-commit" endterm="sql-commit-title">
     and
@@ -254,12 +294,25 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    </para>
 
    <para>
-    The <literal>SCROLL</> option should be specified when defining a cursor 
-    that will be used to fetch backwards.  This is required by
-    <acronym>SQL92</acronym>.  However, for compatibility with
-    earlier versions, <productname>PostgreSQL</productname> will allow
-    backward fetches without <literal>SCROLL</>, if the cursor's query plan
-    is simple enough that no extra overhead is needed to support it.
+    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
+    with <literal>WITH HOLD</literal> is closed when an explicit
+    <command>CLOSE</command> command is issued on it, or the client
+    connection is terminated.
+   </para>
+
+   <para>
+    The <literal>SCROLL</> option should be specified when defining a
+    cursor that will be used to fetch backwards.  This is required by
+    <acronym>SQL92</acronym>.  However, for compatibility with earlier
+    versions, <productname>PostgreSQL</productname> will allow
+    backward fetches without <literal>SCROLL</>, if the cursor's query
+    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>.
    </para>
 
    <para>
@@ -271,7 +324,7 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
     However, <application>ecpg</application>, the
     embedded SQL preprocessor for <productname>PostgreSQL</productname>,
     supports the <acronym>SQL92</acronym> cursor conventions, including those
-    involving DECLARE and OPEN statements.
+    involving <command>DECLARE</command> and <command>OPEN</command> statements.
    </para>
   </refsect2>
  </refsect1>
@@ -303,13 +356,21 @@ DECLARE liahona CURSOR
     SQL92
    </title>
    <para>
-    <acronym>SQL92</acronym> allows cursors only in embedded <acronym>SQL</acronym>
-    and in modules. <productname>PostgreSQL</productname> permits cursors to be used
-    interactively.
+   <para>
+    <acronym>SQL92</acronym> allows cursors only in embedded
+    <acronym>SQL</acronym> and in modules. <productname>PostgreSQL</>
+    permits cursors to be used interactively.
+   </para>
+
+   <para>
     <acronym>SQL92</acronym> allows embedded or modular cursors to
-    update database information.
-    All <productname>PostgreSQL</productname> cursors are read only.
-    The BINARY keyword is a <productname>PostgreSQL</productname> extension.
+    update database information. All <productname>PostgreSQL</>
+    cursors are read only.
+   </para>
+
+   <para>
+    The <literal>BINARY</literal> keyword is a
+    <productname>PostgreSQL</productname> extension.
    </para>
   </refsect2>
  </refsect1>
diff --git a/doc/src/sgml/ref/fetch.sgml b/doc/src/sgml/ref/fetch.sgml
index 8f3244eb39ff51fd270f713c6b6a5af8cd109281..a1f3b13719fae9d3d5d46c422cec4d7ba1346050 100644
--- a/doc/src/sgml/ref/fetch.sgml
+++ b/doc/src/sgml/ref/fetch.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.27 2003/03/11 19:40:22 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.28 2003/03/27 16:51:27 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -251,8 +251,7 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
        </computeroutput></term>
       <listitem>
        <para>
-	If <replaceable class="PARAMETER">cursor</replaceable> is not known.
-	The cursor must have been declared within the current transaction block.
+        There is no cursor with the specified name.
        </para>
       </listitem>
      </varlistentry>
@@ -326,7 +325,9 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
     use any variants of <command>FETCH</> other than <command>FETCH NEXT</>
     or <command>FETCH FORWARD</> with a positive count.  For simple queries
     <productname>PostgreSQL</productname> will allow backwards fetch from
-    cursors not declared with SCROLL, but this behavior is best not relied on.
+    cursors not declared with SCROLL, but this behavior is best not
+    relied on. If the cursor is declared with NO SCROLL, no backward
+    fetches are allowed.
    </para>
 
    <para>
@@ -339,16 +340,11 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
    </para>
 
    <para>
-    Updating data via a cursor is not supported by 
-    <productname>PostgreSQL</productname>,
-    because mapping cursor updates back to base tables is
-    not generally possible, as is also the case with VIEW updates.
-    Consequently,
-    users must issue explicit UPDATE commands to replace data.
-   </para>
-
-   <para>
-    Cursors may only be used inside transaction blocks.
+    Updating data via a cursor is not supported by
+    <productname>PostgreSQL</productname>, because mapping cursor
+    updates back to base tables is not generally possible, as is also
+    the case with view updates.  Consequently, users must issue
+    explicit <command>UPDATE</command> commands to replace data.
    </para>
 
    <para>
@@ -357,12 +353,6 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
     Use
     <xref linkend="sql-move" endterm="sql-move-title">
     to change cursor position without retrieving data.
-    Refer to
-    <xref linkend="sql-begin" endterm="sql-begin-title">,
-    <xref linkend="sql-commit" endterm="sql-commit-title">,
-    and
-    <xref linkend="sql-rollback" endterm="sql-rollback-title">
-    for further information about transactions.
    </para>
   </refsect2>
  </refsect1>
@@ -379,7 +369,7 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
 -- Set up and use a cursor:
 
 BEGIN WORK;
-DECLARE liahona CURSOR FOR SELECT * FROM films;
+DECLARE liahona SCROLL CURSOR FOR SELECT * FROM films;
 
 -- Fetch first 5 rows in the cursor liahona:
 FETCH FORWARD 5 IN liahona;
@@ -425,9 +415,10 @@ COMMIT WORK;
    </title>
 
    <para>
-    <acronym>SQL92</acronym> defines FETCH for use in embedded contexts only.
-    Therefore, it describes placing the results into explicit variables using
-    an <literal>INTO</> clause, for example:
+    <acronym>SQL92</acronym> defines <command>FETCH</command> for use
+    in embedded contexts only.  Therefore, it describes placing the
+    results into explicit variables using an <literal>INTO</> clause,
+    for example:
 
     <synopsis>
 FETCH ABSOLUTE <replaceable class="PARAMETER">n</replaceable>
@@ -435,16 +426,18 @@ FETCH ABSOLUTE <replaceable class="PARAMETER">n</replaceable>
     INTO :<replaceable class="PARAMETER">variable</replaceable> [, ...]
     </synopsis>
 
-    <productname>PostgreSQL</productname>'s use of non-embedded cursors
-    is non-standard, and so is its practice of returning the result data
-    as if it were a SELECT result.  Other than this point, FETCH is fully
+    <productname>PostgreSQL</productname>'s use of non-embedded
+    cursors is non-standard, and so is its practice of returning the
+    result data as if it were a <command>SELECT</command> result.
+    Other than this point, <command>FETCH</command> is fully
     upward-compatible with <acronym>SQL92</acronym>.
    </para>
 
    <para>
-    The FETCH forms involving FORWARD and BACKWARD (including the forms
-    FETCH <replaceable class="PARAMETER">count</replaceable> and FETCH ALL,
-    in which FORWARD is implicit) are <productname>PostgreSQL</productname>
+    The <command>FETCH</command> forms involving FORWARD and BACKWARD
+    (including the forms FETCH <replaceable
+    class="PARAMETER">count</replaceable> and FETCH ALL, in which
+    FORWARD is implicit) are <productname>PostgreSQL</productname>
     extensions.
    </para>
 
diff --git a/doc/src/sgml/ref/move.sgml b/doc/src/sgml/ref/move.sgml
index f01ee9d8a587c4df5a6259347459b2d3609858bf..cd6d6aca0fd357ef64ff12f54d980fa31118ce13 100644
--- a/doc/src/sgml/ref/move.sgml
+++ b/doc/src/sgml/ref/move.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.20 2003/03/11 19:40:22 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.21 2003/03/27 16:51:27 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -64,12 +64,6 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <r
     Refer to 
     <xref linkend="sql-declare" endterm="sql-declare-title">
     to define a cursor.
-    Refer to 
-    <xref linkend="sql-begin" endterm="sql-begin-title">, 
-    <xref linkend="sql-commit" endterm="sql-commit-title">,
-    and
-    <xref linkend="sql-rollback" endterm="sql-rollback-title">
-    for further information about transactions.
    </para>
   </refsect2>
  </refsect1>
@@ -83,7 +77,7 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <r
 
 <programlisting>
 BEGIN WORK;
-DECLARE liahona CURSOR  FOR SELECT * FROM films;
+DECLARE liahona CURSOR FOR SELECT * FROM films;
 -- Skip first 5 rows:
 MOVE FORWARD 5 IN liahona;
 <computeroutput>
diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml
index 9e788ff0616f69926282366040d9717682d533e5..89de66680289b0bff927156959d9833eec7052ee 100644
--- a/doc/src/sgml/sql.sgml
+++ b/doc/src/sgml/sql.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/sql.sgml,v 1.29 2003/02/19 04:06:28 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/sql.sgml,v 1.30 2003/03/27 16:51:26 momjian Exp $
 -->
 
  <chapter id="sql">
@@ -851,7 +851,7 @@ A &lt; B + 3.
 
     <para>
      The most often used command in <acronym>SQL</acronym> is the
-     SELECT statement,
+     <command>SELECT</command> statement,
      used to retrieve data. The syntax is:
 
      <synopsis>
@@ -881,7 +881,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac
      <title>Simple Selects</title>
 
      <para>
-      Here are some simple examples using a SELECT statement:
+      Here are some simple examples using a <command>SELECT</command> statement:
 
       <example>
        <title id="simple-query">Simple Query with Qualification</title>
@@ -905,9 +905,10 @@ SELECT * FROM PART
        </para>
 
        <para>
-	Using <quote>*</quote> in the SELECT statement will deliver all attributes from
-	the table. If we want to retrieve only the attributes PNAME and PRICE
-	from table PART we use the statement:
+	Using <quote>*</quote> in the <command>SELECT</command> statement
+	will deliver all attributes from the table. If we want to retrieve
+	only the attributes PNAME and PRICE from table PART we use the
+	statement:
 
 	<programlisting>
 SELECT PNAME, PRICE 
@@ -924,9 +925,9 @@ SELECT PNAME, PRICE
                       Cam    |   25
 	</programlisting>
 
-	Note that the <acronym>SQL</acronym> SELECT corresponds to the
-	<quote>projection</quote> in relational algebra not to the
-	<quote>selection</quote> (see <xref linkend="rel-alg"
+	Note that the <acronym>SQL</acronym> <command>SELECT</command>
+	corresponds to the <quote>projection</quote> in relational algebra
+	not to the <quote>selection</quote> (see <xref linkend="rel-alg"
 	endterm="rel-alg"> for more details).
        </para>
 
@@ -1252,15 +1253,15 @@ select sname, pname from supplier
      <title id="aggregates-tutorial">Aggregate Operators</title>
 
      <para>
-      <acronym>SQL</acronym> provides aggregate operators
-      (e.g. AVG, COUNT, SUM, MIN, MAX) that
-      take an expression as argument.  The expression is evaluated at
-      each row that satisfies the WHERE clause, and the aggregate operator
-      is calculated over this set of input values.  Normally, an aggregate
-      delivers a single result for a whole SELECT statement.  But if
-      grouping is specified in the query, then a separate calculation is done
-      over the rows of each group, and an aggregate result is delivered per
-      group (see next section).
+      <acronym>SQL</acronym> provides aggregate operators (e.g. AVG,
+      COUNT, SUM, MIN, MAX) that take an expression as argument.  The
+      expression is evaluated at each row that satisfies the WHERE
+      clause, and the aggregate operator is calculated over this set
+      of input values.  Normally, an aggregate delivers a single
+      result for a whole <command>SELECT</command> statement.  But if
+      grouping is specified in the query, then a separate calculation
+      is done over the rows of each group, and an aggregate result is
+      delivered per group (see next section).
 
       <example>
        <title id="aggregates-example">Aggregates</title>
@@ -1413,11 +1414,12 @@ SELECT S.SNO, S.SNAME, COUNT(SE.PNO)
      </para>
 
      <para>
-      Also observe that it makes no sense to ask for an aggregate of an
-      aggregate, e.g., AVG(MAX(sno)), because a SELECT only does one pass
-      of grouping and aggregation.  You can get a result of this kind by
-      using a temporary table or a sub-SELECT in the FROM clause to
-      do the first level of aggregation.
+      Also observe that it makes no sense to ask for an aggregate of
+      an aggregate, e.g., AVG(MAX(sno)), because a
+      <command>SELECT</command> only does one pass of grouping and
+      aggregation.  You can get a result of this kind by using a
+      temporary table or a sub-SELECT in the FROM clause to do the
+      first level of aggregation.
      </para>
     </sect3>
 
@@ -1502,16 +1504,18 @@ SELECT *
        </para>
 
        <para>
-	When we look at the above query we can see
-	the keyword SELECT two times. The first one at the beginning of the
-	query - we will refer to it as outer SELECT - and the one in the WHERE
-	clause which begins a nested query - we will refer to it as inner
-	SELECT. For every tuple of the outer SELECT the inner SELECT has to be
-	evaluated. After every evaluation we know the price of the tuple named
-	'Screw' and we can check if the price of the actual tuple is
-	greater.  (Actually, in this example the inner query need only be
-	evaluated once, since it does not depend on the state of the outer
-	query.)
+	When we look at the above query we can see the keyword
+	<command>SELECT</command> two times. The first one at the
+	beginning of the query - we will refer to it as outer
+	<command>SELECT</command> - and the one in the WHERE clause which
+	begins a nested query - we will refer to it as inner
+	<command>SELECT</command>. For every tuple of the outer
+	<command>SELECT</command> the inner <command>SELECT</command> has
+	to be evaluated. After every evaluation we know the price of the
+	tuple named 'Screw' and we can check if the price of the actual
+	tuple is greater.  (Actually, in this example the inner query need
+	only be evaluated once, since it does not depend on the state of
+	the outer query.)
        </para>
 
        <para>
@@ -1528,11 +1532,13 @@ SELECT *
        </para>
 
        <para>
-	In our example the result will be empty because every supplier sells
-	at least one part. Note that we use S.SNO from the outer SELECT within
-	the WHERE clause of the inner SELECT. Here the subquery must be
-	evaluated afresh for each tuple from the outer query, i.e. the value for
-	S.SNO is always taken from the current tuple of the outer SELECT. 
+	In our example the result will be empty because every supplier
+	sells at least one part. Note that we use S.SNO from the outer
+	<command>SELECT</command> within the WHERE clause of the inner
+	<command>SELECT</command>. Here the subquery must be evaluated
+	afresh for each tuple from the outer query, i.e. the value for
+	S.SNO is always taken from the current tuple of the outer
+	<command>SELECT</command>.
        </para>
       </example>
      </para>
@@ -1670,7 +1676,7 @@ EXCEPT
      <para>
       The most fundamental command for data definition is the
       one that creates a new relation (a new table). The syntax of the
-      CREATE TABLE command is:
+      <command>CREATE TABLE</command> command is:
 
       <synopsis>
 CREATE TABLE <replaceable class="parameter">table_name</replaceable>
@@ -1786,7 +1792,7 @@ CREATE TABLE SELLS
 
      <para>
       To create an index in <acronym>SQL</acronym>
-      the CREATE INDEX command is used. The syntax is:
+      the <command>CREATE INDEX</command> command is used. The syntax is:
 
       <programlisting>
 CREATE INDEX <replaceable class="parameter">index_name</replaceable> 
@@ -1808,10 +1814,11 @@ CREATE INDEX I ON SUPPLIER (SNAME);
      </para>
 
        <para>
-	The created index is maintained automatically, i.e. whenever a new tuple
-	is inserted into the relation SUPPLIER the index I is adapted. Note
-	that the only changes a user can perceive when an index is present
-	are increased speed for SELECT and decreases in speed of updates.
+	The created index is maintained automatically, i.e. whenever a new
+	tuple is inserted into the relation SUPPLIER the index I is
+	adapted. Note that the only changes a user can perceive when an
+	index is present are increased speed for <command>SELECT</command>
+	and decreases in speed of updates.
        </para>
       </example>
      </para>
@@ -1916,7 +1923,7 @@ SELECT * FROM London_Suppliers
 
      <para>
       To destroy a table (including all tuples stored in that table) the
-      DROP TABLE command is used:
+      <command>DROP TABLE</command> command is used:
 
       <programlisting>
 DROP TABLE <replaceable class="parameter">table_name</replaceable>;
@@ -1932,7 +1939,7 @@ DROP TABLE SUPPLIER;
      </para>
 
      <para>
-      The DROP INDEX command is used to destroy an index:
+      The <command>DROP INDEX</command> command is used to destroy an index:
 
       <programlisting>
 DROP INDEX <replaceable class="parameter">index_name</replaceable>;
@@ -1940,7 +1947,8 @@ DROP INDEX <replaceable class="parameter">index_name</replaceable>;
      </para>
 
      <para>
-      Finally to destroy a given view use the command DROP VIEW:
+      Finally to destroy a given view use the command <command>DROP
+      VIEW</command>:
 
       <programlisting>
 DROP VIEW <replaceable class="parameter">view_name</replaceable>;
@@ -1994,7 +2002,7 @@ INSERT INTO SELLS (SNO, PNO)
 
      <para>
       To change one or more attribute values of tuples in a relation the
-      UPDATE command is used. The syntax is:
+      <command>UPDATE</command> command is used. The syntax is:
 
       <programlisting>
 UPDATE <replaceable class="parameter">table_name</replaceable>
@@ -2126,7 +2134,7 @@ DELETE FROM SUPPLIER
      need a mechanism to access every single tuple of the set of tuples
      returned by a SELECT statement. This mechanism can be provided by
      declaring a <firstterm>cursor</firstterm>.
-     After that we can use the FETCH command to
+     After that we can use the <command>FETCH</command> command to
      retrieve a tuple and set the cursor to the next tuple.
     </para>
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 3f172dddfc662c3106f1b334bf6b61980a51a65f..598e3c880e5ed0e0bb345d4a153dcf34ba8d52b1 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.144 2003/03/21 04:33:15 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.145 2003/03/27 16:51:27 momjian Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -926,7 +926,7 @@ CommitTransaction(void)
 	 * access, and in fact could still cause an error...)
 	 */
 
-	AtEOXact_portals();
+	AtEOXact_portals(true);
 
 	/* handle commit for large objects [ PA, 7/17/98 ] */
 	/* XXX probably this does not belong here */
@@ -1057,7 +1057,7 @@ AbortTransaction(void)
 	 * do abort processing
 	 */
 	DeferredTriggerAbortXact();
-	AtEOXact_portals();
+	AtEOXact_portals(false);
 	lo_commit(false);			/* 'false' means it's abort */
 	AtAbort_Notify();
 	AtEOXact_UpdatePasswordFile(false);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index baaf57a70ac0af0206576daf58cb4e57c0970a74..978429c87f7118260520a93bda2bd6a3ddda2026 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1,15 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * copy.c
- *		COPY command.
- *
+ *		Implements the COPY utility command.
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.189 2003/02/03 21:15:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.190 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,6 +49,9 @@
 #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
 #define OCTVALUE(c) ((c) - '0')
 
+/*
+ * Represents the type of data returned by CopyReadAttribute()
+ */
 typedef enum CopyReadResult
 {
 	NORMAL_ATTR,
@@ -1311,7 +1313,7 @@ GetTypeElement(Oid type)
  * *result is set to indicate what terminated the read:
  *		NORMAL_ATTR:	column delimiter
  *		END_OF_LINE:	newline
- *		END_OF_FILE:	EOF indication
+ *		END_OF_FILE:	EOF indicator
  * In all cases, the string read up to the terminator is returned.
  *
  * Note: This function does not care about SQL NULL values -- it
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 1ba72437ad72669e35829d0ff6367d45335f0bc1..7eabc58d49599c55cef10035f068cf0c567027f6 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.10 2003/03/11 19:40:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.11 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,18 +17,23 @@
 
 #include <limits.h>
 
+#include "miscadmin.h"
 #include "commands/portalcmds.h"
 #include "executor/executor.h"
 #include "optimizer/planner.h"
 #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 void DoPortalRewind(Portal portal);
-static Portal PreparePortal(char *portalName);
+static Portal PreparePortal(DeclareCursorStmt *stmt);
 
 
 /*
@@ -46,8 +51,15 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
 	char	   *cursorName;
 	QueryDesc  *queryDesc;
 
-	/* Check for invalid context (must be in transaction block) */
-	RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
+	/*
+	 * If this is a non-holdable cursor, we ensure 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");
 
 	/*
 	 * The query has been through parse analysis, but not rewriting or
@@ -76,7 +88,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
 	/*
 	 * Create a portal and copy the query and plan into its memory context.
 	 */
-	portal = PreparePortal(stmt->portalname);
+	portal = PreparePortal(stmt);
 
 	oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 	query = copyObject(query);
@@ -130,6 +142,7 @@ PerformPortalFetch(FetchStmt *stmt,
 	portal = GetPortalByName(stmt->portalname);
 	if (!PortalIsValid(portal))
 	{
+		/* FIXME: shouldn't this be an ERROR? */
 		elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
 			 stmt->portalname);
 		return;
@@ -343,6 +356,9 @@ DoRelativeFetch(Portal portal,
 	ScanDirection direction;
 	QueryDesc	temp_queryDesc;
 
+	if (portal->holdStore)
+		return DoRelativeStoreFetch(portal, forward, count, dest);
+
 	queryDesc = PortalGetQueryDesc(portal);
 	estate = queryDesc->estate;
 
@@ -407,7 +423,7 @@ DoRelativeFetch(Portal portal,
 	}
 	else
 	{
-		if (!portal->backwardOK)
+		if (portal->scrollType == DISABLE_SCROLL)
 			elog(ERROR, "Cursor can only scan forward"
 				 "\n\tDeclare it with SCROLL option to enable backward scan");
 
@@ -452,17 +468,85 @@ DoRelativeFetch(Portal portal,
 	return estate->es_processed;
 }
 
+/*
+ * DoRelativeStoreFetch
+ *		Do fetch for a simple N-rows-forward-or-backward case, getting
+ *		the results from the portal's tuple store.
+ */
+static long
+DoRelativeStoreFetch(Portal portal,
+					 bool forward,
+					 long count,
+					 CommandDest dest)
+{
+	DestReceiver *destfunc;
+	QueryDesc *queryDesc = portal->queryDesc;
+	long rows_fetched = 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->setup) (destfunc, queryDesc->operation,
+						portal->name, queryDesc->tupDesc);
+
+	for (;;)
+	{
+		HeapTuple tup;
+		bool should_free;
+
+		if (rows_fetched >= count)
+			break;
+		if (portal->atEnd && forward)
+			break;
+		if (portal->atStart && !forward)
+			break;
+
+		tup = tuplestore_getheaptuple(portal->holdStore, forward, &should_free);
+
+		if (tup == NULL)
+		{
+			if (forward)
+				portal->atEnd = true;
+			else
+				portal->atStart = true;
+
+			break;
+		}
+
+		(*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
+
+		rows_fetched++;
+		if (forward)
+			portal->portalPos++;
+		else
+			portal->portalPos--;
+
+		if (forward && portal->atStart)
+			portal->atStart = false;
+		if (!forward && portal->atEnd)
+			portal->atEnd = false;
+
+		if (should_free)
+			pfree(tup);
+	}
+
+	(*destfunc->cleanup) (destfunc);
+
+	return rows_fetched;
+}
+
 /*
  * DoPortalRewind - rewind a Portal to starting point
  */
 static void
 DoPortalRewind(Portal portal)
 {
-	QueryDesc  *queryDesc;
-
-	queryDesc = PortalGetQueryDesc(portal);
-
-	ExecutorRewind(queryDesc);
+	if (portal->holdStore)
+		tuplestore_rescan(portal->holdStore);
+	else
+		ExecutorRewind(PortalGetQueryDesc(portal));
 
 	portal->atStart = true;
 	portal->atEnd = false;
@@ -493,22 +577,25 @@ PerformPortalClose(char *name)
 	/*
 	 * Note: PortalCleanup is called as a side-effect
 	 */
-	PortalDrop(portal);
+	PortalDrop(portal, false);
 }
 
-
 /*
  * PreparePortal
+ *		Given a DECLARE CURSOR statement, returns the Portal data
+ *		structure based on that statement that is used to manage the
+ *		Portal internally. If a portal with specified name already
+ *		exists, it is replaced.
  */
 static Portal
-PreparePortal(char *portalName)
+PreparePortal(DeclareCursorStmt *stmt)
 {
 	Portal		portal;
 
 	/*
 	 * Check for already-in-use portal name.
 	 */
-	portal = GetPortalByName(portalName);
+	portal = GetPortalByName(stmt->portalname);
 	if (PortalIsValid(portal))
 	{
 		/*
@@ -516,19 +603,30 @@ PreparePortal(char *portalName)
 		 * portal?
 		 */
 		elog(WARNING, "Closing pre-existing portal \"%s\"",
-			 portalName);
-		PortalDrop(portal);
+			 stmt->portalname);
+		PortalDrop(portal, false);
 	}
 
 	/*
 	 * Create the new portal.
 	 */
-	portal = CreatePortal(portalName);
+	portal = CreatePortal(stmt->portalname);
+
+	/*
+	 * Modify the newly created portal based on the options specified in
+	 * the DECLARE CURSOR statement.
+	 */
+	if (stmt->options & CURSOR_OPT_SCROLL)
+		portal->scrollType = ENABLE_SCROLL;
+	else if (stmt->options & CURSOR_OPT_NO_SCROLL)
+		portal->scrollType = DISABLE_SCROLL;
+
+	if (stmt->options & CURSOR_OPT_HOLD)
+		portal->holdOpen = true;
 
 	return portal;
 }
 
-
 /*
  * PortalCleanup
  *
@@ -545,14 +643,128 @@ PortalCleanup(Portal portal)
 	AssertArg(PortalIsValid(portal));
 	AssertArg(portal->cleanup == PortalCleanup);
 
+	if (portal->holdStore)
+		tuplestore_end(portal->holdStore);
+	else
+		ExecutorEnd(PortalGetQueryDesc(portal));
+
+}
+
+/*
+ * PersistHoldablePortal
+ *
+ * Prepare the specified Portal for access outside of the current
+ * transaction. When this function returns, all future accesses to the
+ * portal must be done via the Tuplestore (not by invoking the
+ * executor).
+ */
+void
+PersistHoldablePortal(Portal portal)
+{
+	MemoryContext oldcxt;
+	QueryDesc *queryDesc = PortalGetQueryDesc(portal);
+
+	/*
+	 * If we're preserving a holdable portal, we had better be
+	 * inside the transaction that originally created it.
+	 */
+	Assert(portal->createXact == GetCurrentTransactionId());
+	Assert(portal->holdStore == NULL);
+
+	/*
+	 * This context is used to store portal data that needs to persist
+	 * between transactions.
+	 */
+	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
+	 * processed.
+	 */
+	ExecutorRewind(queryDesc);
+
+	/* Fetch the result set into the tuplestore */
+	ExecutorRun(queryDesc, ForwardScanDirection, 0);
+
+	/*
+	 * Reset the position in the result set: ideally, this could be
+	 * implemented by just skipping straight to the tuple # that we need
+	 * to be at, but the tuplestore API doesn't support that. So we
+	 * start at the beginning of the tuplestore and iterate through it
+	 * until we reach where we need to be.
+	 */
+	if (!portal->atEnd)
+	{
+		int store_pos = 0;
+		bool should_free;
+
+		tuplestore_rescan(portal->holdStore);
+
+		while (store_pos < portal->portalPos)
+		{
+			HeapTuple tmp = tuplestore_gettuple(portal->holdStore,
+												true, &should_free);
+
+			if (tmp == 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);
+		}
+	}
+
 	/*
-	 * tell the executor to shutdown the query
+	 * 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.
 	 */
-	ExecutorEnd(PortalGetQueryDesc(portal));
+	{
+		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;
+	}
 
 	/*
-	 * This should be unnecessary since the querydesc should be in the
-	 * portal's memory context, but do it anyway for symmetry.
+	 * We no longer need the portal's short-term memory context.
 	 */
-	FreeQueryDesc(PortalGetQueryDesc(portal));
+	MemoryContextDelete(PortalGetHeapMemory(portal));
+
+	PortalGetHeapMemory(portal) = NULL;
 }
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 7e3f5d2d2c83545b6c2195b91e9934ab6dac772c..cc49bd6d69e8955d57c446b1458aa582ce341004 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for executor
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.20 2003/01/10 23:54:24 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.21 2003/03/27 16:51:27 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -18,7 +18,7 @@ OBJS = execAmi.o execGrouping.o execJunk.o execMain.o \
        nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
        nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
        nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
-       nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
+       nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 1266895a5f698f7a11c33159d86a72ca31f47206..485f1e03fa0d924a585240aaa58701c80ac01f48 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.204 2003/03/27 14:33:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.205 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -217,8 +217,8 @@ ExecutorRun(QueryDesc *queryDesc,
 	estate->es_lastoid = InvalidOid;
 
 	destfunc = DestToFunction(dest);
-	(*destfunc->setup) (destfunc, (int) operation,
-						queryDesc->portalName, queryDesc->tupDesc);
+	(*destfunc->setup) (destfunc, operation, queryDesc->portalName,
+						queryDesc->tupDesc);
 
 	/*
 	 * run plan
@@ -421,15 +421,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 	}
 }
 
-
-/* ===============================================================
- * ===============================================================
-						 static routines follow
- * ===============================================================
- * ===============================================================
- */
-
-
 static void
 ExecCheckXactReadOnly(Query *parsetree, CmdType operation)
 {
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index a1c1fdd8ad350e8851b79ec5b4c655513a5b729d..7e084f2830201ca87e795003139208dbbc031f5d 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.126 2003/03/09 02:19:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.127 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1054,8 +1054,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 									   0,
 									   false);
 				}
-				tupstore = tuplestore_begin_heap(true,	/* randomAccess */
-												 SortMem);
+				tupstore = tuplestore_begin_heap(true, false, SortMem);
 				MemoryContextSwitchTo(oldcontext);
 				rsinfo.setResult = tupstore;
 				rsinfo.setDesc = tupdesc;
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 31152a3d8552d770570417c0a3060e16b1caa21d..b338c8961e2a20b43cd35a9dfc47b0befd86ac70 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.74 2003/01/10 23:54:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.75 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ ExecHash(HashState *node)
 		 * buffers are palloc'd in regular executor context.
 		 */
 		for (i = 0; i < nbatch; i++)
-			hashtable->innerBatchFile[i] = BufFileCreateTemp();
+			hashtable->innerBatchFile[i] = BufFileCreateTemp(false);
 	}
 
 	/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 3603fd9b6800f517233eb6b0d56995eb98800b4c..4bc1671801f5cd84de9ffe4d32e107560e298f0f 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.48 2003/01/27 20:51:48 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.49 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -138,7 +138,7 @@ ExecHashJoin(HashJoinState *node)
 		 * buffers are palloc'd in regular executor context.
 		 */
 		for (i = 0; i < hashtable->nbatch; i++)
-			hashtable->outerBatchFile[i] = BufFileCreateTemp();
+			hashtable->outerBatchFile[i] = BufFileCreateTemp(false);
 	}
 	else if (hashtable == NULL)
 		return NULL;
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 2566851dccc5eb81ae531f6c5d3c5b2883bc2d93..39968c65e0cffe113c4c7cff6a519470a45f2f20 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.41 2003/03/09 02:19:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.42 2003/03/27 16:51:27 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,8 +62,7 @@ ExecMaterial(MaterialState *node)
 	 */
 	if (tuplestorestate == NULL)
 	{
-		tuplestorestate = tuplestore_begin_heap(true,	/* randomAccess */
-												SortMem);
+		tuplestorestate = tuplestore_begin_heap(true, false, SortMem);
 
 		node->tuplestorestate = (void *) tuplestorestate;
 	}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 61eed9b4004d11e545a50ea7fb6e7049e284ab53..7184fbf9ba173f19838aea7953dcc68cf0153b1d 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.88 2003/03/11 19:40:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.89 2003/03/27 16:51:28 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -866,7 +866,7 @@ SPI_cursor_close(Portal portal)
 	if (!PortalIsValid(portal))
 		elog(ERROR, "invalid portal in SPI cursor operation");
 
-	PortalDrop(portal);
+	PortalDrop(portal, false);
 }
 
 /* =================== private functions =================== */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 3486a1e010b15358c7bb0cf7a40c4ce09b677699..64cd5603acdc549351d6896b69b5289bc025cf7a 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.265 2003/03/10 03:53:50 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.266 2003/03/27 16:51:28 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2294,6 +2294,13 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 	result->commandType = CMD_UTILITY;
 	result->utilityStmt = (Node *) stmt;
 
+	/*
+	 * Don't allow both SCROLL and NO SCROLL to be specified
+	 */
+	if ((stmt->options & CURSOR_OPT_SCROLL) &&
+		(stmt->options & CURSOR_OPT_NO_SCROLL))
+		elog(ERROR, "Both SCROLL and NO SCROLL cannot be specified.");
+
 	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 dc8b06e750757f946493811d5b5013ab70078d08..0ee065a9a11785638cccd8a40b0098e085d7b05e 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.408 2003/03/20 18:52:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.409 2003/03/27 16:51:28 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -246,7 +246,7 @@ static void doNegateFloat(Value *v);
 %type <boolean> opt_freeze opt_default opt_recheck
 %type <defelt>	opt_binary opt_oids copy_delimiter
 
-%type <boolean> copy_from
+%type <boolean> copy_from opt_hold
 
 %type <ival>	reindex_type drop_type fetch_count
 				opt_column event comment_type cursor_options
@@ -348,7 +348,7 @@ static void doNegateFloat(Value *v);
 
 	GLOBAL GRANT GROUP_P
 
-	HANDLER HAVING HOUR_P
+	HANDLER HAVING HOLD HOUR_P
 
 	ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCREMENT
 	INDEX INHERITS INITIALLY INNER_P INOUT INPUT
@@ -4230,22 +4230,31 @@ UpdateStmt: UPDATE relation_expr
  *				CURSOR STATEMENTS
  *
  *****************************************************************************/
-DeclareCursorStmt: DECLARE name cursor_options CURSOR FOR SelectStmt
+DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
 				{
 					DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
 					n->portalname = $2;
 					n->options = $3;
-					n->query = $6;
+					n->query = $7;
+
+					if ($5)
+						n->options |= CURSOR_OPT_HOLD;
+
 					$$ = (Node *)n;
 				}
 		;
 
 cursor_options: /*EMPTY*/					{ $$ = 0; }
-			| cursor_options BINARY			{ $$ = $1 | CURSOR_OPT_BINARY; }
+			| cursor_options NO SCROLL		{ $$ = $1 | CURSOR_OPT_NO_SCROLL; }
 			| cursor_options SCROLL			{ $$ = $1 | CURSOR_OPT_SCROLL; }
+			| cursor_options BINARY			{ $$ = $1 | CURSOR_OPT_BINARY; }
 			| cursor_options INSENSITIVE	{ $$ = $1 | CURSOR_OPT_INSENSITIVE; }
 		;
 
+opt_hold: /* EMPTY */						{ $$ = FALSE; }
+			| WITH HOLD						{ $$ = TRUE; }
+			| WITHOUT HOLD					{ $$ = FALSE; }
+
 /*****************************************************************************
  *
  *		QUERY:
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 83608d95edb4999b2ad7b86ddd4bfaa0ed814bad..d62f5668d5d7dc659d2911243d9a1a52a2f8f36e 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.136 2003/03/20 07:02:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.137 2003/03/27 16:51:28 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -143,6 +143,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"group", GROUP_P},
 	{"handler", HANDLER},
 	{"having", HAVING},
+	{"hold", HOLD},
 	{"hour", HOUR_P},
 	{"ilike", ILIKE},
 	{"immediate", IMMEDIATE},
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 7e78793a404337debb63d234f560d44d9f4faf25..90b185cd5dec24169b6012980b5acccafe5443e2 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.14 2002/09/05 00:43:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.15 2003/03/27 16:51:29 momjian Exp $
  *
  * NOTES:
  *
@@ -64,6 +64,7 @@ struct BufFile
 	 */
 
 	bool		isTemp;			/* can only add files if this is TRUE */
+	bool		isInterTxn;		/* keep open over transactions? */
 	bool		dirty;			/* does buffer need to be written? */
 
 	/*
@@ -118,7 +119,7 @@ extendBufFile(BufFile *file)
 	File		pfile;
 
 	Assert(file->isTemp);
-	pfile = OpenTemporaryFile();
+	pfile = OpenTemporaryFile(file->isInterTxn);
 	Assert(pfile >= 0);
 
 	file->files = (File *) repalloc(file->files,
@@ -136,16 +137,17 @@ extendBufFile(BufFile *file)
  * written to it).
  */
 BufFile *
-BufFileCreateTemp(void)
+BufFileCreateTemp(bool interTxn)
 {
 	BufFile    *file;
 	File		pfile;
 
-	pfile = OpenTemporaryFile();
+	pfile = OpenTemporaryFile(interTxn);
 	Assert(pfile >= 0);
 
 	file = makeBufFile(pfile);
 	file->isTemp = true;
+	file->isInterTxn = interTxn;
 
 	return file;
 }
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 83c97fb75503f528517aa13dc28ece463a9e8070..7607d4186cc340001aaf48437c759589e1fee822 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.95 2002/09/02 06:11:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.96 2003/03/27 16:51:29 momjian Exp $
  *
  * NOTES:
  *
@@ -112,14 +112,14 @@ 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)
+
 typedef struct vfd
 {
 	signed short fd;			/* current FD, or VFD_CLOSED if none */
 	unsigned short fdstate;		/* bitflags for VFD's state */
-
-/* these are the assigned bits in fdstate: */
-#define FD_TEMPORARY	(1 << 0)	/* should be unlinked when closed */
-
 	File		nextFree;		/* link to next free VFD, if in freelist */
 	File		lruMoreRecently;	/* doubly linked recency-of-use list */
 	File		lruLessRecently;
@@ -750,9 +750,15 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
  * This routine takes care of generating an appropriate tempfile name.
  * 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
+ * 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.
  */
 File
-OpenTemporaryFile(void)
+OpenTemporaryFile(bool keepOverTxn)
 {
 	char		tempfilepath[128];
 	File		file;
@@ -795,9 +801,13 @@ OpenTemporaryFile(void)
 			elog(ERROR, "Failed to create temporary file %s", tempfilepath);
 	}
 
-	/* Mark it for deletion at close or EOXact */
+	/* 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;
+
 	return file;
 }
 
@@ -1114,6 +1124,7 @@ AtEOXact_Files(void)
 		for (i = 1; i < SizeVfdCache; i++)
 		{
 			if ((VfdCache[i].fdstate & FD_TEMPORARY) &&
+				(VfdCache[i].fdstate & FD_TXN_TEMPORARY) &&
 				VfdCache[i].fileName != NULL)
 				FileClose(i);
 		}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 516af21a4665d4163e7f3a5690885dfaf3eebdfa..ad9d2327717ed849f4ee1bdaa00c6602f0c513bb 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.50 2003/01/21 22:06:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.51 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "postgres.h"
 
 #include "access/printtup.h"
+#include "executor/tstoreReceiver.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 
@@ -60,9 +61,11 @@ donothingCleanup(DestReceiver *self)
 static DestReceiver donothingDR = {
 	donothingReceive, donothingSetup, donothingCleanup
 };
+
 static DestReceiver debugtupDR = {
 	debugtup, debugSetup, donothingCleanup
 };
+
 static DestReceiver spi_printtupDR = {
 	spi_printtup, spi_dest_setup, donothingCleanup
 };
@@ -98,6 +101,9 @@ DestToFunction(CommandDest dest)
 		case SPI:
 			return &spi_printtupDR;
 
+		case Tuplestore:
+			return tstoreReceiverCreateDR();
+
 		case None:
 			return &donothingDR;
 	}
@@ -122,6 +128,7 @@ EndCommand(const char *commandTag, CommandDest dest)
 
 		case None:
 		case Debug:
+		case Tuplestore:
 		case SPI:
 			break;
 	}
@@ -183,6 +190,7 @@ NullCommand(CommandDest dest)
 			break;
 
 		case Debug:
+		case Tuplestore:
 		case None:
 		default:
 			break;
@@ -213,6 +221,7 @@ ReadyForQuery(CommandDest dest)
 			break;
 
 		case Debug:
+		case Tuplestore:
 		case None:
 		default:
 			break;
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 48545b15128fde0f0991be58e88e5e3d79c76d85..729137a1b9290c0a3dd6a4e865e16948e5be13fe 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.38 2002/12/16 16:22:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.39 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,8 @@
 MemoryContext CurrentMemoryContext = NULL;
 
 /*
- * Standard top-level contexts
+ * Standard top-level contexts. For a description of the purpose of each
+ * of these contexts, refer to src/backend/utils/mmgr/README
  */
 MemoryContext TopMemoryContext = NULL;
 MemoryContext ErrorContext = NULL;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 66ee72718cfa640baf2872608181c4dfd9acc574..e6486d4b16dcc89957f6b437b3e0f4b3944712e2 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.53 2003/03/11 19:40:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.54 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,8 +20,8 @@
  *		"PortalData" structure, plans the query and then stores the query
  *		in the portal without executing it.  Later, when the backend
  *		sees a
- *				fetch 1 from FOO
- *		the system looks up the portal named "FOO" in the portal table,
+ *				fetch 1 from foo
+ *		the system looks up the portal named "foo" in the portal table,
  *		gets the planned query and then calls the executor with a count
  *		of 1.  The executor then runs the query and returns a single
  *		tuple.	The problem is that we have to hold onto the state of the
@@ -38,7 +38,6 @@
 #include "utils/memutils.h"
 #include "utils/portal.h"
 
-
 /*
  * estimate of the maximum number of open portals a user would have,
  * used in initially sizing the PortalHashTable in EnablePortalManager()
@@ -131,8 +130,8 @@ EnablePortalManager(void)
 	ctl.entrysize = sizeof(PortalHashEnt);
 
 	/*
-	 * use PORTALS_PER_USER, defined in utils/portal.h as a guess of how
-	 * many hash table entries to create, initially
+	 * use PORTALS_PER_USER as a guess of how many hash table entries to
+	 * create, initially
 	 */
 	PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
 								  &ctl, HASH_ELEM);
@@ -157,7 +156,9 @@ GetPortalByName(const char *name)
 
 /*
  * PortalSetQuery
- *		Attaches a "query" to portal.
+ *		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.
  */
 void
 PortalSetQuery(Portal portal,
@@ -166,9 +167,25 @@ PortalSetQuery(Portal portal,
 {
 	AssertArg(PortalIsValid(portal));
 
+	/*
+	 * If the user didn't specify a SCROLL type, allow or disallow
+	 * scrolling based on whether it would require any additional
+	 * runtime overhead to do so.
+	 */
+	if (portal->scrollType == DEFAULT_SCROLL)
+	{
+		bool backwardPlan;
+
+		backwardPlan = ExecSupportsBackwardScan(queryDesc->plantree);
+
+		if (backwardPlan)
+			portal->scrollType = ENABLE_SCROLL;
+		else
+			portal->scrollType = DISABLE_SCROLL;
+	}
+
 	portal->queryDesc = queryDesc;
 	portal->cleanup = cleanup;
-	portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
 	portal->atStart = true;
 	portal->atEnd = false;		/* allow fetches */
 	portal->portalPos = 0;
@@ -179,10 +196,8 @@ PortalSetQuery(Portal portal,
  * CreatePortal
  *		Returns a new portal given a name.
  *
- * Exceptions:
- *		BadState if called when disabled.
- *		BadArg if portal name is invalid.
- *		"WARNING" if portal name is in use (existing portal is returned!)
+ *		An elog(WARNING) is emitted if portal name is in use (existing
+ *		portal is returned!)
  */
 Portal
 CreatePortal(const char *name)
@@ -214,7 +229,11 @@ CreatePortal(const char *name)
 	/* initialize portal query */
 	portal->queryDesc = NULL;
 	portal->cleanup = NULL;
-	portal->backwardOK = false;
+	portal->scrollType = DEFAULT_SCROLL;
+	portal->holdOpen = false;
+	portal->holdStore = NULL;
+	portal->holdContext = NULL;
+	portal->createXact = GetCurrentTransactionId();
 	portal->atStart = true;
 	portal->atEnd = true;		/* disallow fetches until query is set */
 	portal->portalPos = 0;
@@ -228,17 +247,47 @@ CreatePortal(const char *name)
 
 /*
  * PortalDrop
- *		Destroys portal.
+ *		Destroy the portal.
  *
- * Exceptions:
- *		BadState if called when disabled.
- *		BadArg if portal is invalid.
+ *		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.
  */
 void
-PortalDrop(Portal portal)
+PortalDrop(Portal portal, bool persistHoldable)
 {
 	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 */
 	PortalHashTableDelete(portal);
 
@@ -246,8 +295,20 @@ PortalDrop(Portal portal)
 	if (PointerIsValid(portal->cleanup))
 		(*portal->cleanup) (portal);
 
-	/* release subsidiary storage */
-	MemoryContextDelete(PortalGetHeapMemory(portal));
+	/*
+	 * 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
+	 */
+	if (portal->holdContext)
+		MemoryContextDelete(portal->holdContext);
 
 	/* release name and portal data (both are in PortalMemory) */
 	pfree(portal->name);
@@ -255,7 +316,12 @@ PortalDrop(Portal portal)
 }
 
 /*
- * Destroy all portals created in the current transaction (ie, all of them).
+ * Cleanup the portals created in the current transaction. If the
+ * 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.
  *
  * 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
@@ -264,13 +330,17 @@ PortalDrop(Portal portal)
  * references...
  */
 void
-AtEOXact_portals(void)
+AtEOXact_portals(bool isCommit)
 {
 	HASH_SEQ_STATUS status;
 	PortalHashEnt *hentry;
+	TransactionId xact = GetCurrentTransactionId();
 
 	hash_seq_init(&status, PortalHashTable);
 
 	while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
-		PortalDrop(hentry->portal);
+	{
+		if (hentry->portal->createXact == xact)
+			PortalDrop(hentry->portal, isCommit);
+	}
 }
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 1c28028f5f7e262b3c0d1c2e76d75de54eb79eed..643b7a760181fff3442f8a351503fd3a58ff124f 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -64,7 +64,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/logtape.c,v 1.8 2002/06/20 20:29:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/logtape.c,v 1.9 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -470,7 +470,7 @@ LogicalTapeSetCreate(int ntapes)
 	Assert(ntapes > 0);
 	lts = (LogicalTapeSet *) palloc(sizeof(LogicalTapeSet) +
 									(ntapes - 1) *sizeof(LogicalTape *));
-	lts->pfile = BufFileCreateTemp();
+	lts->pfile = BufFileCreateTemp(false);
 	lts->nFileBlocks = 0L;
 	lts->freeBlocksLen = 32;	/* reasonable initial guess */
 	lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 65804d5484d770a3b852e2502b2728f990864d64..89970b2495b3fedfd28fd8fa1b3971fdaac363ce 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.11 2003/03/09 02:19:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.12 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,6 +65,7 @@ struct Tuplestorestate
 {
 	TupStoreStatus status;		/* enumerated value as shown above */
 	bool		randomAccess;	/* did caller request random access? */
+	bool		interTxn;		/* keep open through transactions? */
 	long		availMem;		/* remaining memory available, in bytes */
 	BufFile    *myfile;			/* underlying file, or NULL if none */
 
@@ -190,7 +191,8 @@ struct Tuplestorestate
 
 
 static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
-						int maxKBytes);
+												bool interTxn,
+												int maxKBytes);
 static void dumptuples(Tuplestorestate *state);
 static unsigned int getlen(Tuplestorestate *state, bool eofOK);
 static void *copytup_heap(Tuplestorestate *state, void *tup);
@@ -205,7 +207,7 @@ static void *readtup_heap(Tuplestorestate *state, unsigned int len);
  */
 
 static Tuplestorestate *
-tuplestore_begin_common(bool randomAccess, int maxKBytes)
+tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
 {
 	Tuplestorestate *state;
 
@@ -213,6 +215,7 @@ tuplestore_begin_common(bool randomAccess, int maxKBytes)
 
 	state->status = TSS_INMEM;
 	state->randomAccess = randomAccess;
+	state->interTxn = interTxn;
 	state->availMem = maxKBytes * 1024L;
 	state->myfile = NULL;
 
@@ -231,10 +234,27 @@ tuplestore_begin_common(bool randomAccess, int maxKBytes)
 	return state;
 }
 
+/*
+ * tuplestore_begin_heap
+ *
+ * Create a new tuplestore; other types of tuple stores (other than
+ * "heap" tuple stores, for heap tuples) are possible, but not presently
+ * implemented.
+ *
+ * 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.
+ *
+ * maxKBytes: how much data to store in memory (any data beyond this
+ * amount is paged to disk).
+ */
 Tuplestorestate *
-tuplestore_begin_heap(bool randomAccess, int maxKBytes)
+tuplestore_begin_heap(bool randomAccess, bool interTxn, int maxKBytes)
 {
-	Tuplestorestate *state = tuplestore_begin_common(randomAccess, maxKBytes);
+	Tuplestorestate *state = tuplestore_begin_common(randomAccess,
+													 interTxn, maxKBytes);
 
 	state->copytup = copytup_heap;
 	state->writetup = writetup_heap;
@@ -321,7 +341,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
 			/*
 			 * Nope; time to switch to tape-based operation.
 			 */
-			state->myfile = BufFileCreateTemp();
+			state->myfile = BufFileCreateTemp(state->interTxn);
 			state->status = TSS_WRITEFILE;
 			dumptuples(state);
 			break;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 49887f450cb9512fd5f02846320b5fad30e35e44..3d4b235e5629e4b999b8a7236d5d1a33204ca610 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.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: parsenodes.h,v 1.235 2003/03/20 18:52:48 momjian Exp $
+ * $Id: parsenodes.h,v 1.236 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1210,7 +1210,9 @@ typedef struct CommentStmt
  */
 #define CURSOR_OPT_BINARY		0x0001
 #define CURSOR_OPT_SCROLL		0x0002
-#define CURSOR_OPT_INSENSITIVE	0x0004
+#define CURSOR_OPT_NO_SCROLL	0x0004
+#define CURSOR_OPT_INSENSITIVE	0x0008
+#define CURSOR_OPT_HOLD			0x0010
 
 typedef struct DeclareCursorStmt
 {
diff --git a/src/include/storage/buffile.h b/src/include/storage/buffile.h
index 902483a1fb34f5c47e4c04a588d02575f36d03a1..c456b74d0f6ad4f40d2152f3d31a189420396b49 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.12 2002/06/20 20:29:52 momjian Exp $
+ * $Id: buffile.h,v 1.13 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@ typedef struct BufFile BufFile;
  * prototypes for functions in buffile.c
  */
 
-extern BufFile *BufFileCreateTemp(void);
+extern BufFile *BufFileCreateTemp(bool interTxn);
 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 a13cec41ea6827dbc137737e9c15ae6a38f44923..261e6314e91a9a60b613a6df4032330973ffc93f 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.36 2002/08/06 02:36:35 tgl Exp $
+ * $Id: fd.h,v 1.37 2003/03/27 16:51:29 momjian 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(void);
+extern File OpenTemporaryFile(bool keepOverTxn);
 extern void FileClose(File file);
 extern void FileUnlink(File file);
 extern int	FileRead(File file, char *buffer, int amount);
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index bbad436c049b8e16725363f714c85ef5833355c4..bbf86ef4ca60b03a39af8fd1f0761da1e0309c9f 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -44,7 +44,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dest.h,v 1.32 2002/09/04 20:31:45 momjian Exp $
+ * $Id: dest.h,v 1.33 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,7 +70,8 @@ typedef enum
 	Remote,						/* results sent to frontend process */
 	RemoteInternal,				/* results sent to frontend process in
 								 * internal (binary) form */
-	SPI							/* results sent to SPI manager */
+	SPI,						/* results sent to SPI manager */
+	Tuplestore					/* results sent to Tuplestore */
 } CommandDest;
 
 /* ----------------
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index c9ca8547ce2d773dfcc69eec2e571d4c56967811..0201b0684c378c2f21402525fb35edaa36ec378b 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.39 2003/03/11 19:40:24 tgl Exp $
+ * $Id: portal.h,v 1.40 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,17 +18,45 @@
 
 #include "executor/execdesc.h"
 #include "nodes/memnodes.h"
+#include "utils/tuplestore.h"
 
+/*
+ * We support three kinds of scroll behavior:
+ *
+ * (1) Neither NO SCROLL nor SCROLL was specified: to remain backward
+ *     compatible, we allow backward fetches here, unless it would
+ *     impose additional runtime overhead to do so.
+ *
+ * (2) NO SCROLL was specified: don't allow any backward fetches.
+ *
+ * (3) SCROLL was specified: allow all kinds of backward fetches, even
+ *     if we need to take a slight performance hit to do so.
+ *
+ * Case #1 is converted to #2 or #3 by looking at the query itself and
+ * determining if scrollability can be supported without additional
+ * overhead.
+ */
+typedef enum
+{
+	DEFAULT_SCROLL,
+	DISABLE_SCROLL,
+	ENABLE_SCROLL
+} ScrollType;
 
 typedef struct PortalData *Portal;
 
 typedef struct PortalData
 {
 	char	   *name;			/* Portal's name */
-	MemoryContext heap;			/* subsidiary memory */
+	MemoryContext heap;			/* memory for storing short-term data */
 	QueryDesc  *queryDesc;		/* Info about query associated with portal */
 	void		(*cleanup) (Portal);	/* Cleanup routine (optional) */
-	bool		backwardOK;		/* is fetch backwards allowed? */
+	ScrollType	scrollType;		/* Allow backward fetches? */
+	bool		holdOpen;		/* hold open after txn ends? */
+	TransactionId createXact;	/* the xid of the creating txn */
+	Tuplestorestate *holdStore;	/* store for holdable cursors */
+	MemoryContext holdContext;  /* memory for long-term data */
+
 	/*
 	 * atStart, atEnd and portalPos indicate the current cursor position.
 	 * portalPos is zero before the first row, N after fetching N'th row of
@@ -58,11 +86,12 @@ typedef struct PortalData
 
 
 extern void EnablePortalManager(void);
-extern void AtEOXact_portals(void);
+extern void AtEOXact_portals(bool isCommit);
 extern Portal CreatePortal(const char *name);
-extern void PortalDrop(Portal portal);
+extern void PortalDrop(Portal portal, bool persistHoldable);
 extern Portal GetPortalByName(const char *name);
 extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
 						   void (*cleanup) (Portal portal));
+extern void PersistHoldablePortal(Portal portal);
 
 #endif   /* PORTAL_H */
diff --git a/src/include/utils/tuplestore.h b/src/include/utils/tuplestore.h
index 76fe9fb42870db7adfc671e3d9e09bd573b67ae4..dbc47ef29d61d87862c8b92850e74e80b5c03771 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.9 2003/03/09 03:34:10 tgl Exp $
+ * $Id: tuplestore.h,v 1.10 2003/03/27 16:51:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,8 @@ typedef struct Tuplestorestate Tuplestorestate;
  */
 
 extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
-					  int maxKBytes);
+											  bool interTxn,
+											  int maxKBytes);
 
 extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index d105c0e8dcc076a891c0aec78ae740c975c5e247..d2e05f16c26a06c01962f336ff580ac0b0335abe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.83 2003/03/25 03:16:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.84 2003/03/27 16:51:29 momjian Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -1757,7 +1757,7 @@ exec_init_tuple_store(PLpgSQL_execstate * estate)
 	estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
 
 	oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
-	estate->tuple_store = tuplestore_begin_heap(true, SortMem);
+	estate->tuple_store = tuplestore_begin_heap(true, false, SortMem);
 	MemoryContextSwitchTo(oldcxt);
 
 	estate->rettupdesc = rsi->expectedDesc;
diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out
index 23c186cead15bc3942c51f66181efaea4fc14619..7520653924e17213d159707fad2defaed72530d3 100644
--- a/src/test/regress/expected/portals.out
+++ b/src/test/regress/expected/portals.out
@@ -1,30 +1,30 @@
 --
--- PORTALS
+-- Cursor regression tests
 --
 BEGIN;
-DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
-DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
-DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1;
 FETCH 1 in foo1;
  unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
 ---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
@@ -675,4 +675,66 @@ CLOSE foo9;
 CLOSE foo10;
 CLOSE foo11;
 CLOSE foo12;
-end;
+-- is there a reason why we don't close the rest of the open cursors?
+END;
+--
+-- NO SCROLL disallows backward fetching
+--
+BEGIN;
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1;
+FETCH 1 FROM foo24;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    8800 |       0 |   0 |    0 |   0 |      0 |       0 |      800 |         800 |      3800 |     8800 |   0 |    1 | MAAAAA   | AAAAAA   | AAAAxx
+(1 row)
+
+FETCH BACKWARD 1 FROM foo24; -- should fail
+ERROR:  Cursor can only scan forward
+	Declare it with SCROLL option to enable backward scan
+END;
+--
+-- Cursors outside transaction blocks
+--
+BEGIN;
+DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2;
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    8800 |       0 |   0 |    0 |   0 |      0 |       0 |      800 |         800 |      3800 |     8800 |   0 |    1 | MAAAAA   | AAAAAA   | AAAAxx
+(1 row)
+
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    1891 |       1 |   1 |    3 |   1 |     11 |      91 |      891 |        1891 |      1891 |     1891 | 182 |  183 | TUAAAA   | BAAAAA   | HHHHxx
+(1 row)
+
+COMMIT;
+FETCH FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    3420 |       2 |   0 |    0 |   0 |      0 |      20 |      420 |        1420 |      3420 |     3420 |  40 |   41 | OBAAAA   | CAAAAA   | OOOOxx
+(1 row)
+
+FETCH BACKWARD FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    1891 |       1 |   1 |    3 |   1 |     11 |      91 |      891 |        1891 |      1891 |     1891 | 182 |  183 | TUAAAA   | BAAAAA   | HHHHxx
+(1 row)
+
+FETCH ABSOLUTE -1 FROM foo25;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    2968 |    9999 |   0 |    0 |   8 |      8 |      68 |      968 |         968 |      2968 |     2968 | 136 |  137 | EKAAAA   | PUOAAA   | VVVVxx
+(1 row)
+
+CLOSE foo25;
+--
+-- ROLLBACK should close holdable cursors
+--
+BEGIN;
+DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
+ROLLBACK;
+-- should fail
+FETCH FROM foo26;
+WARNING:  PerformPortalFetch: portal "foo26" not found
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index c4e257ef2d07d04d7a5d123eed7cf62d13bccdf4..2df820a30a79c28b39c33f6e11db1bc6e76691a9 100644
--- a/src/test/regress/sql/portals.sql
+++ b/src/test/regress/sql/portals.sql
@@ -1,54 +1,54 @@
 --
--- PORTALS
+-- Cursor regression tests
 --
 
 BEGIN;
 
-DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo1 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo2 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo3 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo4 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo5 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo6 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo7 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo8 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo9 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo10 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo11 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo12 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo13 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo14 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo15 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo16 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo17 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo18 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo19 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo20 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo21 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
-DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
+DECLARE foo22 SCROLL CURSOR FOR SELECT * FROM tenk2;
 
-DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+DECLARE foo23 SCROLL CURSOR FOR SELECT * FROM tenk1;
 
 FETCH 1 in foo1;
 
@@ -166,5 +166,55 @@ CLOSE foo11;
 
 CLOSE foo12;
 
-end;
+-- is there a reason why we don't close the rest of the open cursors?
 
+END;
+
+--
+-- NO SCROLL disallows backward fetching
+--
+
+BEGIN;
+
+DECLARE foo24 NO SCROLL CURSOR FOR SELECT * FROM tenk1;
+
+FETCH 1 FROM foo24;
+
+FETCH BACKWARD 1 FROM foo24; -- should fail
+
+END;
+
+--
+-- Cursors outside transaction blocks
+--
+
+BEGIN;
+
+DECLARE foo25 SCROLL CURSOR WITH HOLD FOR SELECT * FROM tenk2;
+
+FETCH FROM foo25;
+
+FETCH FROM foo25;
+
+COMMIT;
+
+FETCH FROM foo25;
+
+FETCH BACKWARD FROM foo25;
+
+FETCH ABSOLUTE -1 FROM foo25;
+
+CLOSE foo25;
+
+--
+-- ROLLBACK should close holdable cursors
+--
+
+BEGIN;
+
+DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
+
+ROLLBACK;
+
+-- should fail
+FETCH FROM foo26;
\ No newline at end of file