OSDN Git Service

Implement SQL92-compatible FIRST, LAST, ABSOLUTE n, RELATIVE n options
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 11 Mar 2003 19:40:24 +0000 (19:40 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 11 Mar 2003 19:40:24 +0000 (19:40 +0000)
for FETCH and MOVE.

13 files changed:
doc/src/sgml/ref/fetch.sgml
doc/src/sgml/ref/move.sgml
src/backend/commands/portalcmds.c
src/backend/executor/execMain.c
src/backend/executor/spi.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/tcop/utility.c
src/backend/utils/mmgr/portalmem.c
src/include/commands/portalcmds.h
src/include/executor/executor.h
src/include/nodes/parsenodes.h
src/include/utils/portal.h

index 0452cf0..8f3244e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.26 2003/03/10 03:53:49 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.27 2003/03/11 19:40:22 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -18,17 +18,32 @@ PostgreSQL documentation
  </refnamediv>
  <refsynopsisdiv>
   <refsynopsisdivinfo>
-   <date>1999-07-20</date>
+   <date>2003-03-11</date>
   </refsynopsisdivinfo>
   <synopsis>
-FETCH [ <replaceable class="PARAMETER">direction</replaceable> ] [ <replaceable class="PARAMETER">count</replaceable> ] { IN | FROM } <replaceable class="PARAMETER">cursor</replaceable>
-FETCH [ FORWARD | BACKWARD | RELATIVE ] [ <replaceable class="PARAMETER">#</replaceable> | ALL | NEXT | PRIOR ]
-    { IN | FROM } <replaceable class="PARAMETER">cursor</replaceable>
+FETCH [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <replaceable class="PARAMETER">cursor</replaceable>
+
+where <replaceable class="PARAMETER">direction</replaceable> can be empty or one of:
+
+    NEXT
+    PRIOR
+    FIRST
+    LAST
+    ABSOLUTE <replaceable class="PARAMETER">count</replaceable>
+    RELATIVE <replaceable class="PARAMETER">count</replaceable>
+    <replaceable class="PARAMETER">count</replaceable>
+    ALL
+    FORWARD
+    FORWARD <replaceable class="PARAMETER">count</replaceable>
+    FORWARD ALL
+    BACKWARD
+    BACKWARD <replaceable class="PARAMETER">count</replaceable>
+    BACKWARD ALL
   </synopsis>
 
   <refsect2 id="R2-SQL-FETCH-1">
    <refsect2info>
-    <date>1998-09-01</date>
+    <date>2003-03-11</date>
    </refsect2info>
    <title>
     Inputs
@@ -41,97 +56,171 @@ FETCH [ FORWARD | BACKWARD | RELATIVE ] [ <replaceable class="PARAMETER">#</repl
       <listitem>
        <para>
        <replaceable class="PARAMETER">direction</replaceable>
-       defines the fetch direction. It can be one of
-       the following:
+       defines the fetch direction and number of rows to fetch.
+       It can be one of the following:
 
        <variablelist>
         <varlistentry>
-         <term>FORWARD</term>
+         <term>NEXT</term>
          <listitem>
           <para>
-           fetch next row(s). This is the default
+           fetch next row. This is the default
            if <replaceable class="PARAMETER">direction</replaceable> is omitted.
           </para>
          </listitem>
         </varlistentry>
+
         <varlistentry>
-         <term>BACKWARD</term>
+         <term>PRIOR</term>
          <listitem>
           <para>
-           fetch previous row(s).
+           fetch prior row.
           </para>
          </listitem>
         </varlistentry>
+
         <varlistentry>
-         <term>RELATIVE</term>
+         <term>FIRST</term>
          <listitem>
           <para>
-           Same as FORWARD; provided for SQL92 compatibility.
+           fetch first row of query (same as ABSOLUTE 1).
           </para>
          </listitem>
         </varlistentry>
-       </variablelist>
-       </para>
-      </listitem>
-     </varlistentry>
 
-     <varlistentry>
-      <term><replaceable class="PARAMETER">count</replaceable></term>
-      <listitem>
-       <para>
-       <replaceable class="PARAMETER">count</replaceable>
-       determines how many rows to fetch. It can be one of the following:
+        <varlistentry>
+         <term>LAST</term>
+         <listitem>
+          <para>
+           fetch last row of query (same as ABSOLUTE -1).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term>ABSOLUTE <replaceable class="PARAMETER">count</replaceable></term>
+         <listitem>
+          <para>
+           fetch the <replaceable class="PARAMETER">count</replaceable>'th
+           row of query, or the
+           abs(<replaceable class="PARAMETER">count</replaceable>)'th row
+           from the end if
+           <replaceable class="PARAMETER">count</replaceable> &lt; 0.
+           Position before first row or after last row
+           if <replaceable class="PARAMETER">count</replaceable> is out of
+           range; in particular, ABSOLUTE 0 positions before first row.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term>RELATIVE <replaceable class="PARAMETER">count</replaceable></term>
+         <listitem>
+          <para>
+           fetch the <replaceable class="PARAMETER">count</replaceable>'th
+           succeeding row, or the
+           abs(<replaceable class="PARAMETER">count</replaceable>)'th prior
+           row if <replaceable class="PARAMETER">count</replaceable> &lt; 0.
+           RELATIVE 0 re-fetches current row, if any.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><replaceable class="PARAMETER">count</replaceable></term>
+         <listitem>
+          <para>
+           fetch the next <replaceable class="PARAMETER">count</replaceable>
+           rows (same as FORWARD <replaceable class="PARAMETER">count</replaceable>).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term>ALL</term>
+         <listitem>
+          <para>
+           fetch all remaining rows (same as FORWARD ALL).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term>FORWARD</term>
+         <listitem>
+          <para>
+           fetch next row (same as NEXT).
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term>FORWARD <replaceable class="PARAMETER">count</replaceable></term>
+         <listitem>
+          <para>
+           fetch next <replaceable class="PARAMETER">count</replaceable>
+           rows.  FORWARD 0 re-fetches current row.
+          </para>
+         </listitem>
+        </varlistentry>
 
-       <variablelist>
         <varlistentry>
-         <term><replaceable class="PARAMETER">#</replaceable></term>
+         <term>FORWARD ALL</term>
          <listitem>
           <para>
-           A signed integer constant that specifies how many rows to fetch.
-           Note that a negative integer is equivalent to changing the sense of
-           FORWARD and BACKWARD. Zero re-fetches the current row, if any.
+           fetch all remaining rows.
           </para>
          </listitem>
         </varlistentry>
 
         <varlistentry>
-         <term>
-          ALL
-         </term>
+         <term>BACKWARD</term>
          <listitem>
           <para>
-           Retrieve all remaining rows.
+           fetch prior row (same as PRIOR).
           </para>
          </listitem>
         </varlistentry>
 
         <varlistentry>
-         <term>
-          NEXT
-         </term>
+         <term>BACKWARD <replaceable class="PARAMETER">count</replaceable></term>
          <listitem>
           <para>
-           Equivalent to specifying a count of <command>1</command>.
+           fetch prior <replaceable class="PARAMETER">count</replaceable>
+           rows (scanning backwards).  BACKWARD 0 re-fetches current row.
           </para>
          </listitem>
         </varlistentry>
 
         <varlistentry>
-         <term>
-          PRIOR
-         </term>
+         <term>BACKWARD ALL</term>
          <listitem>
           <para>
-           Equivalent to specifying a count of <command>-1</command>.
+           fetch all prior rows (scanning backwards).
           </para>
          </listitem>
         </varlistentry>
+
        </variablelist>
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
+      <term><replaceable class="PARAMETER">count</replaceable></term>
+      <listitem>
+       <para>
+       <replaceable class="PARAMETER">count</replaceable>
+       is a possibly-signed integer constant, determining the location
+       or number of rows to fetch.  For FORWARD and BACKWARD cases,
+       specifying a negative <replaceable
+       class="PARAMETER">count</replaceable>
+       is equivalent to changing the sense of FORWARD and BACKWARD.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><replaceable class="PARAMETER">cursor</replaceable></term>
       <listitem>
        <para>
@@ -145,7 +234,7 @@ FETCH [ FORWARD | BACKWARD | RELATIVE ] [ <replaceable class="PARAMETER">#</repl
 
   <refsect2 id="R2-SQL-FETCH-2">
    <refsect2info>
-    <date>1998-04-15</date>
+    <date>2003-03-11</date>
    </refsect2info>
    <title>
     Outputs
@@ -162,25 +251,11 @@ WARNING:  PerformPortalFetch: portal "<replaceable class="PARAMETER">cursor</rep
        </computeroutput></term>
       <listitem>
        <para>
-       If <replaceable class="PARAMETER">cursor</replaceable>
-       is not previously declared.
-       The cursor must be declared within a transaction block.
+       If <replaceable class="PARAMETER">cursor</replaceable> is not known.
+       The cursor must have been declared within the current transaction block.
        </para>
       </listitem>
      </varlistentry>
-
-     <varlistentry>
-      <term><computeroutput>
-WARNING:  FETCH/ABSOLUTE not supported, using RELATIVE
-       </computeroutput></term>
-      <listitem>
-       <para>
-       <productname>PostgreSQL</productname> does not support absolute
-       positioning of cursors.
-       </para>
-      </listitem>
-     </varlistentry>
-
     </variablelist>
    </para>
   </refsect2>
@@ -188,75 +263,79 @@ WARNING:  FETCH/ABSOLUTE not supported, using RELATIVE
 
  <refsect1 id="R1-SQL-FETCH-1">
   <refsect1info>
-   <date>1998-04-15</date>
+   <date>2003-03-11</date>
   </refsect1info>
   <title>
    Description
   </title>
 
   <para>
-   <command>FETCH</command> allows a user to retrieve rows using a cursor.
-   The number of rows retrieved is specified by
-   <replaceable class="PARAMETER">#</replaceable>.
-   If the number of rows remaining in the cursor is less
-   than <replaceable class="PARAMETER">#</replaceable>,
-   then only those available are fetched.
-   Substituting the keyword ALL in place of a number will
-   cause all remaining rows in the cursor to be retrieved.
-   Rows may be fetched in both FORWARD and BACKWARD
-   directions. The default direction is FORWARD.
+   <command>FETCH</command> retrieves rows using a cursor.
   </para>
 
   <para>
-   The cursor position can be before the first row of the query result, or on
-   any particular row of the result, or after the last row of the result.
-   When created, a cursor is positioned before the first row.  After fetching
-   some rows, the cursor is positioned on the last row retrieved.  A new
-   <command>FETCH</command> always steps one row in the specified direction
-   (if possible) before beginning to return rows.  If the
-   <command>FETCH</command> requests more rows than available, the cursor is
-   left positioned after the last row of the query result (or before the first
-   row, in the case of a backward fetch).  This will always be the case after
-   <command>FETCH ALL</>.
+   A cursor has an associated <firstterm>position</> that is used by
+   <command>FETCH</>.  The cursor position can be before the first row of the
+   query result, or on any particular row of the result, or after the last row
+   of the result.  When created, a cursor is positioned before the first row.
+   After fetching some rows, the cursor is positioned on the row most recently
+   retrieved.  If <command>FETCH</> runs off the end of the available rows
+   then the cursor is left positioned after the last row, or before the first
+   row if fetching backward.  <command>FETCH ALL</> or <command>FETCH BACKWARD
+   ALL</> will always leave the cursor positioned after the last row or before
+   the first row.
+  </para>
+
+  <para>
+   The SQL-compatible forms (NEXT, PRIOR, FIRST, LAST, ABSOLUTE, RELATIVE)
+   fetch a single row after moving the cursor appropriately.  If there is
+   no such row, an empty result is returned, and the cursor is left positioned
+   before the first row or after the last row as appropriate.
+  </para>
+
+  <para>
+   The forms using FORWARD and BACKWARD are not in the SQL standard, but
+   are <productname>PostgreSQL</productname> extensions.  These forms
+   retrieve the indicated number of rows moving in the forward or backward
+   direction, leaving the cursor positioned on the last-returned row
+   (or after/before all rows, if the <replaceable
+   class="PARAMETER">count</replaceable> exceeds the number of rows
+   available).
   </para>
 
    <tip>
     <para>
-     A zero row count requests fetching the current row without moving the
+     RELATIVE 0, FORWARD 0, and BACKWARD 0 all request
+     fetching the current row without moving the
      cursor --- that is, re-fetching the most recently fetched row.
      This will succeed unless the cursor is positioned before the
      first row or after the last row; in which case, no row is returned.
     </para>
    </tip>
 
-   <tip>
-    <para>
-     Negative numbers are allowed to be specified for the
-     row count. A negative number is equivalent to reversing
-     the sense of the FORWARD and BACKWARD keywords. For example,
-     <command>FORWARD -1</command> is the same as <command>BACKWARD 1</command>.
-    </para>
-   </tip>
-
   <refsect2 id="R2-SQL-FETCH-3">
    <refsect2info>
-    <date>1998-04-15</date>
+    <date>2003-03-11</date>
    </refsect2info>
    <title>
     Notes
    </title>
 
    <para>
-    A cursor to be used in backwards fetching should be declared with the
-    SCROLL option.  In simple cases, <productname>PostgreSQL</productname>
-    will allow backwards fetch from cursors not declared with SCROLL, but
-    this behavior is best not relied on.
+    The cursor should be declared with the SCROLL option if one intends to
+    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.
    </para>
 
    <para>
-    The FORWARD, BACKWARD, and ALL keywords are
-    <productname>PostgreSQL</productname> extensions.
-    See below for details on compatibility issues.
+    ABSOLUTE fetches are not any faster than navigating to the desired row
+    with a relative move: the underlying implementation must traverse all
+    the intermediate rows anyway.  Negative absolute fetches are even worse:
+    the query must be read to the end to find the last row, and then
+    traversed backward from there.  However, rewinding to the start of the
+    query (as with FETCH ABSOLUTE 0) is fast.
    </para>
 
    <para>
@@ -316,7 +395,7 @@ FETCH FORWARD 5 IN liahona;
 </computeroutput>
 
 -- Fetch previous row:
-FETCH BACKWARD 1 IN liahona;
+FETCH PRIOR FROM liahona;
 
 <computeroutput>
  code  | title   | did | date_prod  | kind   | len
@@ -339,52 +418,39 @@ COMMIT WORK;
 
   <refsect2 id="R2-SQL-FETCH-4">
    <refsect2info>
-    <date>1998-09-01</date>
+    <date>2003-03-11</date>
    </refsect2info>
    <title>
     SQL92
    </title>
 
    <para>
-    <note>
-     <para>
-      The non-embedded use of cursors is a <productname>PostgreSQL</productname>
-      extension. The syntax and usage of cursors is being compared
-      against the embedded form of cursors defined in <acronym>SQL92</acronym>.
-     </para>
-    </note>
-   </para>
-
-   <para>
-    <acronym>SQL92</acronym> allows absolute positioning of the cursor for
-    FETCH, and allows placing the results into explicit variables:
+    <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:
 
     <synopsis>
-FETCH ABSOLUTE <replaceable class="PARAMETER">#</replaceable>
+FETCH ABSOLUTE <replaceable class="PARAMETER">n</replaceable>
     FROM <replaceable class="PARAMETER">cursor</replaceable>
     INTO :<replaceable class="PARAMETER">variable</replaceable> [, ...]
     </synopsis>
 
-    <variablelist>
-     <varlistentry>
-      <term>ABSOLUTE</term>
-      <listitem>
-       <para>
-       The cursor should be positioned to the specified absolute
-       row number. All row numbers in <productname>PostgreSQL</productname>
-       are relative numbers so this capability is not supported.
-       </para>
-      </listitem>
-     </varlistentry>
-     <varlistentry>
-      <term>:<replaceable class="PARAMETER">variable</replaceable></term>
-      <listitem>
-       <para>
-       Target host variable(s).
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    <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
+    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>
+    extensions.
+   </para>
+
+   <para>
+    <acronym>SQL92</acronym> allows only <literal>FROM</> preceding the
+    cursor name; the option to use <literal>IN</> is an extension.
    </para>
   </refsect2>
  </refsect1>
index 928faab..f01ee9d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.19 2003/03/10 03:53:49 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.20 2003/03/11 19:40:22 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -21,7 +21,7 @@ PostgreSQL documentation
    <date>1999-07-20</date>
   </refsynopsisdivinfo>
   <synopsis>
-MOVE [ <replaceable class="PARAMETER">direction</replaceable> ] [ <replaceable class="PARAMETER">count</replaceable> ] { IN | FROM } <replaceable class="PARAMETER">cursor</replaceable>
+MOVE [ <replaceable class="PARAMETER">direction</replaceable> { FROM | IN } ] <replaceable class="PARAMETER">cursor</replaceable>
   </synopsis>
  </refsynopsisdiv>
 
@@ -33,9 +33,7 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> ] [ <replaceable c
    Description
   </title>
   <para>
-   <command>MOVE</command> allows the user to move the cursor position a 
-   specified number of rows, or to the beginning or end of the cursor.
-   <command>MOVE ALL</command> moves to the end of the cursor.
+   <command>MOVE</command> repositions a cursor without retrieving any data.
    <command>MOVE</command> works exactly like the <command>FETCH</command>
    command, except it only repositions the cursor and does not return rows.
   </para>
@@ -54,8 +52,9 @@ MOVE [ <replaceable class="PARAMETER">direction</replaceable> ] [ <replaceable c
    </title>
 
    <para>
-    <command>MOVE</command> is a <productname>PostgreSQL</productname>
-    language extension.
+    The count returned in <command>MOVE</command>'s status string is the
+    count of the number of rows that would have been returned by the
+    equivalent <command>FETCH</command> command.
    </para>
 
    <para>
@@ -119,9 +118,6 @@ COMMIT WORK;
    </title>
    <para>
     There is no <acronym>SQL92</acronym> <command>MOVE</command> statement. 
-    Instead, <acronym>SQL92</acronym> allows
-    one to <command>FETCH</command> rows from an absolute cursor position,
-    implicitly moving the cursor to the correct position.
    </para>
   </refsect2>
  </refsect1>
index 0621cdd..1ba7243 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.9 2003/03/10 03:53:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.10 2003/03/11 19:40:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "rewrite/rewriteHandler.h"
 
 
+static long DoRelativeFetch(Portal portal,
+                                                       bool forward,
+                                                       long count,
+                                                       CommandDest dest);
+static void DoPortalRewind(Portal portal);
 static Portal PreparePortal(char *portalName);
 
 
@@ -102,9 +107,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
  * PerformPortalFetch
  *             Execute SQL FETCH or MOVE command.
  *
- *     name: name of portal
- *     forward: forward or backward fetch?
- *     count: # of tuples to fetch (INT_MAX means "all"; 0 means "refetch")
+ *     stmt: parsetree node for command
  *     dest: where to send results
  *     completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
  *             in which to store a command completion status string.
@@ -112,9 +115,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
  * completionTag may be NULL if caller doesn't want a status string.
  */
 void
-PerformPortalFetch(char *name,
-                                  bool forward,
-                                  long count,
+PerformPortalFetch(FetchStmt *stmt,
                                   CommandDest dest,
                                   char *completionTag)
 {
@@ -123,48 +124,150 @@ PerformPortalFetch(char *name,
 
        /* initialize completion status in case of early exit */
        if (completionTag)
-               strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
-
-       /* sanity checks */
-       if (name == NULL)
-       {
-               elog(WARNING, "PerformPortalFetch: missing portal name");
-               return;
-       }
+               strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
 
        /* get the portal from the portal name */
-       portal = GetPortalByName(name);
+       portal = GetPortalByName(stmt->portalname);
        if (!PortalIsValid(portal))
        {
                elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
-                        name);
+                        stmt->portalname);
                return;
        }
 
        /* Do it */
-       nprocessed = DoPortalFetch(portal, forward, count, dest);
+       nprocessed = DoPortalFetch(portal,
+                                                          stmt->direction,
+                                                          stmt->howMany,
+                                                          stmt->ismove ? None : dest);
 
        /* Return command status if wanted */
        if (completionTag)
                snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
-                                (dest == None) ? "MOVE" : "FETCH",
+                                stmt->ismove ? "MOVE" : "FETCH",
                                 nprocessed);
 }
 
 /*
  * DoPortalFetch
- *             Guts of PerformPortalFetch --- shared with SPI cursor operations
+ *             Guts of PerformPortalFetch --- shared with SPI cursor operations.
+ *             Caller must already have validated the Portal.
  *
- * Returns number of rows processed.
+ * Returns number of rows processed (suitable for use in result tag)
  */
 long
-DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
+DoPortalFetch(Portal portal,
+                         FetchDirection fdirection,
+                         long count,
+                         CommandDest dest)
 {
-       QueryDesc  *queryDesc;
-       EState     *estate;
-       MemoryContext oldcontext;
-       ScanDirection direction;
-       bool            temp_desc = false;
+       bool            forward;
+
+       switch (fdirection)
+       {
+               case FETCH_FORWARD:
+                       if (count < 0)
+                       {
+                               fdirection = FETCH_BACKWARD;
+                               count = -count;
+                       }
+                       /* fall out of switch to share code with FETCH_BACKWARD */
+                       break;
+               case FETCH_BACKWARD:
+                       if (count < 0)
+                       {
+                               fdirection = FETCH_FORWARD;
+                               count = -count;
+                       }
+                       /* fall out of switch to share code with FETCH_FORWARD */
+                       break;
+               case FETCH_ABSOLUTE:
+                       if (count > 0)
+                       {
+                               /*
+                                * Definition: Rewind to start, advance count-1 rows, return
+                                * next row (if any).  In practice, if the goal is less than
+                                * halfway back to the start, it's better to scan from where
+                                * we are.  In any case, we arrange to fetch the target row
+                                * going forwards.
+                                */
+                               if (portal->posOverflow || portal->portalPos == LONG_MAX ||
+                                       count-1 <= portal->portalPos / 2)
+                               {
+                                       DoPortalRewind(portal);
+                                       if (count > 1)
+                                               DoRelativeFetch(portal, true, count-1, None);
+                               }
+                               else
+                               {
+                                       long            pos = portal->portalPos;
+
+                                       if (portal->atEnd)
+                                               pos++;  /* need one extra fetch if off end */
+                                       if (count <= pos)
+                                               DoRelativeFetch(portal, false, pos-count+1, None);
+                                       else if (count > pos+1)
+                                               DoRelativeFetch(portal, true, count-pos-1, None);
+                               }
+                               return DoRelativeFetch(portal, true, 1L, dest);
+                       }
+                       else if (count < 0)
+                       {
+                               /*
+                                * Definition: Advance to end, back up abs(count)-1 rows,
+                                * return prior row (if any).  We could optimize this if we
+                                * knew in advance where the end was, but typically we won't.
+                                * (Is it worth considering case where count > half of size
+                                * of query?  We could rewind once we know the size ...)
+                                */
+                               DoRelativeFetch(portal, true, FETCH_ALL, None);
+                               if (count < -1)
+                                       DoRelativeFetch(portal, false, -count-1, None);
+                               return DoRelativeFetch(portal, false, 1L, dest);
+                       }
+                       else /* count == 0 */
+                       {
+                               /* Rewind to start, return zero rows */
+                               DoPortalRewind(portal);
+                               return DoRelativeFetch(portal, true, 0L, dest);
+                       }
+                       break;
+               case FETCH_RELATIVE:
+                       if (count > 0)
+                       {
+                               /*
+                                * Definition: advance count-1 rows, return next row (if any).
+                                */
+                               if (count > 1)
+                                       DoRelativeFetch(portal, true, count-1, None);
+                               return DoRelativeFetch(portal, true, 1L, dest);
+                       }
+                       else if (count < 0)
+                       {
+                               /*
+                                * Definition: back up abs(count)-1 rows, return prior row
+                                * (if any).
+                                */
+                               if (count < -1)
+                                       DoRelativeFetch(portal, false, -count-1, None);
+                               return DoRelativeFetch(portal, false, 1L, dest);
+                       }
+                       else /* count == 0 */
+                       {
+                               /* Same as FETCH FORWARD 0, so fall out of switch */
+                               fdirection = FETCH_FORWARD;
+                       }
+                       break;
+               default:
+                       elog(ERROR, "DoPortalFetch: bogus direction");
+                       break;
+       }
+
+       /*
+        * Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD,
+        * and count >= 0.
+        */
+       forward = (fdirection == FETCH_FORWARD);
 
        /*
         * Zero count means to re-fetch the current row, if any (per SQL92)
@@ -174,7 +277,7 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
                bool    on_row;
 
                /* Are we sitting on a row? */
-               on_row = (portal->atStart == false && portal->atEnd == false);
+               on_row = (!portal->atStart && !portal->atEnd);
 
                if (dest == None)
                {
@@ -187,14 +290,12 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
                         * If we are sitting on a row, back up one so we can re-fetch it.
                         * If we are not sitting on a row, we still have to start up and
                         * shut down the executor so that the destination is initialized
-                        * and shut down correctly; so keep going.  Further down in the
-                        * routine, count == 0 means we will retrieve no row.
+                        * and shut down correctly; so keep going.  To DoRelativeFetch,
+                        * count == 0 means we will retrieve no row.
                         */
                        if (on_row)
                        {
-                               DoPortalFetch(portal,
-                                                         false /* backward */, 1L,
-                                                         None /* throw away output */);
+                               DoRelativeFetch(portal, false, 1L, None);
                                /* Set up to fetch one row forward */
                                count = 1;
                                forward = true;
@@ -203,9 +304,44 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
        }
 
        /*
-        * switch into the portal context
+        * Optimize MOVE BACKWARD ALL into a Rewind.
         */
-       oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+       if (!forward && count == FETCH_ALL && dest == None)
+       {
+               long    result = portal->portalPos;
+
+               if (result > 0 && !portal->atEnd)
+                       result--;
+               DoPortalRewind(portal);
+               /* result is bogus if pos had overflowed, but it's best we can do */
+               return result;
+       }
+
+       return DoRelativeFetch(portal, forward, count, dest);
+}
+
+/*
+ * DoRelativeFetch
+ *             Do fetch for a simple N-rows-forward-or-backward case.
+ *
+ * count <= 0 is interpreted as a no-op: the destination gets started up
+ * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
+ * interpreted as "all rows".
+ *
+ * Caller must already have validated the Portal.
+ *
+ * Returns number of rows processed (suitable for use in result tag)
+ */
+static long
+DoRelativeFetch(Portal portal,
+                               bool forward,
+                               long count,
+                               CommandDest dest)
+{
+       QueryDesc  *queryDesc;
+       EState     *estate;
+       ScanDirection direction;
+       QueryDesc       temp_queryDesc;
 
        queryDesc = PortalGetQueryDesc(portal);
        estate = queryDesc->estate;
@@ -224,12 +360,9 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
        if (dest != queryDesc->dest &&
                !(queryDesc->dest == RemoteInternal && dest == Remote))
        {
-               QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
-
-               memcpy(qdesc, queryDesc, sizeof(QueryDesc));
-               qdesc->dest = dest;
-               queryDesc = qdesc;
-               temp_desc = true;
+               memcpy(&temp_queryDesc, queryDesc, sizeof(QueryDesc));
+               temp_queryDesc.dest = dest;
+               queryDesc = &temp_queryDesc;
        }
 
        /*
@@ -240,65 +373,101 @@ DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
         * robust about being called again if they've already returned NULL
         * once.)  Then call the executor (we must not skip this, because the
         * destination needs to see a setup and shutdown even if no tuples are
-        * available).  Finally, update the atStart/atEnd state depending on
+        * available).  Finally, update the portal position state depending on
         * the number of tuples that were retrieved.
         */
        if (forward)
        {
-               if (portal->atEnd || count == 0)
+               if (portal->atEnd || count <= 0)
                        direction = NoMovementScanDirection;
                else
                        direction = ForwardScanDirection;
 
-               /* In the executor, zero count processes all portal rows */
-               if (count == INT_MAX)
+               /* In the executor, zero count processes all rows */
+               if (count == FETCH_ALL)
                        count = 0;
 
                ExecutorRun(queryDesc, direction, count);
 
                if (direction != NoMovementScanDirection)
                {
+                       long    oldPos;
+
                        if (estate->es_processed > 0)
-                               portal->atStart = false;        /* OK to back up now */
-                       if (count <= 0 || (long) estate->es_processed < count)
+                               portal->atStart = false;        /* OK to go backward now */
+                       if (count == 0 ||
+                               (unsigned long) estate->es_processed < (unsigned long) count)
                                portal->atEnd = true;           /* we retrieved 'em all */
+                       oldPos = portal->portalPos;
+                       portal->portalPos += estate->es_processed;
+                       /* portalPos doesn't advance when we fall off the end */
+                       if (portal->portalPos < oldPos)
+                               portal->posOverflow = true;
                }
        }
        else
        {
                if (!portal->backwardOK)
-                       elog(ERROR, "Cursor cannot scan backwards"
+                       elog(ERROR, "Cursor can only scan forward"
                                 "\n\tDeclare it with SCROLL option to enable backward scan");
 
-               if (portal->atStart || count == 0)
+               if (portal->atStart || count <= 0)
                        direction = NoMovementScanDirection;
                else
                        direction = BackwardScanDirection;
 
-               /* In the executor, zero count processes all portal rows */
-               if (count == INT_MAX)
+               /* In the executor, zero count processes all rows */
+               if (count == FETCH_ALL)
                        count = 0;
 
                ExecutorRun(queryDesc, direction, count);
 
                if (direction != NoMovementScanDirection)
                {
-                       if (estate->es_processed > 0)
+                       if (estate->es_processed > 0 && portal->atEnd)
+                       {
                                portal->atEnd = false;          /* OK to go forward now */
-                       if (count <= 0 || (long) estate->es_processed < count)
+                               portal->portalPos++;            /* adjust for endpoint case */
+                       }
+                       if (count == 0 ||
+                               (unsigned long) estate->es_processed < (unsigned long) count)
+                       {
                                portal->atStart = true;         /* we retrieved 'em all */
+                               portal->portalPos = 0;
+                               portal->posOverflow = false;
+                       }
+                       else
+                       {
+                               long    oldPos;
+
+                               oldPos = portal->portalPos;
+                               portal->portalPos -= estate->es_processed;
+                               if (portal->portalPos > oldPos ||
+                                       portal->portalPos <= 0)
+                                       portal->posOverflow = true;
+                       }
                }
        }
 
-       /*
-        * Clean up and switch back to old context.
-        */
-       if (temp_desc)
-               pfree(queryDesc);
+       return estate->es_processed;
+}
 
-       MemoryContextSwitchTo(oldcontext);
+/*
+ * DoPortalRewind - rewind a Portal to starting point
+ */
+static void
+DoPortalRewind(Portal portal)
+{
+       QueryDesc  *queryDesc;
 
-       return estate->es_processed;
+       queryDesc = PortalGetQueryDesc(portal);
+
+       ExecutorRewind(queryDesc);
+
+       portal->atStart = true;
+       portal->atEnd = false;
+       portal->portalPos = 0;
+       portal->posOverflow = false;
 }
 
 /*
@@ -311,15 +480,6 @@ PerformPortalClose(char *name)
        Portal          portal;
 
        /*
-        * sanity checks ... why is this case allowed by the grammar, anyway?
-        */
-       if (name == NULL)
-       {
-               elog(WARNING, "PerformPortalClose: missing portal name");
-               return;
-       }
-
-       /*
         * get the portal from the portal name
         */
        portal = GetPortalByName(name);
index f037e72..638cc61 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.201 2003/03/10 03:53:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.202 2003/03/11 19:40:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -287,6 +287,42 @@ ExecutorEnd(QueryDesc *queryDesc)
        queryDesc->planstate = NULL;
 }
 
+/* ----------------------------------------------------------------
+ *             ExecutorRewind
+ *
+ *             This routine may be called on an open queryDesc to rewind it
+ *             to the start.
+ * ----------------------------------------------------------------
+ */
+void
+ExecutorRewind(QueryDesc *queryDesc)
+{
+       EState     *estate;
+       MemoryContext oldcontext;
+
+       /* sanity checks */
+       Assert(queryDesc != NULL);
+
+       estate = queryDesc->estate;
+
+       Assert(estate != NULL);
+
+       /* It's probably not sensible to rescan updating queries */
+       Assert(queryDesc->operation == CMD_SELECT);
+
+       /*
+        * Switch into per-query memory context
+        */
+       oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+       /*
+        * rescan plan
+        */
+       ExecReScan(queryDesc->planstate, NULL);
+
+       MemoryContextSwitchTo(oldcontext);
+}
+
 
 /*
  * ExecCheckRTPerms
index e1ccdf0..61eed9b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.87 2003/03/10 03:53:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.88 2003/03/11 19:40:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1349,8 +1349,11 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
        _SPI_current->tuptable = NULL;
 
        /* Run the cursor */
-       _SPI_current->processed = DoPortalFetch(portal, forward, (long) count,
-                                                                                       dest);
+       _SPI_current->processed =
+               DoPortalFetch(portal,
+                                         forward ? FETCH_FORWARD : FETCH_BACKWARD,
+                                         (long) count,
+                                         dest);
 
        if (dest == SPI && _SPI_checktuples())
                elog(FATAL, "SPI_fetch: # of processed tuples check failed");
index 045d3cc..7b28bdc 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.405 2003/03/10 03:53:50 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.406 2003/03/11 19:40:23 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -191,7 +191,7 @@ static void doNegateFloat(Value *v);
 
 %type <range>  qualified_name OptConstrFromTable
 
-%type <str>            opt_id  all_Op MathOp opt_name SpecialRuleRelation
+%type <str>            all_Op MathOp opt_name SpecialRuleRelation
 
 %type <str>            iso_level opt_encoding
 %type <node>   grantee
@@ -248,12 +248,10 @@ static void doNegateFloat(Value *v);
 
 %type <boolean> copy_from
 
-%type <ival>   direction reindex_type drop_type
+%type <ival>   reindex_type drop_type fetch_count
                                opt_column event comment_type cursor_options
 
-%type <ival>   fetch_how_many
-
-%type <node>   select_limit_value select_offset_value
+%type <node>   fetch_direction select_limit_value select_offset_value
 
 %type <list>   OptSeqList
 %type <defelt> OptSeqElem
@@ -345,7 +343,7 @@ static void doNegateFloat(Value *v);
        EACH ELSE ENCODING ENCRYPTED END_P ESCAPE EXCEPT
        EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
 
-       FALSE_P FETCH FLOAT_P FOR FORCE FOREIGN FORWARD
+       FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
        FREEZE FROM FULL FUNCTION
 
        GLOBAL GRANT GROUP_P
@@ -361,7 +359,7 @@ static void doNegateFloat(Value *v);
 
        KEY
 
-       LANCOMPILER LANGUAGE LEADING LEFT LEVEL LIKE LIMIT
+       LANCOMPILER LANGUAGE LAST_P LEADING LEFT LEVEL LIKE LIMIT
        LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
        LOCK_P
 
@@ -1239,16 +1237,15 @@ opt_drop_behavior:
                ;
 
 
-
 /*****************************************************************************
  *
  *             QUERY :
- *                             close <optname>
+ *                             close <portalname>
  *
  *****************************************************************************/
 
 ClosePortalStmt:
-                       CLOSE opt_id
+                       CLOSE name
                                {
                                        ClosePortalStmt *n = makeNode(ClosePortalStmt);
                                        n->portalname = $2;
@@ -1256,10 +1253,6 @@ ClosePortalStmt:
                                }
                ;
 
-opt_id:        ColId                                                                   { $$ = $1; }
-                       | /*EMPTY*/                                                             { $$ = NULL; }
-               ;
-
 
 /*****************************************************************************
  *
@@ -2583,151 +2576,159 @@ comment_text:
 /*****************************************************************************
  *
  *             QUERY:
- *                     fetch/move [forward | backward] [ # | all ] [ in <portalname> ]
- *                     fetch [ forward | backward | absolute | relative ]
- *                               [ # | all | next | prior ] [ [ in | from ] <portalname> ]
+ *                     fetch/move
  *
  *****************************************************************************/
 
-FetchStmt:     FETCH direction fetch_how_many from_in name
+FetchStmt:     FETCH fetch_direction from_in name
                                {
-                                       FetchStmt *n = makeNode(FetchStmt);
-                                       if ($3 < 0)
-                                       {
-                                               $3 = -$3;
-                                               $2 = (($2 == FETCH_FORWARD) ? FETCH_BACKWARD : FETCH_FORWARD);
-                                       }
-                                       n->direction = $2;
-                                       n->howMany = $3;
-                                       n->portalname = $5;
+                                       FetchStmt *n = (FetchStmt *) $2;
+                                       n->portalname = $4;
                                        n->ismove = FALSE;
                                        $$ = (Node *)n;
                                }
-                       | FETCH fetch_how_many from_in name
+                       | FETCH name
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
-                                       if ($2 < 0)
-                                       {
-                                               n->howMany = -$2;
-                                               n->direction = FETCH_BACKWARD;
-                                       }
-                                       else
-                                       {
-                                               n->direction = FETCH_FORWARD;
-                                               n->howMany = $2;
-                                       }
-                                       n->portalname = $4;
+                                       n->direction = FETCH_FORWARD;
+                                       n->howMany = 1;
+                                       n->portalname = $2;
                                        n->ismove = FALSE;
                                        $$ = (Node *)n;
                                }
-                       | FETCH direction from_in name
+                       | MOVE fetch_direction from_in name
                                {
-                                       FetchStmt *n = makeNode(FetchStmt);
-                                       n->direction = $2;
-                                       n->howMany = 1;
+                                       FetchStmt *n = (FetchStmt *) $2;
                                        n->portalname = $4;
-                                       n->ismove = FALSE;
+                                       n->ismove = TRUE;
                                        $$ = (Node *)n;
                                }
-                       | FETCH from_in name
+                       | MOVE name
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
                                        n->direction = FETCH_FORWARD;
                                        n->howMany = 1;
-                                       n->portalname = $3;
-                                       n->ismove = FALSE;
+                                       n->portalname = $2;
+                                       n->ismove = TRUE;
                                        $$ = (Node *)n;
                                }
-                       | FETCH name
+               ;
+
+fetch_direction:
+                       /*EMPTY*/
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
                                        n->direction = FETCH_FORWARD;
                                        n->howMany = 1;
-                                       n->portalname = $2;
-                                       n->ismove = FALSE;
                                        $$ = (Node *)n;
                                }
-                       | MOVE direction fetch_how_many from_in name
+                       | NEXT
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
-                                       if ($3 < 0)
-                                       {
-                                               $3 = -$3;
-                                               $2 = (($2 == FETCH_FORWARD) ? FETCH_BACKWARD : FETCH_FORWARD);
-                                       }
-                                       n->direction = $2;
-                                       n->howMany = $3;
-                                       n->portalname = $5;
-                                       n->ismove = TRUE;
+                                       n->direction = FETCH_FORWARD;
+                                       n->howMany = 1;
                                        $$ = (Node *)n;
                                }
-                       | MOVE fetch_how_many from_in name
+                       | PRIOR
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
-                                       if ($2 < 0)
-                                       {
-                                               n->howMany = -$2;
-                                               n->direction = FETCH_BACKWARD;
-                                       }
-                                       else
-                                       {
-                                               n->direction = FETCH_FORWARD;
-                                               n->howMany = $2;
-                                       }
-                                       n->portalname = $4;
-                                       n->ismove = TRUE;
+                                       n->direction = FETCH_BACKWARD;
+                                       n->howMany = 1;
                                        $$ = (Node *)n;
                                }
-                       | MOVE direction from_in name
+                       | FIRST_P
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
-                                       n->direction = $2;
+                                       n->direction = FETCH_ABSOLUTE;
                                        n->howMany = 1;
-                                       n->portalname = $4;
-                                       n->ismove = TRUE;
                                        $$ = (Node *)n;
                                }
