Restructure parsetree representation of DECLARE CURSOR: now it's a
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 10 Mar 2003 03:53:52 +0000 (03:53 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 10 Mar 2003 03:53:52 +0000 (03:53 +0000)
utility statement (DeclareCursorStmt) with a SELECT query dangling from
it, rather than a SELECT query with a few unusual fields in it.  Add
code to determine whether a planned query can safely be run backwards.
If DECLARE CURSOR specifies SCROLL, ensure that the plan can be run
backwards by adding a Materialize plan node if it can't.  Without SCROLL,
you get an error if you try to fetch backwards from a cursor that can't
handle it.  (There is still some discussion about what the exact
behavior should be, but this is necessary infrastructure in any case.)
Along the way, make EXPLAIN DECLARE CURSOR work.

40 files changed:
doc/src/sgml/ref/declare.sgml
doc/src/sgml/ref/explain.sgml
doc/src/sgml/ref/fetch.sgml
doc/src/sgml/ref/move.sgml
doc/src/sgml/ref/prepare.sgml
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/spi.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_type.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/backend/utils/mmgr/portalmem.c
src/include/catalog/catversion.h
src/include/commands/portalcmds.h
src/include/executor/executor.h
src/include/executor/spi.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/optimizer/planmain.h
src/include/optimizer/planner.h
src/include/tcop/pquery.h
src/include/utils/portal.h

index 77c45d90b09e409d6e00bd6ee2b19cca332d5461..5f4812200710d73f48e081907b65437d05cd909f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.18 2002/05/18 15:44:47 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/declare.sgml,v 1.19 2003/03/10 03:53:48 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -47,8 +47,7 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term>BINARY</term>
       <listitem>
        <para>
-   Causes the cursor to fetch data in binary
-   rather than in text format.
+   Causes the cursor to return data in binary rather than in text format.
        </para>
       </listitem>
      </varlistentry>
@@ -70,9 +69,8 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term>SCROLL</term>
       <listitem>
        <para>
-   <acronym>SQL92</acronym> keyword indicating that data may be retrieved
-   in multiple rows per FETCH operation. Since this is allowed at all times
-   by <productname>PostgreSQL</productname> this keyword has no effect.
+   Specifies that the cursor may be used to retrieve rows
+   in a nonsequential fashion (e.g., backwards).
        </para>
       </listitem>
      </varlistentry>
@@ -81,10 +79,10 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
       <term><replaceable class="parameter">query</replaceable></term>
       <listitem>
        <para>
-   An SQL query which will provide the rows to be governed by the
-   cursor.
-   Refer to the SELECT statement for further information about
-   valid arguments.
+   A <command>SELECT</> query which will provide the rows to be
+   returned by the cursor.
+   Refer to <xref linkend="sql-select" endterm="sql-select-title">
+   for further information about valid arguments.
        </para>
       </listitem>
      </varlistentry>
@@ -126,6 +124,10 @@ DECLARE <replaceable class="parameter">cursorname</replaceable> [ BINARY ] [ INS
 
     </variablelist>
    </para>
+
+   <para>
+    The BINARY, INSENSITIVE, and SCROLL keywords may appear in any order.
+   </para>
   </refsect2>
 
   <refsect2 id="R2-SQL-DECLARE-2">
@@ -193,9 +195,8 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
   </para>
 
   <para>
-   Normal cursors return data in text format, either ASCII or another
-   encoding scheme depending on how the <productname>PostgreSQL</productname>
-   backend was built. Since
+   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.
@@ -228,15 +229,11 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    representations (e.g., <quote>big-endian</quote> versus <quote>little-endian</quote>),
    you will probably not want your data returned in
    binary format.
-   However, binary cursors may be a
-   little more efficient since there is less conversion overhead in
-   the server to client data transfer.
 
    <tip>
     <para>
-     If you intend to display the data in
-     ASCII,  getting it back in ASCII will save you some
-     effort on the client side.
+     If you intend to display the data as text, retrieving it in text form
+     will save you some effort on the client side.
     </para>
    </tip>
   </para>
@@ -250,7 +247,7 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    </title>
 
    <para>
-    Cursors are only available in transactions. Use to
+    Cursors are only available within transactions. Use
     <xref linkend="sql-begin" endterm="sql-begin-title">,
     <xref linkend="sql-commit" endterm="sql-commit-title">
     and
@@ -258,6 +255,15 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
     to define a transaction block.
    </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.
+   </para>
+
    <para>
     In <acronym>SQL92</acronym> cursors are only available in
     embedded <acronym>SQL</acronym> (<acronym>ESQL</acronym>) applications. 
index 14639cc268ce0e75c345f24aad7c63b21dca42d7..be812bebf294bc877040e21ab9120be86600a378 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.23 2003/02/02 23:46:37 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/explain.sgml,v 1.24 2003/03/10 03:53:49 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -56,7 +56,8 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="PARAMETER">query</replaceabl
       <listitem>
        <para>
    Any <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>,
-   <command>DELETE</>, or <command>EXECUTE</> query.
+   <command>DELETE</>, <command>EXECUTE</>,
+   or <command>DECLARE CURSOR</> query.
        </para>
       </listitem>
      </varlistentry>
index 4770545cb838379e82d62bb3d6b3bdcaa2f80e42..0452cf0144ffb0a76778ab8332835237d022c32a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.25 2003/02/04 11:23:58 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/fetch.sgml,v 1.26 2003/03/10 03:53:49 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -13,7 +13,7 @@ PostgreSQL documentation
    FETCH
   </refname>
   <refpurpose>
-   retrieve rows from a table using a cursor
+   retrieve rows from a query using a cursor
   </refpurpose>
  </refnamediv>
  <refsynopsisdiv>
@@ -66,7 +66,7 @@ FETCH [ FORWARD | BACKWARD | RELATIVE ] [ <replaceable class="PARAMETER">#</repl
      <term>RELATIVE</term>
      <listitem>
       <para>
-       Noise word for SQL92 compatibility.
+       Same as FORWARD; provided for SQL92 compatibility.
       </para>
      </listitem>
     </varlistentry>
@@ -247,13 +247,20 @@ WARNING:  FETCH/ABSOLUTE not supported, using RELATIVE
    </title>
 
    <para>
-    Note that the FORWARD, BACKWARD, and ALL keywords are
+    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.
+   </para>
+
+   <para>
+    The FORWARD, BACKWARD, and ALL keywords are
     <productname>PostgreSQL</productname> extensions.
     See below for details on compatibility issues.
    </para>
 
    <para>
-    Updating data in a cursor is not supported by 
+    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.
@@ -262,8 +269,7 @@ WARNING:  FETCH/ABSOLUTE not supported, using RELATIVE
    </para>
 
    <para>
-    Cursors may only be used inside of transactions because
-    the data that they store spans multiple user queries.
+    Cursors may only be used inside transaction blocks.
    </para>
 
    <para>
@@ -288,7 +294,7 @@ WARNING:  FETCH/ABSOLUTE not supported, using RELATIVE
   </title>
 
   <para>
-   The following examples traverses a table using a cursor.
+   The following example traverses a table using a cursor.
 
 <programlisting>
 -- Set up and use a cursor:
index 69be788c35c5fb12641f64c8b4e19db91c972faa..928faabc818bcee4c6696e2e5cf6842a0c3e513a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.18 2003/02/04 11:23:58 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/move.sgml,v 1.19 2003/03/10 03:53:49 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -88,13 +88,11 @@ DECLARE liahona CURSOR  FOR SELECT * FROM films;
 -- Skip first 5 rows:
 MOVE FORWARD 5 IN liahona;
 <computeroutput>
-MOVE
+MOVE 5
 </computeroutput>
 -- Fetch 6th row in the cursor liahona:
 FETCH 1 IN liahona;
 <computeroutput>
-FETCH
-
  code  | title  | did | date_prod | kind   | len
 -------+--------+-----+-----------+--------+-------
  P_303 | 48 Hrs | 103 | 1982-10-22| Action | 01:37
index 418bd83ace222495d9febd0f50ea2e240c5b69dc..754017309156856916c8852f697b3295058877d6 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/prepare.sgml,v 1.2 2003/02/02 23:46:37 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/prepare.sgml,v 1.3 2003/03/10 03:53:49 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -54,6 +54,15 @@ PostgreSQL documentation
        </para>
       </listitem>
      </varlistentry>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">query</replaceable></term>
+      <listitem>
+       <para>
+   Any <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>,
+   or <command>DELETE</> query.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </refsect2>
index 8bc39884b43dc53651d43e1d9b68ba9ca600f185..11425620e6b22974dc135fa23a3e8774a69e9b62 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.103 2003/02/10 17:06:23 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.104 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -85,7 +85,9 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
    if (query->commandType == CMD_UTILITY)
    {
        /* Rewriter will not cope with utility statements */
-       if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
+       if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
+           ExplainOneQuery(query, stmt, tstate);
+       else if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
            ExplainExecuteQuery(stmt, tstate);
        else
            do_text_output_oneline(tstate, "Utility statements have no plan structure");
@@ -125,30 +127,45 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 {
    Plan       *plan;
    QueryDesc  *queryDesc;
+   bool        isCursor = false;
+   int         cursorOptions = 0;
 
    /* planner will not cope with utility statements */
    if (query->commandType == CMD_UTILITY)
    {
-       if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
+       if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
+       {
+           DeclareCursorStmt *dcstmt;
+           List       *rewritten;
+
+           dcstmt = (DeclareCursorStmt *) query->utilityStmt;
+           query = (Query *) dcstmt->query;
+           isCursor = true;
+           cursorOptions = dcstmt->options;
+           /* Still need to rewrite cursor command */
+           Assert(query->commandType == CMD_SELECT);
+           rewritten = QueryRewrite(query);
+           if (length(rewritten) != 1)
+               elog(ERROR, "ExplainOneQuery: unexpected rewrite result");
+           query = (Query *) lfirst(rewritten);
+           Assert(query->commandType == CMD_SELECT);
+           /* do not actually execute the underlying query! */
+           stmt->analyze = false;
+       }
+       else if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
+       {
            do_text_output_oneline(tstate, "NOTIFY");
+           return;
+       }
        else
+       {
            do_text_output_oneline(tstate, "UTILITY");
-       return;
+           return;
+       }
    }
 
-   /*
-    * We don't support DECLARE CURSOR in EXPLAIN, but parser will take it
-    * because it's an OptimizableStmt
-    */
-   if (query->isPortal)
-       elog(ERROR, "EXPLAIN / DECLARE CURSOR is not supported");
-
    /* plan the query */
-   plan = planner(query);
-
-   /* pg_plan could have failed */
-   if (plan == NULL)
-       return;
+   plan = planner(query, isCursor, cursorOptions);
 
    /* Create a QueryDesc requesting no output */
    queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL,
index 5881fe6c582288f74b894de41d2389b433ab1f11..0621cdd59036df38dc9acffc72e457a5a3927db7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.8 2003/01/08 00:22:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.9 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "commands/portalcmds.h"
 #include "executor/executor.h"
+#include "optimizer/planner.h"
+#include "rewrite/rewriteHandler.h"
+
+
+static Portal PreparePortal(char *portalName);
 
 
 /*
- * PortalCleanup
- *
- * Clean up a portal when it's dropped.  Since this mainly exists to run
- * ExecutorEnd(), it should not be set as the cleanup hook until we have
- * called ExecutorStart() on the portal's query.
+ * PerformCursorOpen
+ *     Execute SQL DECLARE CURSOR command.
  */
 void
-PortalCleanup(Portal portal)
+PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
 {
+   List       *rewritten;
+   Query      *query;
+   Plan       *plan;
+   Portal      portal;
+   MemoryContext oldContext;
+   char       *cursorName;
+   QueryDesc  *queryDesc;
+
+   /* Check for invalid context (must be in transaction block) */
+   RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
+
    /*
-    * sanity checks
+    * The query has been through parse analysis, but not rewriting or
+    * planning as yet.  Note that the grammar ensured we have a SELECT
+    * query, so we are not expecting rule rewriting to do anything strange.
     */
-   AssertArg(PortalIsValid(portal));
-   AssertArg(portal->cleanup == PortalCleanup);
+   rewritten = QueryRewrite((Query *) stmt->query);
+   if (length(rewritten) != 1 || !IsA(lfirst(rewritten), Query))
+       elog(ERROR, "PerformCursorOpen: unexpected rewrite result");
+   query = (Query *) lfirst(rewritten);
+   if (query->commandType != CMD_SELECT)
+       elog(ERROR, "PerformCursorOpen: unexpected rewrite result");
+
+   if (query->into)
+       elog(ERROR, "DECLARE CURSOR may not specify INTO");
+   if (query->rowMarks != NIL)
+       elog(ERROR, "DECLARE/UPDATE is not supported"
+            "\n\tCursors must be READ ONLY");
+
+   plan = planner(query, true, stmt->options);
+
+   /* If binary cursor, switch to alternate output format */
+   if ((stmt->options & CURSOR_OPT_BINARY) && dest == Remote)
+       dest = RemoteInternal;
 
    /*
-    * tell the executor to shutdown the query
+    * Create a portal and copy the query and plan into its memory context.
     */
-   ExecutorEnd(PortalGetQueryDesc(portal));
+   portal = PreparePortal(stmt->portalname);
+
+   oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+   query = copyObject(query);
+   plan = copyObject(plan);
 
    /*
-    * This should be unnecessary since the querydesc should be in the
-    * portal's memory context, but do it anyway for symmetry.
+    * Create the QueryDesc object in the portal context, too.
     */
-   FreeQueryDesc(PortalGetQueryDesc(portal));
-}
+   cursorName = pstrdup(stmt->portalname);
+   queryDesc = CreateQueryDesc(query, plan, dest, cursorName, NULL, false);
+
+   /*
+    * call ExecStart to prepare the plan for execution
+    */
+   ExecutorStart(queryDesc);
 
+   /* Arrange to shut down the executor if portal is dropped */
+   PortalSetQuery(portal, queryDesc, PortalCleanup);
+
+   /*
+    * We're done; the query won't actually be run until PerformPortalFetch
+    * is called.
+    */
+   MemoryContextSwitchTo(oldContext);
+}
 
 /*
  * PerformPortalFetch
+ *     Execute SQL FETCH or MOVE command.
  *
  * name: name of portal
  * forward: forward or backward fetch?
@@ -70,28 +119,20 @@ PerformPortalFetch(char *name,
                   char *completionTag)
 {
    Portal      portal;
-   QueryDesc  *queryDesc;
-   EState     *estate;
-   MemoryContext oldcontext;
-   ScanDirection direction;
-   bool        temp_desc = false;
+   long        nprocessed;
 
    /* initialize completion status in case of early exit */
    if (completionTag)
        strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
 
-   /*
-    * sanity checks
-    */
+   /* sanity checks */
    if (name == NULL)
    {
        elog(WARNING, "PerformPortalFetch: missing portal name");
        return;
    }
 
-   /*
-    * get the portal from the portal name
-    */
+   /* get the portal from the portal name */
    portal = GetPortalByName(name);
    if (!PortalIsValid(portal))
    {
@@ -100,6 +141,31 @@ PerformPortalFetch(char *name,
        return;
    }
 
+   /* Do it */
+   nprocessed = DoPortalFetch(portal, forward, count, dest);
+
+   /* Return command status if wanted */
+   if (completionTag)
+       snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
+                (dest == None) ? "MOVE" : "FETCH",
+                nprocessed);
+}
+
+/*
+ * DoPortalFetch
+ *     Guts of PerformPortalFetch --- shared with SPI cursor operations
+ *
+ * Returns number of rows processed.
+ */
+long
+DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest)
+{
+   QueryDesc  *queryDesc;
+   EState     *estate;
+   MemoryContext oldcontext;
+   ScanDirection direction;
+   bool        temp_desc = false;
+
    /*
     * Zero count means to re-fetch the current row, if any (per SQL92)
     */
@@ -113,9 +179,7 @@ PerformPortalFetch(char *name,
        if (dest == None)
        {
            /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
-           if (completionTag && on_row)
-               strcpy(completionTag, "MOVE 1");
-           return;
+           return on_row ? 1L : 0L;
        }
        else
        {
@@ -128,9 +192,9 @@ PerformPortalFetch(char *name,
             */
            if (on_row)
            {
-               PerformPortalFetch(name, false /* backward */, 1L,
-                                  None, /* throw away output */
-                                  NULL /* do not modify the command tag */);
+               DoPortalFetch(portal,
+                             false /* backward */, 1L,
+                             None /* throw away output */);
                /* Set up to fetch one row forward */
                count = 1;
                forward = true;
@@ -202,6 +266,10 @@ PerformPortalFetch(char *name,
    }
    else
    {
+       if (!portal->backwardOK)
+           elog(ERROR, "Cursor cannot scan backwards"
+                "\n\tDeclare it with SCROLL option to enable backward scan");
+
        if (portal->atStart || count == 0)
            direction = NoMovementScanDirection;
        else
@@ -222,12 +290,6 @@ PerformPortalFetch(char *name,
        }
    }
 
-   /* Return command status if wanted */
-   if (completionTag)
-       snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
-                (dest == None) ? "MOVE" : "FETCH",
-                estate->es_processed);
-
    /*
     * Clean up and switch back to old context.
     */
@@ -235,18 +297,21 @@ PerformPortalFetch(char *name,
        pfree(queryDesc);
 
    MemoryContextSwitchTo(oldcontext);
+
+   return estate->es_processed;
 }
 
 /*
  * PerformPortalClose
+ *     Close a cursor.
  */
 void
-PerformPortalClose(char *name, CommandDest dest)
+PerformPortalClose(char *name)
 {
    Portal      portal;
 
    /*
-    * sanity checks
+    * sanity checks ... why is this case allowed by the grammar, anyway?
     */
    if (name == NULL)
    {
@@ -270,3 +335,64 @@ PerformPortalClose(char *name, CommandDest dest)
     */
    PortalDrop(portal);
 }
+
+
+/*
+ * PreparePortal
+ */
+static Portal
+PreparePortal(char *portalName)
+{
+   Portal      portal;
+
+   /*
+    * Check for already-in-use portal name.
+    */
+   portal = GetPortalByName(portalName);
+   if (PortalIsValid(portal))
+   {
+       /*
+        * XXX Should we raise an error rather than closing the old
+        * portal?
+        */
+       elog(WARNING, "Closing pre-existing portal \"%s\"",
+            portalName);
+       PortalDrop(portal);
+   }
+
+   /*
+    * Create the new portal.
+    */
+   portal = CreatePortal(portalName);
+
+   return portal;
+}
+
+
+/*
+ * PortalCleanup
+ *
+ * Clean up a portal when it's dropped.  Since this mainly exists to run
+ * ExecutorEnd(), it should not be set as the cleanup hook until we have
+ * called ExecutorStart() on the portal's query.
+ */
+void
+PortalCleanup(Portal portal)
+{
+   /*
+    * sanity checks
+    */
+   AssertArg(PortalIsValid(portal));
+   AssertArg(portal->cleanup == PortalCleanup);
+
+   /*
+    * tell the executor to shutdown the query
+    */
+   ExecutorEnd(PortalGetQueryDesc(portal));
+
+   /*
+    * This should be unnecessary since the querydesc should be in the
+    * portal's memory context, but do it anyway for symmetry.
+    */
+   FreeQueryDesc(PortalGetQueryDesc(portal));
+}
index b22ad7634982003f66f721dea37dc7bcf2079cd6..b189e3e94bc521d7d89168661dad9df103ef763d 100644 (file)
@@ -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/executor/execAmi.c,v 1.69 2003/02/09 00:30:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.70 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -281,3 +281,61 @@ ExecSupportsMarkRestore(NodeTag plantype)
 
    return false;
 }
+
+/*
+ * ExecSupportsBackwardScan - does a plan type support backwards scanning?
+ *
+ * Ideally, all plan types would support backwards scan, but that seems
+ * unlikely to happen soon.  In some cases, a plan node passes the backwards
+ * scan down to its children, and so supports backwards scan only if its
+ * children do.  Therefore, this routine must be passed a complete plan tree.
+ */
+bool
+ExecSupportsBackwardScan(Plan *node)
+{
+   if (node == NULL)
+       return false;
+
+   switch (nodeTag(node))
+   {
+       case T_Result:
+           if (outerPlan(node) != NULL)
+               return ExecSupportsBackwardScan(outerPlan(node));
+           else
+               return false;
+
+       case T_Append:
+       {
+           List   *l;
+
+           foreach(l, ((Append *) node)->appendplans)
+           {
+               if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
+                   return false;
+           }
+           return true;
+       }
+
+       case T_SeqScan:
+       case T_IndexScan:
+       case T_TidScan:
+       case T_FunctionScan:
+           return true;
+
+       case T_SubqueryScan:
+           return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
+
+       case T_Material:
+       case T_Sort:
+           return true;
+
+       case T_Unique:
+           return ExecSupportsBackwardScan(outerPlan(node));
+
+       case T_Limit:
+           return ExecSupportsBackwardScan(outerPlan(node));
+
+       default:
+           return false;
+   }
+}
index 9c4b6e748192b4bf28696b0a08dd3dc72cfc047f..f037e72fd911bba0fe2f458ab09cd35800750abf 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.200 2003/02/03 15:07:06 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.201 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,9 +537,7 @@ InitPlan(QueryDesc *queryDesc)
     */
    do_select_into = false;
 
-   if (operation == CMD_SELECT &&
-       !parseTree->isPortal &&
-       parseTree->into != NULL)
+   if (operation == CMD_SELECT && parseTree->into != NULL)
    {
        do_select_into = true;
        /*
index d94e12cde5619fd119d79b6b402eb7c4a6fd4b86..e1ccdf08f97425bb69540364c59eef749e15feed 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.86 2003/02/14 21:12:45 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.87 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -725,9 +725,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
 
    if (queryTree->commandType != CMD_SELECT)
        elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
-   if (queryTree->isPortal)
-       elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
-   else if (queryTree->into != NULL)
+   if (queryTree->into != NULL)
        elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
 
    /* Increment CommandCounter to see changes made by now */
@@ -764,20 +762,12 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
 
    /* Create the portal */
    portal = CreatePortal(name);
-   if (portal == NULL)
-       elog(ERROR, "failed to create portal \"%s\"", name);
 
    /* Switch to portals memory and copy the parsetree and plan to there */
    oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
    queryTree = copyObject(queryTree);
    planTree = copyObject(planTree);
 
-   /* Modify the parsetree to be a cursor */
-   queryTree->isPortal = true;
-   queryTree->into = makeNode(RangeVar);
-   queryTree->into->relname = pstrdup(name);
-   queryTree->isBinary = false;
-
    /* If the plan has parameters, set them up */
    if (spiplan->nargs > 0)
    {
@@ -812,7 +802,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
        paramLI = NULL;
 
    /* Create the QueryDesc object */
-   queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL,
+   queryDesc = CreateQueryDesc(queryTree, planTree, SPI, pstrdup(name),
                                paramLI, false);
 
    /* Start the executor */
@@ -1106,7 +1096,8 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
                    if (stmt->filename == NULL)
                        return SPI_ERROR_COPY;
                }
-               else if (IsA(queryTree->utilityStmt, ClosePortalStmt) ||
+               else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
+                        IsA(queryTree->utilityStmt, ClosePortalStmt) ||
                         IsA(queryTree->utilityStmt, FetchStmt))
                    return SPI_ERROR_CURSOR;
                else if (IsA(queryTree->utilityStmt, TransactionStmt))
@@ -1263,12 +1254,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
 static int
 _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
 {
-   Query      *parseTree = queryDesc->parsetree;
    int         operation = queryDesc->operation;
-   CommandDest dest = queryDesc->dest;
-   bool        isRetrieveIntoPortal = false;
-   bool        isRetrieveIntoRelation = false;
-   char       *intoName = NULL;
    int         res;
    Oid         save_lastoid;
 
@@ -1276,20 +1262,10 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
    {
        case CMD_SELECT:
            res = SPI_OK_SELECT;
-           if (parseTree->isPortal)
-           {
-               isRetrieveIntoPortal = true;
-               intoName = parseTree->into->relname;
-               parseTree->isBinary = false;    /* */
-
-               return SPI_ERROR_CURSOR;
-
-           }
-           else if (parseTree->into != NULL)   /* select into table */
+           if (queryDesc->parsetree->into != NULL) /* select into table */
            {
                res = SPI_OK_SELINTO;
-               isRetrieveIntoRelation = true;
-               queryDesc->dest = None; /* */
+               queryDesc->dest = None; /* don't output results anywhere */
            }
            break;
        case CMD_INSERT:
@@ -1315,14 +1291,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
 
    ExecutorStart(queryDesc);
 
-   /*
-    * Don't work currently --- need to rearrange callers so that we
-    * prepare the portal before doing ExecutorStart() etc. See
-    * pquery.c for the correct order of operations.
-    */
-   if (isRetrieveIntoPortal)
-       elog(FATAL, "SPI_select: retrieve into portal not implemented");
-
    ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
 
    _SPI_current->processed = queryDesc->estate->es_processed;
@@ -1334,7 +1302,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
            elog(FATAL, "SPI_select: # of processed tuples check failed");
    }
 
-   if (dest == SPI)
+   if (queryDesc->dest == SPI)
    {
        SPI_processed = _SPI_current->processed;
        SPI_lastoid = save_lastoid;
@@ -1367,12 +1335,6 @@ static void
 _SPI_cursor_operation(Portal portal, bool forward, int count,
                      CommandDest dest)
 {
-   QueryDesc  *querydesc;
-   EState     *estate;
-   MemoryContext oldcontext;
-   ScanDirection direction;
-   CommandDest olddest;
-
    /* Check that the portal is valid */
    if (!PortalIsValid(portal))
        elog(ERROR, "invalid portal in SPI cursor operation");
@@ -1386,53 +1348,9 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
    _SPI_current->processed = 0;
    _SPI_current->tuptable = NULL;
 
-   /* Switch to the portals memory context */
-   oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
-   querydesc = PortalGetQueryDesc(portal);
-   estate = querydesc->estate;
-
-   /* Save the queries command destination and set it to SPI (for fetch) */
-   /* or None (for move) */
-   olddest = querydesc->dest;
-   querydesc->dest = dest;
-
-   /* Run the executor like PerformPortalFetch and remember states */
-   if (forward)
-   {
-       if (portal->atEnd)
-           direction = NoMovementScanDirection;
-       else
-           direction = ForwardScanDirection;
-
-       ExecutorRun(querydesc, direction, (long) count);
-
-       if (estate->es_processed > 0)
-           portal->atStart = false;    /* OK to back up now */
-       if (count <= 0 || (int) estate->es_processed < count)
-           portal->atEnd = true;       /* we retrieved 'em all */
-   }
-   else
-   {
-       if (portal->atStart)
-           direction = NoMovementScanDirection;
-       else
-           direction = BackwardScanDirection;
-
-       ExecutorRun(querydesc, direction, (long) count);
-
-       if (estate->es_processed > 0)
-           portal->atEnd = false;      /* OK to go forward now */
-       if (count <= 0 || (int) estate->es_processed < count)
-           portal->atStart = true;     /* we retrieved 'em all */
-   }
-
-   _SPI_current->processed = estate->es_processed;
-
-   /* Restore the old command destination and switch back to callers */
-   /* memory context */
-   querydesc->dest = olddest;
-   MemoryContextSwitchTo(oldcontext);
+   /* Run the cursor */
+   _SPI_current->processed = DoPortalFetch(portal, forward, (long) count,
+                                           dest);
 
    if (dest == SPI && _SPI_checktuples())
        elog(FATAL, "SPI_fetch: # of processed tuples check failed");
index 627f62c84f8c27d6cc541ca11ce9b0657fd27e09..9ea51d589b5d05b00cc8d43581c337596c0ca001 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.245 2003/03/05 20:01:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.246 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1465,8 +1465,6 @@ _copyQuery(Query *from)
    COPY_NODE_FIELD(utilityStmt);
    COPY_SCALAR_FIELD(resultRelation);
    COPY_NODE_FIELD(into);
-   COPY_SCALAR_FIELD(isPortal);
-   COPY_SCALAR_FIELD(isBinary);
    COPY_SCALAR_FIELD(hasAggs);
    COPY_SCALAR_FIELD(hasSubLinks);
    COPY_NODE_FIELD(rtable);
@@ -1547,8 +1545,6 @@ _copySelectStmt(SelectStmt *from)
    COPY_NODE_FIELD(groupClause);
    COPY_NODE_FIELD(havingClause);
    COPY_NODE_FIELD(sortClause);
-   COPY_STRING_FIELD(portalname);
-   COPY_SCALAR_FIELD(binary);
    COPY_NODE_FIELD(limitOffset);
    COPY_NODE_FIELD(limitCount);
    COPY_NODE_FIELD(forUpdate);
@@ -1648,6 +1644,17 @@ _copyInsertDefault(InsertDefault *from)
    return newnode;
 }
 
+static DeclareCursorStmt *
+_copyDeclareCursorStmt(DeclareCursorStmt *from)
+{
+   DeclareCursorStmt *newnode = makeNode(DeclareCursorStmt);
+
+   COPY_STRING_FIELD(portalname);
+   COPY_SCALAR_FIELD(options);
+   COPY_NODE_FIELD(query);
+
+   return newnode;
+}
 
 static ClosePortalStmt *
 _copyClosePortalStmt(ClosePortalStmt *from)
@@ -2632,6 +2639,9 @@ copyObject(void *from)
        case T_GrantStmt:
            retval = _copyGrantStmt(from);
            break;
+       case T_DeclareCursorStmt:
+           retval = _copyDeclareCursorStmt(from);
+           break;
        case T_ClosePortalStmt:
            retval = _copyClosePortalStmt(from);
            break;
index 6a0f36600979976a32290728e51ef66745ec5dee..3dd552bdd523bcc6fdea845b135e5ce7cc9ae28f 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.188 2003/03/05 20:01:02 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.189 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -560,8 +560,6 @@ _equalQuery(Query *a, Query *b)
    COMPARE_NODE_FIELD(utilityStmt);
    COMPARE_SCALAR_FIELD(resultRelation);
    COMPARE_NODE_FIELD(into);
-   COMPARE_SCALAR_FIELD(isPortal);
-   COMPARE_SCALAR_FIELD(isBinary);
    COMPARE_SCALAR_FIELD(hasAggs);
    COMPARE_SCALAR_FIELD(hasSubLinks);
    COMPARE_NODE_FIELD(rtable);
@@ -631,8 +629,6 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
    COMPARE_NODE_FIELD(groupClause);
    COMPARE_NODE_FIELD(havingClause);
    COMPARE_NODE_FIELD(sortClause);
-   COMPARE_STRING_FIELD(portalname);
-   COMPARE_SCALAR_FIELD(binary);
    COMPARE_NODE_FIELD(limitOffset);
    COMPARE_NODE_FIELD(limitCount);
    COMPARE_NODE_FIELD(forUpdate);
@@ -718,6 +714,16 @@ _equalInsertDefault(InsertDefault *a, InsertDefault *b)
    return true;
 }
 
+static bool
+_equalDeclareCursorStmt(DeclareCursorStmt *a, DeclareCursorStmt *b)
+{
+   COMPARE_STRING_FIELD(portalname);
+   COMPARE_SCALAR_FIELD(options);
+   COMPARE_NODE_FIELD(query);
+
+   return true;
+}
+
 static bool
 _equalClosePortalStmt(ClosePortalStmt *a, ClosePortalStmt *b)
 {
@@ -1756,6 +1762,9 @@ equal(void *a, void *b)
        case T_GrantStmt:
            retval = _equalGrantStmt(a, b);
            break;
+       case T_DeclareCursorStmt:
+           retval = _equalDeclareCursorStmt(a, b);
+           break;
        case T_ClosePortalStmt:
            retval = _equalClosePortalStmt(a, b);
            break;
index 8485244492cab297276ac95276985fda06be313c..899b93e4727944bdce290cdbc4319d5b68d4561f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.200 2003/02/16 02:30:37 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.201 2003/03/10 03:53:49 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -1081,6 +1081,16 @@ _outNotifyStmt(StringInfo str, NotifyStmt *node)
    WRITE_NODE_FIELD(relation);
 }
 
+static void
+_outDeclareCursorStmt(StringInfo str, DeclareCursorStmt *node)
+{
+   WRITE_NODE_TYPE("DECLARECURSOR");
+
+   WRITE_STRING_FIELD(portalname);
+   WRITE_INT_FIELD(options);
+   WRITE_NODE_FIELD(query);
+}
+
 static void
 _outSelectStmt(StringInfo str, SelectStmt *node)
 {
@@ -1173,6 +1183,7 @@ _outQuery(StringInfo str, Query *node)
            case T_CreateStmt:
            case T_IndexStmt:
            case T_NotifyStmt:
+           case T_DeclareCursorStmt:
                WRITE_NODE_FIELD(utilityStmt);
                break;
            default:
@@ -1185,8 +1196,6 @@ _outQuery(StringInfo str, Query *node)
 
    WRITE_INT_FIELD(resultRelation);
    WRITE_NODE_FIELD(into);
-   WRITE_BOOL_FIELD(isPortal);
-   WRITE_BOOL_FIELD(isBinary);
    WRITE_BOOL_FIELD(hasAggs);
    WRITE_BOOL_FIELD(hasSubLinks);
    WRITE_NODE_FIELD(rtable);
@@ -1684,6 +1693,9 @@ _outNode(StringInfo str, void *obj)
            case T_NotifyStmt:
                _outNotifyStmt(str, obj);
                break;
+           case T_DeclareCursorStmt:
+               _outDeclareCursorStmt(str, obj);
+               break;
            case T_SelectStmt:
                _outSelectStmt(str, obj);
                break;
index 410d092c916404f0ab59cb232de9c31c8035898b..fb1ea0c94d095c8fe7e3c0ceada5d2d43dbb73eb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.149 2003/02/16 02:30:37 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.150 2003/03/10 03:53:49 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -198,8 +198,6 @@ _readQuery(void)
    READ_NODE_FIELD(utilityStmt);
    READ_INT_FIELD(resultRelation);
    READ_NODE_FIELD(into);
-   READ_BOOL_FIELD(isPortal);
-   READ_BOOL_FIELD(isBinary);
    READ_BOOL_FIELD(hasAggs);
    READ_BOOL_FIELD(hasSubLinks);
    READ_NODE_FIELD(rtable);
@@ -233,6 +231,21 @@ _readNotifyStmt(void)
    READ_DONE();
 }
 
+/*
+ * _readDeclareCursorStmt
+ */
+static DeclareCursorStmt *
+_readDeclareCursorStmt(void)
+{
+   READ_LOCALS(DeclareCursorStmt);
+
+   READ_STRING_FIELD(portalname);
+   READ_INT_FIELD(options);
+   READ_NODE_FIELD(query);
+
+   READ_DONE();
+}
+
 /*
  * _readSortClause
  */
@@ -894,8 +907,6 @@ parseNodeString(void)
 
    if (MATCH("QUERY", 5))
        return_value = _readQuery();
-   else if (MATCH("NOTIFY", 6))
-       return_value = _readNotifyStmt();
    else if (MATCH("SORTCLAUSE", 10))
        return_value = _readSortClause();
    else if (MATCH("GROUPCLAUSE", 11))
@@ -966,6 +977,10 @@ parseNodeString(void)
        return_value = _readExprFieldSelect();
    else if (MATCH("RTE", 3))
        return_value = _readRangeTblEntry();
+   else if (MATCH("NOTIFY", 6))
+       return_value = _readNotifyStmt();
+   else if (MATCH("DECLARECURSOR", 13))
+       return_value = _readDeclareCursorStmt();
    else
    {
        elog(ERROR, "badly formatted node string \"%.32s\"...", token);
index 99d979d57c07dd5fae16353531a5e0a877f5f1cd..0bf43cab24df73713e6cacc53221503d7184cea0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.98 2003/03/05 20:01:03 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.99 2003/03/10 03:53:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -343,8 +343,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
    }
 
    /* Generate the plan for the subquery */
-   rel->subplan = subquery_planner(subquery,
-                                   -1.0 /* default case */ );
+   rel->subplan = subquery_planner(subquery, 0.0 /* default case */ );
 
    /* Copy number of output rows from subplan */
    rel->tuples = rel->subplan->plan_rows;
index bfc11aa004662cd6de53f7fc69e2112c7e809b50..d01acdc6182afb172913718bc904bcc2cea1378c 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.137 2003/02/16 06:06:32 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.138 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1827,6 +1827,41 @@ make_material(List *tlist, Plan *lefttree)
    return node;
 }
 
+/*
+ * materialize_finished_plan: stick a Material node atop a completed plan
+ *
+ * There are a couple of places where we want to attach a Material node
+ * after completion of subquery_planner().  This currently requires hackery.
+ * Since subquery_planner has already run SS_finalize_plan on the subplan
+ * tree, we have to kluge up parameter lists for the Material node.
+ * Possibly this could be fixed by postponing SS_finalize_plan processing
+ * until setrefs.c is run?
+ */
+Plan *
+materialize_finished_plan(Plan *subplan)
+{
+   Plan       *matplan;
+   Path        matpath;        /* dummy for result of cost_material */
+
+   matplan = (Plan *) make_material(subplan->targetlist, subplan);
+
+   /* Set cost data */
+   cost_material(&matpath,
+                 subplan->total_cost,
+                 subplan->plan_rows,
+                 subplan->plan_width);
+   matplan->startup_cost = matpath.startup_cost;
+   matplan->total_cost = matpath.total_cost;
+   matplan->plan_rows = subplan->plan_rows;
+   matplan->plan_width = subplan->plan_width;
+
+   /* parameter kluge --- see comments above */
+   matplan->extParam = bms_copy(subplan->extParam);
+   matplan->allParam = bms_copy(subplan->allParam);
+
+   return matplan;
+}
+
 Agg *
 make_agg(Query *root, List *tlist, List *qual,
         AggStrategy aggstrategy,
index daa840f789e197f65a16da5208481dc23c6f6402..97f6b76a8e48c61dd12414cfb1bfafa5fce605c8 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.74 2003/01/20 18:54:52 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.75 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * indxpath.c need to see it.)
  *
  * tuple_fraction is interpreted as follows:
- *   0 (or less): expect all tuples to be retrieved (normal case)
+ *   0: expect all tuples to be retrieved (normal case)
  *   0 < tuple_fraction < 1: expect the given fraction of tuples available
  *     from the plan to be retrieved
  *   tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
  *     expected to be retrieved (ie, a LIMIT specification)
- * Note that while this routine and its subroutines treat a negative
- * tuple_fraction the same as 0, grouping_planner has a different
- * interpretation.
  *--------------------
  */
 void
index c7c072fe2eba3e02a979726469a2229154c51f3b..9fa78b8a237acad26dcd4510c16d764746f6edcf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.150 2003/03/05 20:01:03 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.151 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
+#include "executor/executor.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #ifdef OPTIMIZER_DEBUG
@@ -73,8 +74,9 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
  *
  *****************************************************************************/
 Plan *
-planner(Query *parse)
+planner(Query *parse, bool isCursor, int cursorOptions)
 {
+   double      tuple_fraction;
    Plan       *result_plan;
    Index       save_PlannerQueryLevel;
    List       *save_PlannerParamVar;
@@ -99,11 +101,38 @@ planner(Query *parse)
    PlannerQueryLevel = 0;      /* will be 1 in top-level subquery_planner */
    PlannerParamVar = NIL;
 
+   /* Determine what fraction of the plan is likely to be scanned */
+   if (isCursor)
+   {
+       /*
+        * We have no real idea how many tuples the user will ultimately
+        * FETCH from a cursor, but it seems a good bet that he
+        * doesn't want 'em all.  Optimize for 10% retrieval (you
+        * gotta better number?  Should this be a SETtable parameter?)
+        */
+       tuple_fraction = 0.10;
+   }
+   else
+   {
+       /* Default assumption is we need all the tuples */
+       tuple_fraction = 0.0;
+   }
+
    /* primary planning entry point (may recurse for subqueries) */
-   result_plan = subquery_planner(parse, -1.0 /* default case */ );
+   result_plan = subquery_planner(parse, tuple_fraction);
 
    Assert(PlannerQueryLevel == 0);
 
+   /*
+    * If creating a plan for a scrollable cursor, make sure it can
+    * run backwards on demand.  Add a Material node at the top at need.
+    */
+   if (isCursor && (cursorOptions & CURSOR_OPT_SCROLL))
+   {
+       if (!ExecSupportsBackwardScan(result_plan))
+           result_plan = materialize_finished_plan(result_plan);
+   }
+
    /* executor wants to know total number of Params used overall */
    result_plan->nParamExec = length(PlannerParamVar);
 
@@ -505,14 +534,11 @@ inheritance_planner(Query *parse, List *inheritlist)
  * tuple_fraction is the fraction of tuples we expect will be retrieved
  *
  * tuple_fraction is interpreted as follows:
- *   < 0: determine fraction by inspection of query (normal case)
- *   0: expect all tuples to be retrieved
+ *   0: expect all tuples to be retrieved (normal case)
  *   0 < tuple_fraction < 1: expect the given fraction of tuples available
  *     from the plan to be retrieved
  *   tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
  *     expected to be retrieved (ie, a LIMIT specification)
- * The normal case is to pass -1, but some callers pass values >= 0 to
- * override this routine's determination of the appropriate fraction.
  *
  * Returns a query plan.  Also, parse->query_pathkeys is returned as the
  * actual output ordering of the plan (in pathkey format).
@@ -693,29 +719,6 @@ grouping_planner(Query *parse, double tuple_fraction)
        else
            parse->query_pathkeys = NIL;
 
-       /*
-        * Figure out whether we expect to retrieve all the tuples that
-        * the plan can generate, or to stop early due to outside factors
-        * such as a cursor.  If the caller passed a value >= 0, believe
-        * that value, else do our own examination of the query context.
-        */
-       if (tuple_fraction < 0.0)
-       {
-           /* Initial assumption is we need all the tuples */
-           tuple_fraction = 0.0;
-
-           /*
-            * Check for retrieve-into-portal, ie DECLARE CURSOR.
-            *
-            * We have no real idea how many tuples the user will ultimately
-            * FETCH from a cursor, but it seems a good bet that he
-            * doesn't want 'em all.  Optimize for 10% retrieval (you
-            * gotta better number?  Should this be a SETtable parameter?)
-            */
-           if (parse->isPortal)
-               tuple_fraction = 0.10;
-       }
-
        /*
         * Adjust tuple_fraction if we see that we are going to apply
         * limiting/grouping/aggregation/etc.  This is not overridable by
index fc428977c3318281cecb27f357929f3bcdf52a31..417eecc1fe3fa8d87821e17bc73a0788e68a942d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.72 2003/02/09 06:56:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.73 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -222,7 +222,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
             slink->subLinkType == ANY_SUBLINK)
        tuple_fraction = 0.5;   /* 50% */
    else
-       tuple_fraction = -1.0;  /* default behavior */
+       tuple_fraction = 0.0;   /* default behavior */
 
    /*
     * Generate the plan for the subquery.
@@ -336,12 +336,6 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
         * is anything more complicated than a plain sequential scan, and we
         * do it even for seqscan if the qual appears selective enough to
         * eliminate many tuples.
-        *
-        * XXX It's pretty ugly to be inserting a MATERIAL node at this
-        * point.  Since subquery_planner has already run SS_finalize_plan
-        * on the subplan tree, we have to kluge up parameter lists for
-        * the MATERIAL node.  Possibly this could be fixed by postponing
-        * SS_finalize_plan processing until setrefs.c is run.
         */
        else if (node->parParam == NIL)
        {
@@ -380,23 +374,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
            }
            if (use_material)
            {
-               Plan       *matplan;
-               Path        matpath; /* dummy for result of cost_material */
-
-               matplan = (Plan *) make_material(plan->targetlist, plan);
-               /* need to calculate costs */
-               cost_material(&matpath,
-                             plan->total_cost,
-                             plan->plan_rows,
-                             plan->plan_width);
-               matplan->startup_cost = matpath.startup_cost;
-               matplan->total_cost = matpath.total_cost;
-               matplan->plan_rows = plan->plan_rows;
-               matplan->plan_width = plan->plan_width;
-               /* parameter kluge --- see comments above */
-               matplan->extParam = bms_copy(plan->extParam);
-               matplan->allParam = bms_copy(plan->allParam);
-               node->plan = plan = matplan;
+               node->plan = plan = materialize_finished_plan(plan);
            }
        }
 
index 207a813e8e019aefe1810260562019fe88333212..a265623bfb705284e1bf6d0cb202d57458df50a9 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.6 2003/02/10 17:08:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.7 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -362,8 +362,7 @@ is_simple_subquery(Query *subquery)
    if (!IsA(subquery, Query) ||
        subquery->commandType != CMD_SELECT ||
        subquery->resultRelation != 0 ||
-       subquery->into != NULL ||
-       subquery->isPortal)
+       subquery->into != NULL)
        elog(ERROR, "is_simple_subquery: subquery is bogus");
 
    /*
index f0a64f2980c240924a1d82fa65460bd1fcea2354..1782bcc88b953a1c5e34258bb8f13302aeb62349 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.91 2003/03/05 20:01:03 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.92 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,8 +134,7 @@ recurse_set_operations(Node *setOp, Query *parse,
        /*
         * Generate plan for primitive subquery
         */
-       subplan = subquery_planner(subquery,
-                                  -1.0 /* default case */ );
+       subplan = subquery_planner(subquery, 0.0 /* default case */ );
 
        /*
         * Add a SubqueryScan with the caller-requested targetlist
index 40e440a3754593fc9613dd1adbe475cfe53fbbd4..fcd583741893133c4b7b5cf31c15d749b6581761 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.130 2003/02/16 02:30:38 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.131 2003/03/10 03:53:50 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1787,7 +1787,6 @@ inline_function(Oid funcid, List *args, HeapTuple func_tuple,
        querytree->commandType != CMD_SELECT ||
        querytree->resultRelation != 0 ||
        querytree->into ||
-       querytree->isPortal ||
        querytree->hasAggs ||
        querytree->hasSubLinks ||
        querytree->rtable ||
index 2089008bbf55848ca25784f9491d8bef690291ba..3486a1e010b15358c7bb0cf7a40c4ce09b677699 100644 (file)
@@ -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.264 2003/02/13 22:50:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.265 2003/03/10 03:53:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -96,6 +96,8 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
 static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static Query *transformDeclareCursorStmt(ParseState *pstate,
+                                        DeclareCursorStmt *stmt);
 static Query *transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt);
 static Query *transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt);
 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
@@ -313,6 +315,11 @@ transformStmt(ParseState *pstate, Node *parseTree,
                                               (SelectStmt *) parseTree);
            break;
 
+       case T_DeclareCursorStmt:
+           result = transformDeclareCursorStmt(pstate,
+                                               (DeclareCursorStmt *) parseTree);
+           break;
+
        default:
 
            /*
@@ -445,7 +452,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 
        Assert(IsA(selectQuery, Query));
        Assert(selectQuery->commandType == CMD_SELECT);
-       if (selectQuery->into || selectQuery->isPortal)
+       if (selectQuery->into)
            elog(ERROR, "INSERT ... SELECT may not specify INTO");
 
        /*
@@ -1616,27 +1623,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
    qry->commandType = CMD_SELECT;
 
-   if (stmt->portalname)
-   {
-       /* DECLARE CURSOR */
-       if (stmt->into)
-           elog(ERROR, "DECLARE CURSOR must not specify INTO");
-       if (stmt->forUpdate)
-           elog(ERROR, "DECLARE/UPDATE is not supported"
-                "\n\tCursors must be READ ONLY");
-       qry->into = makeNode(RangeVar);
-       qry->into->relname = stmt->portalname;
-       qry->isPortal = TRUE;
-       qry->isBinary = stmt->binary;   /* internal portal */
-   }
-   else
-   {
-       /* SELECT */
-       qry->into = stmt->into;
-       qry->isPortal = FALSE;
-       qry->isBinary = FALSE;
-   }
-
    /* make FOR UPDATE clause available to addRangeTableEntry */
    pstate->p_forUpdate = stmt->forUpdate;
 
@@ -1646,6 +1632,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
    /* transform targetlist */
    qry->targetList = transformTargetList(pstate, stmt->targetList);
 
+   /* handle any SELECT INTO/CREATE TABLE AS spec */
+   qry->into = stmt->into;
    if (stmt->intoColNames)
        applyColumnNames(qry->targetList, stmt->intoColNames);
 
@@ -1708,8 +1696,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
    SetOperationStmt *sostmt;
    RangeVar   *into;
    List       *intoColNames;
-   char       *portalname;
-   bool        binary;
    List       *sortClause;
    Node       *limitOffset;
    Node       *limitCount;
@@ -1738,14 +1724,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
           leftmostSelect->larg == NULL);
    into = leftmostSelect->into;
    intoColNames = leftmostSelect->intoColNames;
-   portalname = stmt->portalname;
-   binary = stmt->binary;
 
    /* clear them to prevent complaints in transformSetOperationTree() */
    leftmostSelect->into = NULL;
    leftmostSelect->intoColNames = NIL;
-   stmt->portalname = NULL;
-   stmt->binary = false;
 
    /*
     * These are not one-time, exactly, but we want to process them here
@@ -1825,36 +1807,13 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
    }
 
    /*
-    * Insert one-time items into top-level query
+    * Handle SELECT INTO/CREATE TABLE AS.
     *
-    * This needs to agree with transformSelectStmt!
-    */
-   if (portalname)
-   {
-       /* DECLARE CURSOR */
-       if (into)
-           elog(ERROR, "DECLARE CURSOR must not specify INTO");
-       if (forUpdate)
-           elog(ERROR, "DECLARE/UPDATE is not supported"
-                "\n\tCursors must be READ ONLY");
-       qry->into = makeNode(RangeVar);
-       qry->into->relname = portalname;
-       qry->isPortal = TRUE;
-       qry->isBinary = binary; /* internal portal */
-   }
-   else
-   {
-       /* SELECT */
-       qry->into = into;
-       qry->isPortal = FALSE;
-       qry->isBinary = FALSE;
-   }
-
-   /*
     * Any column names from CREATE TABLE AS need to be attached to both
     * the top level and the leftmost subquery.  We do not do this earlier
     * because we do *not* want the targetnames list to be affected.
     */
+   qry->into = into;
    if (intoColNames)
    {
        applyColumnNames(qry->targetList, intoColNames);
@@ -1938,8 +1897,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
     */
    if (stmt->into)
        elog(ERROR, "INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT");
-   if (stmt->portalname)       /* should not happen */
-       elog(ERROR, "Portal may not appear in UNION/INTERSECT/EXCEPT");
    /* We don't support forUpdate with set ops at the moment. */
    if (stmt->forUpdate)
        elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
@@ -2327,6 +2284,27 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
    return qry;
 }
 
+static Query *
+transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
+{
+   Query      *result = makeNode(Query);
+   List       *extras_before = NIL,
+              *extras_after = NIL;
+
+   result->commandType = CMD_UTILITY;
+   result->utilityStmt = (Node *) stmt;
+
+   stmt->query = (Node *) transformStmt(pstate, stmt->query,
+                                        &extras_before, &extras_after);
+
+   /* Shouldn't get any extras, since grammar only allows SelectStmt */
+   if (extras_before || extras_after)
+       elog(ERROR, "transformDeclareCursorStmt: internal error");
+
+   return result;
+}
+
+
 static Query *
 transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
 {
index c593196dfc869179e570ac07071fed0a71a0a573..045d3cc2ca24f2e20713526823852a91a89b4b35 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.404 2003/02/16 02:30:38 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.405 2003/03/10 03:53:50 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -135,12 +135,12 @@ static void doNegateFloat(Value *v);
        CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt
        CreateSchemaStmt CreateSeqStmt CreateStmt
        CreateAssertStmt CreateTrigStmt CreateUserStmt
-       CreatedbStmt CursorStmt DefineStmt DeleteStmt
+       CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt
        DropGroupStmt DropOpClassStmt DropPLangStmt DropStmt
        DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt
        DropUserStmt DropdbStmt ExplainStmt FetchStmt
        GrantStmt IndexStmt InsertStmt ListenStmt LoadStmt
-       LockStmt NotifyStmt OptimizableStmt
+       LockStmt NotifyStmt ExplainableStmt PreparableStmt
        CreateFunctionStmt ReindexStmt RemoveAggrStmt
        RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt
        RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -241,7 +241,7 @@ static void doNegateFloat(Value *v);
 %type <ival>   opt_interval
 %type <node>   overlay_placing substr_from substr_for
 
-%type <boolean> opt_instead opt_cursor opt_analyze
+%type <boolean> opt_instead opt_analyze
 %type <boolean> index_opt_unique opt_verbose opt_full
 %type <boolean> opt_freeze opt_default opt_recheck
 %type <defelt> opt_binary opt_oids copy_delimiter
@@ -249,7 +249,7 @@ static void doNegateFloat(Value *v);
 %type <boolean> copy_from
 
 %type <ival>   direction reindex_type drop_type
-               opt_column event comment_type
+               opt_column event comment_type cursor_options
 
 %type <ival>   fetch_how_many
 
@@ -481,68 +481,72 @@ stmt :
            | AlterDomainStmt
            | AlterGroupStmt
            | AlterTableStmt
-           | AlterUserStmt
            | AlterUserSetStmt
+           | AlterUserStmt
+           | AnalyzeStmt
+           | CheckPointStmt
            | ClosePortalStmt
+           | ClusterStmt
+           | CommentStmt
+           | ConstraintsSetStmt
            | CopyStmt
-           | CreateStmt
            | CreateAsStmt
+           | CreateAssertStmt
            | CreateCastStmt
+           | CreateConversionStmt
            | CreateDomainStmt
            | CreateFunctionStmt
-           | CreateSchemaStmt
            | CreateGroupStmt
-           | CreateSeqStmt
            | CreateOpClassStmt
            | CreatePLangStmt
-           | CreateAssertStmt
+           | CreateSchemaStmt
+           | CreateSeqStmt
+           | CreateStmt
            | CreateTrigStmt
            | CreateUserStmt
-           | ClusterStmt
+           | CreatedbStmt
            | DeallocateStmt
+           | DeclareCursorStmt
            | DefineStmt
-           | DropStmt
-           | TruncateStmt
-           | CommentStmt
+           | DeleteStmt
+           | DropAssertStmt
            | DropCastStmt
            | DropGroupStmt
            | DropOpClassStmt
            | DropPLangStmt
-           | DropAssertStmt
-           | DropTrigStmt
            | DropRuleStmt
+           | DropStmt
+           | DropTrigStmt
            | DropUserStmt
+           | DropdbStmt
            | ExecuteStmt
            | ExplainStmt
            | FetchStmt
            | GrantStmt
            | IndexStmt
+           | InsertStmt
            | ListenStmt
-           | UnlistenStmt
+           | LoadStmt
            | LockStmt
            | NotifyStmt
            | PrepareStmt
            | ReindexStmt
            | RemoveAggrStmt
-           | RemoveOperStmt
            | RemoveFuncStmt
+           | RemoveOperStmt
            | RenameStmt
            | RevokeStmt
-           | OptimizableStmt
            | RuleStmt
+           | SelectStmt
            | TransactionStmt
-           | ViewStmt
-           | LoadStmt
-           | CreatedbStmt
-           | DropdbStmt
+           | TruncateStmt
+           | UnlistenStmt
+           | UpdateStmt
            | VacuumStmt
-           | AnalyzeStmt
+           | VariableResetStmt
            | VariableSetStmt
            | VariableShowStmt
-           | VariableResetStmt
-           | ConstraintsSetStmt
-           | CheckPointStmt
-           | CreateConversionStmt
+           | ViewStmt
            | /*EMPTY*/
                { $$ = (Node *)NULL; }
        ;
@@ -3961,16 +3965,7 @@ opt_name_list:
  *
  *****************************************************************************/
 
-ExplainStmt:
-           EXPLAIN opt_analyze opt_verbose OptimizableStmt
-               {
-                   ExplainStmt *n = makeNode(ExplainStmt);
-                   n->analyze = $2;
-                   n->verbose = $3;
-                   n->query = (Query*)$4;
-                   $$ = (Node *)n;
-               }
-           | EXPLAIN opt_analyze opt_verbose ExecuteStmt
+ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
                {
                    ExplainStmt *n = makeNode(ExplainStmt);
                    n->analyze = $2;
@@ -3980,6 +3975,15 @@ ExplainStmt:
                }
        ;
 
+ExplainableStmt:
+           SelectStmt
+           | InsertStmt
+           | UpdateStmt
+           | DeleteStmt
+           | DeclareCursorStmt
+           | ExecuteStmt                   /* by default all are $$=$1 */
+       ;
+
 opt_analyze:
            analyze_keyword         { $$ = TRUE; }
            | /* EMPTY */           { $$ = FALSE; }
@@ -3992,7 +3996,7 @@ opt_analyze:
  *
  *****************************************************************************/
 
-PrepareStmt: PREPARE name prep_type_clause AS OptimizableStmt
+PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt
                {
                    PrepareStmt *n = makeNode(PrepareStmt);
                    n->name = $2;
@@ -4011,6 +4015,13 @@ prep_type_list: Typename         { $$ = makeList1($1); }
                                    { $$ = lappend($1, $3); }
        ;
 
+PreparableStmt:
+           SelectStmt
+           | InsertStmt
+           | UpdateStmt
+           | DeleteStmt                    /* by default all are $$=$1 */
+       ;
+
 /*****************************************************************************
  *
  *     QUERY:
@@ -4053,26 +4064,6 @@ DeallocateStmt: DEALLOCATE name
                    }
        ;
 
-/*****************************************************************************
- *                                                                          *
- *     Optimizable Stmts:                                                   *
- *                                                                          *
- *     one of the five queries processed by the planner                     *
- *                                                                          *
- *     [ultimately] produces query-trees as specified                       *
- *     in the query-spec document in ~postgres/ref                          *
- *                                                                          *
- *****************************************************************************/
-
-OptimizableStmt:
-           SelectStmt
-           | CursorStmt
-           | UpdateStmt
-           | InsertStmt
-           | DeleteStmt                    /* by default all are $$=$1 */
-       ;
-
-
 /*****************************************************************************
  *
  *     QUERY:
@@ -4213,20 +4204,20 @@ UpdateStmt: UPDATE relation_expr
  *             CURSOR STATEMENTS
  *
  *****************************************************************************/
-CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt
+DeclareCursorStmt: DECLARE name cursor_options CURSOR FOR SelectStmt
                {
-                   SelectStmt *n = (SelectStmt *)$6;
+                   DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
                    n->portalname = $2;
-                   n->binary = $3;
-                   $$ = $6;
+                   n->options = $3;
+                   n->query = $6;
+                   $$ = (Node *)n;
                }
        ;
 
-opt_cursor: BINARY                                 { $$ = TRUE; }
-           | INSENSITIVE                           { $$ = FALSE; }
-           | SCROLL                                { $$ = FALSE; }
-           | INSENSITIVE SCROLL                    { $$ = FALSE; }
-           | /*EMPTY*/                             { $$ = FALSE; }
+cursor_options: /*EMPTY*/                  { $$ = 0; }
+           | cursor_options BINARY         { $$ = $1 | CURSOR_OPT_BINARY; }
+           | cursor_options SCROLL         { $$ = $1 | CURSOR_OPT_SCROLL; }
+           | cursor_options INSENSITIVE    { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
        ;
 
 /*****************************************************************************
index 33e7cce420390774765d89403e75935c6ed0222c..d4c13165e5a4ca158fe96459891c71bb57cda234 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.110 2003/02/16 02:30:38 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.111 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -414,7 +414,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 
    if (query->commandType != CMD_SELECT)
        elog(ERROR, "Expected SELECT query from subselect in FROM");
-   if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
+   if (query->resultRelation != 0 || query->into != NULL)
        elog(ERROR, "Subselect in FROM may not have SELECT INTO");
 
    /*
index e5f1e88ffdb7250633f8c575aff57525cf471972..1f0b7639d5ffc778616c6d8cdf7904489ec44e09 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.53 2003/02/19 23:41:15 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.54 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -455,7 +455,6 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
        stmt->groupClause != NIL ||
        stmt->havingClause != NULL ||
        stmt->sortClause != NIL ||
-       stmt->portalname != NULL ||
        stmt->limitOffset != NULL ||
        stmt->limitCount != NULL ||
        stmt->forUpdate != NIL ||
index b4f064d0e1b52c9e378bf36a6a5d866a79d3087f..ec717d8c122fbb0c5d6bd511de5e77864f4525c9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.316 2003/03/06 00:04:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.317 2003/03/10 03:53:51 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -483,7 +483,7 @@ pg_plan_query(Query *querytree)
        ResetUsage();
 
    /* call the optimizer */
-   plan = planner(querytree);
+   plan = planner(querytree, false, 0);
 
    if (log_planner_stats)
        ShowUsage("PLANNER STATISTICS");
@@ -1789,7 +1789,7 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.316 $ $Date: 2003/03/06 00:04:27 $\n");
+       puts("$Revision: 1.317 $ $Date: 2003/03/10 03:53:51 $\n");
    }
 
    /*
@@ -2245,6 +2245,10 @@ CreateCommandTag(Node *parsetree)
            }
            break;
 
+       case T_DeclareCursorStmt:
+           tag = "DECLARE CURSOR";
+           break;
+
        case T_ClosePortalStmt:
            tag = "CLOSE CURSOR";
            break;
index 1e02e42193df31e0a30307b0b173b0fa1153ef85..29d5018440dee68ec5f58644aefa0c75f321de73 100644 (file)
@@ -8,19 +8,15 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.58 2002/12/15 16:17:52 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.59 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "commands/portalcmds.h"
-#include "executor/execdefs.h"
 #include "executor/executor.h"
 #include "tcop/pquery.h"
-#include "utils/memutils.h"
-#include "utils/ps_status.h"
 
 
 /*
@@ -64,38 +60,6 @@ FreeQueryDesc(QueryDesc *qdesc)
    pfree(qdesc);
 }
 
-/* ----------------
- *     PreparePortal
- * ----------------
- */
-Portal
-PreparePortal(char *portalName)
-{
-   Portal      portal;
-
-   /*
-    * Check for already-in-use portal name.
-    */
-   portal = GetPortalByName(portalName);
-   if (PortalIsValid(portal))
-   {
-       /*
-        * XXX Should we raise an error rather than closing the old
-        * portal?
-        */
-       elog(WARNING, "Closing pre-existing portal \"%s\"",
-            portalName);
-       PortalDrop(portal);
-   }
-
-   /*
-    * Create the new portal.
-    */
-   portal = CreatePortal(portalName);
-
-   return portal;
-}
-
 
 /*
  * ProcessQuery
@@ -116,10 +80,6 @@ ProcessQuery(Query *parsetree,
             char *completionTag)
 {
    int         operation = parsetree->commandType;
-   bool        isRetrieveIntoPortal = false;
-   char       *intoName = NULL;
-   Portal      portal = NULL;
-   MemoryContext oldContext = NULL;
    QueryDesc  *queryDesc;
 
    /*
@@ -127,16 +87,7 @@ ProcessQuery(Query *parsetree,
     */
    if (operation == CMD_SELECT)
    {
-       if (parsetree->isPortal)
-       {
-           isRetrieveIntoPortal = true;
-           /* If binary portal, switch to alternate output format */
-           if (dest == Remote && parsetree->isBinary)
-               dest = RemoteInternal;
-           /* Check for invalid context (must be in transaction block) */
-           RequireTransactionChain((void *) parsetree, "DECLARE CURSOR");
-       }
-       else if (parsetree->into != NULL)
+       if (parsetree->into != NULL)
        {
            /*
             * SELECT INTO table (a/k/a CREATE AS ... SELECT).
@@ -150,57 +101,17 @@ ProcessQuery(Query *parsetree,
    }
 
    /*
-    * If retrieving into a portal, set up the portal and copy the
-    * parsetree and plan into its memory context.
+    * Create the QueryDesc object
     */
-   if (isRetrieveIntoPortal)
-   {
-       intoName = parsetree->into->relname;
-       portal = PreparePortal(intoName);
-       oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-       parsetree = copyObject(parsetree);
-       plan = copyObject(plan);
-       intoName = parsetree->into->relname;    /* use copied name in
-                                                * QueryDesc */
-
-       /*
-        * We stay in portal's memory context for now, so that query desc
-        * is also allocated in the portal context.
-        */
-   }
+   queryDesc = CreateQueryDesc(parsetree, plan, dest, NULL, NULL, false);
 
    /*
-    * Now we can create the QueryDesc object.
-    */
-   queryDesc = CreateQueryDesc(parsetree, plan, dest, intoName, NULL, false);
-
-   /*
-    * call ExecStart to prepare the plan for execution
+    * Call ExecStart to prepare the plan for execution
     */
    ExecutorStart(queryDesc);
 
    /*
-    * If retrieve into portal, stop now; we do not run the plan until a
-    * FETCH command is received.
-    */
-   if (isRetrieveIntoPortal)
-   {
-       /* Arrange to shut down the executor if portal is dropped */
-       PortalSetQuery(portal, queryDesc, PortalCleanup);
-
-       /* Now we can return to caller's memory context. */
-       MemoryContextSwitchTo(oldContext);
-
-       /* Set completion tag.  SQL calls this operation DECLARE CURSOR */
-       if (completionTag)
-           strcpy(completionTag, "DECLARE CURSOR");
-
-       return;
-   }
-
-   /*
-    * Now we get to the important call to ExecutorRun() where we actually
-    * run the plan..
+    * And run the plan.
     */
    ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 
index db7dc0945cd0c74e23cd3b3b3553f1e84577b81d..0fae711a2c320930aaae5fef4583e78929e48a06 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.193 2003/02/19 03:59:02 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.194 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -306,13 +306,17 @@ ProcessUtility(Node *parsetree,
            break;
 
            /*
-            * ************************* portal manipulation ***************************
+            * Portal (cursor) manipulation
             */
+       case T_DeclareCursorStmt:
+           PerformCursorOpen((DeclareCursorStmt *) parsetree, dest);
+           break;
+
        case T_ClosePortalStmt:
            {
                ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
 
-               PerformPortalClose(stmt->portalname, dest);
+               PerformPortalClose(stmt->portalname);
            }
            break;
 
index 520a55d64ae504cd45b975f1ed7df77ff0677b28..654247dd8c5a848f7157079065714ade58c1aa56 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.51 2002/12/30 22:10:54 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.52 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "executor/executor.h"
 #include "utils/hsearch.h"
 #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()
+ */
+#define PORTALS_PER_USER      64
+
+
 /* ----------------
  *     Global state
  * ----------------
  */
 
-#define MAX_PORTALNAME_LEN     64
+#define MAX_PORTALNAME_LEN     NAMEDATALEN
 
 typedef struct portalhashent
 {
@@ -158,7 +167,8 @@ PortalSetQuery(Portal portal,
    AssertArg(PortalIsValid(portal));
 
    portal->queryDesc = queryDesc;
-   portal->atStart = true;     /* Allow fetch forward only */
+   portal->backwardOK = ExecSupportsBackwardScan(queryDesc->plantree);
+   portal->atStart = true;     /* Allow fetch forward only, to start */
    portal->atEnd = false;
    portal->cleanup = cleanup;
 }
@@ -201,6 +211,7 @@ 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;
index fc24db5d2e1a455bb9c711067aa1fe942c4561ef..6344f0bae535cfb338c93edc7ae7e77bb26ac72d 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.179 2003/02/22 00:45:05 tgl Exp $
+ * $Id: catversion.h,v 1.180 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200302211
+#define CATALOG_VERSION_NO 200303091
 
 #endif
index d143423f6a32586d71879e142fab37b821e5ab75..3f2a4221add76007db73e7b07fc2ea6276649338 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.4 2002/12/30 15:31:50 momjian Exp $
+ * $Id: portalcmds.h,v 1.5 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "utils/portal.h"
 
-/*
- * PerformPortalFetch
- *     Performs the POSTQUEL function FETCH.  Fetches count
- * tuples in portal with name in the forward direction iff goForward.
- *
- * Exceptions:
- *     BadArg if forward invalid.
- *     "ERROR" if portal not found.
- */
+
+extern void PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest);
+
 extern void PerformPortalFetch(char *name, bool forward, long count,
                   CommandDest dest, char *completionTag);
 
-/*
- * PerformPortalClose
- *     Performs the POSTQUEL function CLOSE.
- */
-extern void PerformPortalClose(char *name, CommandDest dest);
+extern long DoPortalFetch(Portal portal, bool forward, long count,
+                         CommandDest dest);
+
+extern void PerformPortalClose(char *name);
 
 extern void PortalCleanup(Portal portal);
 
index ee30f51896974e74b279589bedf93f6437d9bb0f..785d21718b2b5328e8ddaaf39a6efecef73aab60 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.89 2003/02/09 00:30:39 tgl Exp $
+ * $Id: executor.h,v 1.90 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@ extern void ExecReScan(PlanState *node, ExprContext *exprCtxt);
 extern void ExecMarkPos(PlanState *node);
 extern void ExecRestrPos(PlanState *node);
 extern bool ExecSupportsMarkRestore(NodeTag plantype);
+extern bool ExecSupportsBackwardScan(Plan *node);
 
 /*
  * prototypes from functions in execGrouping.c
index 85a987b14b23bccfdd53820849af37ad0fd6647a..ead328da24758c18d916deef3bc24a68cdf75cc3 100644 (file)
@@ -2,7 +2,7 @@
  *
  * spi.h
  *
- * $Id: spi.h,v 1.35 2002/12/30 22:10:54 tgl Exp $
+ * $Id: spi.h,v 1.36 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "tcop/utility.h"
 #include "tcop/dest.h"
 #include "nodes/params.h"
+#include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/portal.h"
 #include "utils/syscache.h"
-#include "utils/builtins.h"
 #include "catalog/pg_language.h"
 #include "access/heapam.h"
 #include "access/xact.h"
index 356f4b60fa3726a7701a7bc669c3df35336aea47..3304ea89e4e5a1912c76338086c04b524ea3613e 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: nodes.h,v 1.137 2003/02/16 02:30:39 tgl Exp $
+ * $Id: nodes.h,v 1.138 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -248,6 +248,7 @@ typedef enum NodeTag
    T_PrepareStmt,
    T_ExecuteStmt,
    T_DeallocateStmt,
+   T_DeclareCursorStmt,
 
    T_A_Expr = 800,
    T_ColumnRef,
index 381c11c3893b72e0fa710150fdce4dccc5700f6a..c84348ded9e85383ed7ce95690866550420dc8b4 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.231 2003/02/16 02:30:39 tgl Exp $
+ * $Id: parsenodes.h,v 1.232 2003/03/10 03:53:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,9 +38,6 @@ typedef enum QuerySource
  *   for further processing by the optimizer
  *   utility statements (i.e. non-optimizable statements)
  *   have the *utilityStmt field set.
- *
- * we need the isPortal flag because portal names can be null too; can
- * get rid of it if we support CURSOR as a commandType.
  */
 typedef struct Query
 {
@@ -54,10 +51,8 @@ typedef struct Query
                                 * statement */
 
    int         resultRelation; /* target relation (index into rtable) */
-   RangeVar   *into;           /* target relation or portal (cursor) for
-                                * portal just name is meaningful */
-   bool        isPortal;       /* is this a retrieve into portal? */
-   bool        isBinary;       /* binary portal? */
+
+   RangeVar   *into;           /* target relation for SELECT INTO */
 
    bool        hasAggs;        /* has aggregates in tlist or havingQual */
    bool        hasSubLinks;    /* has subquery SubLink */
@@ -597,6 +592,8 @@ typedef struct SelectStmt
 
    /*
     * These fields are used only in "leaf" SelectStmts.
+    *
+    * into and intoColNames are a kluge; they belong somewhere else...
     */
    List       *distinctClause; /* NULL, list of DISTINCT ON exprs, or
                                 * lcons(NIL,NIL) for all (SELECT
@@ -611,11 +608,9 @@ typedef struct SelectStmt
 
    /*
     * These fields are used in both "leaf" SelectStmts and upper-level
-    * SelectStmts.  portalname/binary may only be set at the top level.
+    * SelectStmts.
     */
    List       *sortClause;     /* sort clause (a list of SortGroupBy's) */
-   char       *portalname;     /* the portal (cursor) to create */
-   bool        binary;         /* a binary (internal) portal? */
    Node       *limitOffset;    /* # of result tuples to skip */
    Node       *limitCount;     /* # of result tuples to return */
    List       *forUpdate;      /* FOR UPDATE clause */
@@ -815,16 +810,6 @@ typedef struct PrivTarget
    List       *objs;
 } PrivTarget;
 
-/* ----------------------
- *     Close Portal Statement
- * ----------------------
- */
-typedef struct ClosePortalStmt
-{
-   NodeTag     type;
-   char       *portalname;     /* name of the portal (cursor) */
-} ClosePortalStmt;
-
 /* ----------------------
  *     Copy Statement
  * ----------------------
@@ -1212,7 +1197,33 @@ typedef struct CommentStmt
 } CommentStmt;
 
 /* ----------------------
- *     Fetch Statement
+ *     Declare Cursor Statement
+ * ----------------------
+ */
+#define CURSOR_OPT_BINARY      0x0001
+#define CURSOR_OPT_SCROLL      0x0002
+#define CURSOR_OPT_INSENSITIVE 0x0004
+
+typedef struct DeclareCursorStmt
+{
+   NodeTag     type;
+   char       *portalname;     /* name of the portal (cursor) */
+   int         options;        /* bitmask of options (see above) */
+   Node       *query;          /* the SELECT query */
+} DeclareCursorStmt;
+
+/* ----------------------
+ *     Close Portal Statement
+ * ----------------------
+ */
+typedef struct ClosePortalStmt
+{
+   NodeTag     type;
+   char       *portalname;     /* name of the portal (cursor) */
+} ClosePortalStmt;
+
+/* ----------------------
+ *     Fetch Statement (also Move)
  * ----------------------
  */
 typedef enum FetchDirection
index 399b3bb1310eaafd512c1f33a21cedae04c9dacd..bd1d757e6a7d90f8e061832cc41c76c99eed380d 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: planmain.h,v 1.68 2003/01/24 03:58:44 tgl Exp $
+ * $Id: planmain.h,v 1.69 2003/03/10 03:53:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ extern Group *make_group(Query *root, List *tlist,
                         double numGroups,
                         Plan *lefttree);
 extern Material *make_material(List *tlist, Plan *lefttree);
+extern Plan *materialize_finished_plan(Plan *subplan);
 extern Unique *make_unique(List *tlist, Plan *lefttree, List *distinctList);
 extern Limit *make_limit(List *tlist, Plan *lefttree,
           Node *limitOffset, Node *limitCount);
index 16885b2f1383ebf29d83123b8e809ee076f47f7a..f9500202ce85e2969bf690873ffb64c83d46a25b 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: planner.h,v 1.25 2003/01/20 18:55:05 tgl Exp $
+ * $Id: planner.h,v 1.26 2003/03/10 03:53:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 #include "nodes/plannodes.h"
 
 
-extern Plan *planner(Query *parse);
+extern Plan *planner(Query *parse, bool isCursor, int cursorOptions);
 extern Plan *subquery_planner(Query *parse, double tuple_fraction);
 
 #endif   /* PLANNER_H */
index b00142499777b306b50b5802e74cc038ce8273e3..c992306a9af377c9b5eb79edb04724c9ff87b3d1 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: pquery.h,v 1.23 2002/12/05 15:50:39 tgl Exp $
+ * $Id: pquery.h,v 1.24 2003/03/10 03:53:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define PQUERY_H
 
 #include "executor/execdesc.h"
-#include "utils/portal.h"
 
 
 extern void ProcessQuery(Query *parsetree, Plan *plan, CommandDest dest,
             char *completionTag);
 
-extern Portal PreparePortal(char *portalName);
-
 #endif   /* PQUERY_H */
index 41fa2f735c2b15b19504ecb3621658a5f2c50487..21469dd52df442631cb6632f10e96a25acaf09af 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.37 2002/12/30 22:10:54 tgl Exp $
+ * $Id: portal.h,v 1.38 2003/03/10 03:53:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,8 +27,9 @@ typedef struct PortalData
    char       *name;           /* Portal's name */
    MemoryContext heap;         /* subsidiary memory */
    QueryDesc  *queryDesc;      /* Info about query associated with portal */
-   bool        atStart;        /* T => fetch backwards is not allowed */
-   bool        atEnd;          /* T => fetch forwards is not allowed */
+   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) */
 } PortalData;
 
@@ -44,12 +45,6 @@ typedef struct PortalData
 #define PortalGetQueryDesc(portal) ((portal)->queryDesc)
 #define PortalGetHeapMemory(portal) ((portal)->heap)
 
-/*
- * estimate of the maximum number of open portals a user would have,
- * used in initially sizing the PortalHashTable in EnablePortalManager()
- */
-#define PORTALS_PER_USER      64
-
 
 extern void EnablePortalManager(void);
 extern void AtEOXact_portals(void);