-                       | MOVE from_in name
+                       | LAST_P
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_ABSOLUTE;
+                                       n->howMany = -1;
+                                       $$ = (Node *)n;
+                               }
+                       | ABSOLUTE fetch_count
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_ABSOLUTE;
+                                       n->howMany = $2;
+                                       $$ = (Node *)n;
+                               }
+                       | RELATIVE fetch_count
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_RELATIVE;
+                                       n->howMany = $2;
+                                       $$ = (Node *)n;
+                               }
+                       | fetch_count
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_FORWARD;
+                                       n->howMany = $1;
+                                       $$ = (Node *)n;
+                               }
+                       | ALL
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_FORWARD;
+                                       n->howMany = FETCH_ALL;
+                                       $$ = (Node *)n;
+                               }
+                       | FORWARD
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
                                        n->direction = FETCH_FORWARD;
                                        n->howMany = 1;
-                                       n->portalname = $3;
-                                       n->ismove = TRUE;
                                        $$ = (Node *)n;
                                }
-                       | MOVE name
+                       | FORWARD fetch_count
                                {
                                        FetchStmt *n = makeNode(FetchStmt);
                                        n->direction = FETCH_FORWARD;
+                                       n->howMany = $2;
+                                       $$ = (Node *)n;
+                               }
+                       | FORWARD ALL
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_FORWARD;
+                                       n->howMany = FETCH_ALL;
+                                       $$ = (Node *)n;
+                               }
+                       | BACKWARD
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_BACKWARD;
                                        n->howMany = 1;
-                                       n->portalname = $2;
-                                       n->ismove = TRUE;
                                        $$ = (Node *)n;
                                }
-               ;
-
-direction:     FORWARD                                                                 { $$ = FETCH_FORWARD; }
-                       | BACKWARD                                                              { $$ = FETCH_BACKWARD; }
-                       | RELATIVE                                                              { $$ = FETCH_FORWARD; }
-                       | ABSOLUTE
+                       | BACKWARD fetch_count
+                               {
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_BACKWARD;
+                                       n->howMany = $2;
+                                       $$ = (Node *)n;
+                               }
+                       | BACKWARD ALL
                                {
-                                       elog(NOTICE,
-                                       "FETCH / ABSOLUTE not supported, using RELATIVE");
-                                       $$ = FETCH_FORWARD;
+                                       FetchStmt *n = makeNode(FetchStmt);
+                                       n->direction = FETCH_BACKWARD;
+                                       n->howMany = FETCH_ALL;
+                                       $$ = (Node *)n;
                                }
                ;
 
-fetch_how_many:
+fetch_count:
                        Iconst                                                                  { $$ = $1; }
                        | '-' Iconst                                                    { $$ = - $2; }
-                       | ALL                                                                   { $$ = INT_MAX; }
-                       | NEXT                                                                  { $$ = 1; }
-                       | PRIOR                                                                 { $$ = -1; }
                ;
 
-from_in:       IN_P                                                                    {}
-                       | FROM                                                                  {}
+from_in:       FROM                                                                    {}
+                       | IN_P                                                                  {}
                ;
 
 
@@ -7093,6 +7094,7 @@ unreserved_keyword:
                        | EXPLAIN
                        | EXTERNAL
                        | FETCH
+                       | FIRST_P
                        | FORCE
                        | FORWARD
                        | FUNCTION
@@ -7115,6 +7117,7 @@ unreserved_keyword:
                        | KEY
                        | LANCOMPILER
                        | LANGUAGE
+                       | LAST_P
                        | LEVEL
                        | LISTEN
                        | LOAD
@@ -7170,9 +7173,9 @@ unreserved_keyword:
                        | SCROLL
                        | SECOND_P
                        | SECURITY
-                       | SESSION
                        | SEQUENCE
                        | SERIALIZABLE
+                       | SESSION
                        | SET
                        | SHARE
                        | SHOW
@@ -7211,8 +7214,8 @@ unreserved_keyword:
                        | VOLATILE
                        | WITH
                        | WITHOUT
-                       | WRITE
                        | WORK
+                       | WRITE
                        | YEAR_P
                        | ZONE
                ;
index 727cff3..49432cb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.134 2003/02/10 04:44:46 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.135 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -128,6 +128,7 @@ static const ScanKeyword ScanKeywords[] = {
        {"extract", EXTRACT},
        {"false", FALSE_P},
        {"fetch", FETCH},
+       {"first", FIRST_P},
        {"float", FLOAT_P},
        {"for", FOR},
        {"force", FORCE},
@@ -171,6 +172,7 @@ static const ScanKeyword ScanKeywords[] = {
        {"key", KEY},
        {"lancompiler", LANCOMPILER},
        {"language", LANGUAGE},
+       {"last", LAST_P},
        {"leading", LEADING},
        {"left", LEFT},
        {"level", LEVEL},
index 0fae711..baf74aa 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.194 2003/03/10 03:53:51 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.195 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,15 +321,8 @@ ProcessUtility(Node *parsetree,
                        break;
 
                case T_FetchStmt:
-                       {
-                               FetchStmt  *stmt = (FetchStmt *) parsetree;
-
-                               PerformPortalFetch(stmt->portalname,
-                                                                  stmt->direction == FETCH_FORWARD,
-                                                                  stmt->howMany,
-                                                                  (stmt->ismove) ? None : dest,
-                                                                  completionTag);
-                       }
+                       PerformPortalFetch((FetchStmt *) parsetree, dest,
+                                                          completionTag);
                        break;
 
                        /*
index 654247d..66ee727 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.52 2003/03/10 03:53:51 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.53 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -167,10 +167,12 @@ PortalSetQuery(Portal portal,
        AssertArg(PortalIsValid(portal));
 
        portal->queryDesc = queryDesc;
-       portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
-       portal->atStart = true;         /* Allow fetch forward only, to start */
-       portal->atEnd = false;
        portal->cleanup = cleanup;
+       portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
+       portal->atStart = true;
+       portal->atEnd = false;          /* allow fetches */
+       portal->portalPos = 0;
+       portal->posOverflow = false;
 }
 
 /*
@@ -211,10 +213,12 @@ CreatePortal(const char *name)
 
        /* initialize portal query */
        portal->queryDesc = NULL;
-       portal->backwardOK = false;
-       portal->atStart = true;         /* disallow fetches until query is set */
-       portal->atEnd = true;
        portal->cleanup = NULL;
+       portal->backwardOK = false;
+       portal->atStart = true;
+       portal->atEnd = true;           /* disallow fetches until query is set */
+       portal->portalPos = 0;
+       portal->posOverflow = false;
 
        /* put portal in table */
        PortalHashTableInsert(portal);
index 3f2a422..74855dd 100644 (file)
@@ -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.5 2003/03/10 03:53:51 tgl Exp $
+ * $Id: portalcmds.h,v 1.6 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 extern void PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest);
 
-extern void PerformPortalFetch(char *name, bool forward, long count,
-                                  CommandDest dest, char *completionTag);
+extern void PerformPortalFetch(FetchStmt *stmt, CommandDest dest,
+                                                          char *completionTag);
 
-extern long DoPortalFetch(Portal portal, bool forward, long count,
+extern long DoPortalFetch(Portal portal,
+                                                 FetchDirection fdirection,
+                                                 long count,
                                                  CommandDest dest);
 
 extern void PerformPortalClose(char *name);
index 785d217..40a696e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.90 2003/03/10 03:53:51 tgl Exp $
+ * $Id: executor.h,v 1.91 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,6 +86,7 @@ extern void ExecutorStart(QueryDesc *queryDesc);
 extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
                        ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
+extern void ExecutorRewind(QueryDesc *queryDesc);
 extern void ExecCheckRTPerms(List *rangeTable, CmdType operation);
 extern void ExecEndPlan(PlanState *planstate, EState *estate);
 extern void ExecConstraints(const char *caller, ResultRelInfo *resultRelInfo,
index c84348d..216ed04 100644 (file)
@@ -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.232 2003/03/10 03:53:51 tgl Exp $
+ * $Id: parsenodes.h,v 1.233 2003/03/11 19:40:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1228,16 +1228,21 @@ typedef struct ClosePortalStmt
  */
 typedef enum FetchDirection
 {
+       /* for these, howMany is how many rows to fetch; FETCH_ALL means ALL */
        FETCH_FORWARD,
-       FETCH_BACKWARD
-       /* ABSOLUTE someday? */
+       FETCH_BACKWARD,
+       /* for these, howMany indicates a position; only one row is fetched */
+       FETCH_ABSOLUTE,
+       FETCH_RELATIVE
 } FetchDirection;
 
+#define FETCH_ALL      LONG_MAX
+
 typedef struct FetchStmt
 {
        NodeTag         type;
        FetchDirection direction;       /* see above */
-       long            howMany;                /* number of rows */
+       long            howMany;                /* number of rows, or position argument */
        char       *portalname;         /* name of portal (cursor) */
        bool            ismove;                 /* TRUE if MOVE */
 } FetchStmt;
index 21469dd..c9ca854 100644 (file)
@@ -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.38 2003/03/10 03:53:52 tgl Exp $
+ * $Id: portal.h,v 1.39 2003/03/11 19:40:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,10 +27,21 @@ typedef struct PortalData
        char       *name;                       /* Portal's name */
        MemoryContext heap;                     /* subsidiary memory */
        QueryDesc  *queryDesc;          /* Info about query associated with portal */
-       bool            backwardOK;             /* is fetch backwards allowed at all? */
-       bool            atStart;                /* T => fetch backwards is not allowed now */
-       bool            atEnd;                  /* T => fetch forwards is not allowed now */
        void            (*cleanup) (Portal);    /* Cleanup routine (optional) */
+       bool            backwardOK;             /* is fetch backwards allowed? */
+       /*
+        * atStart, atEnd and portalPos indicate the current cursor position.
+        * portalPos is zero before the first row, N after fetching N'th row of
+        * query.  After we run off the end, portalPos = # of rows in query, and
+        * atEnd is true.  If portalPos overflows, set posOverflow (this causes
+        * us to stop relying on its value for navigation).  Note that atStart
+        * implies portalPos == 0, but not the reverse (portalPos could have
+        * overflowed).
+        */
+       bool            atStart;
+       bool            atEnd;
+       bool            posOverflow;
+       long            portalPos;
 } PortalData;
 
 /*