]> granicus.if.org Git - postgresql/commitdiff
Expose more cursor-related functionality in SPI: specifically, allow
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Apr 2007 01:14:58 +0000 (01:14 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Apr 2007 01:14:58 +0000 (01:14 +0000)
access to the planner's cursor-related planning options, and provide new
FETCH/MOVE routines that allow access to the full power of those commands.
Small refactoring of planner(), pg_plan_query(), and pg_plan_queries()
APIs to make it convenient to pass the planning options down from SPI.

This is the core-code portion of Pavel Stehule's patch for scrollable
cursor support in plpgsql; I'll review and apply the plpgsql changes
separately.

15 files changed:
doc/src/sgml/spi.sgml
src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/optimizer/plan/planner.c
src/backend/parser/gram.y
src/backend/tcop/postgres.c
src/backend/utils/cache/plancache.c
src/include/executor/spi.h
src/include/nodes/parsenodes.h
src/include/optimizer/planner.h
src/include/tcop/tcopprot.h

index 14e975b5fc9263dffaeaea19ee95419e90959fdd..7ee47162cd74aaa1ca0370704487e4d8b8680284 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.55 2007/03/25 23:27:59 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.56 2007/04/16 01:14:55 tgl Exp $ -->
 
 <chapter id="spi">
  <title>Server Programming Interface</title>
@@ -799,6 +799,104 @@ SPIPlanPtr SPI_prepare(const char * <parameter>command</parameter>, int <paramet
 
 <!-- *********************************************** -->
 
+<refentry id="spi-spi-prepare-cursor">
+ <refmeta>
+  <refentrytitle>SPI_prepare_cursor</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_prepare_cursor</refname>
+  <refpurpose>prepare a plan for a command, without executing it yet</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_prepare_cursor</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <parameter>nargs</parameter>, Oid * <parameter>argtypes</parameter>, int <parameter>cursorOptions</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_prepare_cursor</function> is identical to
+   <function>SPI_prepare</function>, except that it also allows specification
+   of the planner's <quote>cursor options</> parameter.  This is a bitmask
+   having the values shown in <filename>nodes/parsenodes.h</filename>
+   for the <structfield>options</> field of <structname>DeclareCursorStmt</>.
+   <function>SPI_prepare</function> always takes these options as zero.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>const char * <parameter>command</parameter></literal></term>
+    <listitem>
+     <para>
+      command string
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>int <parameter>nargs</parameter></literal></term>
+    <listitem>
+     <para>
+      number of input parameters (<literal>$1</>, <literal>$2</>, etc.)
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>Oid * <parameter>argtypes</parameter></literal></term>
+    <listitem>
+     <para>
+      pointer to an array containing the <acronym>OID</acronym>s of
+      the data types of the parameters
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><literal>int <parameter>cursorOptions</parameter></literal></term>
+    <listitem>
+     <para>
+      integer bitmask of cursor options; zero produces default behavior
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   <function>SPI_prepare_cursor</function> has the same return conventions as
+   <function>SPI_prepare</function>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   Useful bits to set in <parameter>cursorOptions</> include
+   <symbol>CURSOR_OPT_SCROLL</symbol>,
+   <symbol>CURSOR_OPT_NO_SCROLL</symbol>, and
+   <symbol>CURSOR_OPT_FAST_PLAN</symbol>.  Note in particular that
+   <symbol>CURSOR_OPT_HOLD</symbol> is ignored.
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
 <refentry id="spi-spi-getargcount">
  <refmeta>
   <refentrytitle>SPI_getargcount</refentrytitle>
@@ -1430,7 +1528,9 @@ void SPI_cursor_fetch(Portal <parameter>portal</parameter>, bool <parameter>forw
 
   <para>
    <function>SPI_cursor_fetch</function> fetches some rows from a
-   cursor.  This is equivalent to the SQL command <command>FETCH</>.
+   cursor.  This is equivalent to a subset of the SQL command
+   <command>FETCH</> (see <function>SPI_scroll_cursor_fetch</function>
+   for more functionality).
   </para>
  </refsect1>
 
@@ -1476,6 +1576,15 @@ void SPI_cursor_fetch(Portal <parameter>portal</parameter>, bool <parameter>forw
    <function>SPI_execute</function> if successful.
   </para>
  </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   Fetching backward may fail if the cursor's plan was not created
+   with the <symbol>CURSOR_OPT_SCROLL</symbol> option.
+  </para>
+ </refsect1>
 </refentry>
 
 <!-- *********************************************** -->
@@ -1503,8 +1612,9 @@ void SPI_cursor_move(Portal <parameter>portal</parameter>, bool <parameter>forwa
 
   <para>
    <function>SPI_cursor_move</function> skips over some number of rows
-   in a cursor.  This is equivalent to the SQL command
-   <command>MOVE</>.
+   in a cursor.  This is equivalent to a subset of the SQL command
+   <command>MOVE</> (see <function>SPI_scroll_cursor_move</function>
+   for more functionality).
   </para>
  </refsect1>
 
@@ -1540,6 +1650,210 @@ void SPI_cursor_move(Portal <parameter>portal</parameter>, bool <parameter>forwa
    </varlistentry>
   </variablelist>
  </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   Moving backward may fail if the cursor's plan was not created
+   with the <symbol>CURSOR_OPT_SCROLL</symbol> option.
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-scroll-cursor-fetch">
+ <refmeta>
+  <refentrytitle>SPI_scroll_cursor_fetch</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_scroll_cursor_fetch</refname>
+  <refpurpose>fetch some rows from a cursor</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_scroll_cursor_fetch</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+void SPI_scroll_cursor_fetch(Portal <parameter>portal</parameter>, FetchDirection <parameter>direction</parameter>, long <parameter>count</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_scroll_cursor_fetch</function> fetches some rows from a
+   cursor.  This is equivalent to the SQL command <command>FETCH</>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>Portal <parameter>portal</parameter></literal></term>
+    <listitem>
+     <para>
+      portal containing the cursor
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>FetchDirection <parameter>direction</parameter></literal></term>
+    <listitem>
+     <para>
+      one of <symbol>FETCH_FORWARD</symbol>,
+      <symbol>FETCH_BACKWARD</symbol>,
+      <symbol>FETCH_ABSOLUTE</symbol> or
+      <symbol>FETCH_RELATIVE</symbol>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>long <parameter>count</parameter></literal></term>
+    <listitem>
+     <para>
+      number of rows to fetch for
+      <symbol>FETCH_FORWARD</symbol> or
+      <symbol>FETCH_BACKWARD</symbol>; absolute row number to fetch for
+      <symbol>FETCH_ABSOLUTE</symbol>; or relative row number to fetch for
+      <symbol>FETCH_RELATIVE</symbol>
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   <varname>SPI_processed</varname> and
+   <varname>SPI_tuptable</varname> are set as in
+   <function>SPI_execute</function> if successful.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   See the SQL <xref linkend="sql-fetch" endterm="sql-fetch-title"> command
+   for details of the interpretation of the 
+   <parameter>direction</parameter> and
+   <parameter>count</parameter> parameters.
+  </para>
+
+  <para>
+   Direction values other than <symbol>FETCH_FORWARD</symbol>
+   may fail if the cursor's plan was not created
+   with the <symbol>CURSOR_OPT_SCROLL</symbol> option.
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-scroll-cursor-move">
+ <refmeta>
+  <refentrytitle>SPI_scroll_cursor_move</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_scroll_cursor_move</refname>
+  <refpurpose>move a cursor</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_scroll_cursor_move</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+void SPI_scroll_cursor_move(Portal <parameter>portal</parameter>, FetchDirection <parameter>direction</parameter>, long <parameter>count</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_scroll_cursor_move</function> skips over some number of rows
+   in a cursor.  This is equivalent to the SQL command
+   <command>MOVE</>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>Portal <parameter>portal</parameter></literal></term>
+    <listitem>
+     <para>
+      portal containing the cursor
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>FetchDirection <parameter>direction</parameter></literal></term>
+    <listitem>
+     <para>
+      one of <symbol>FETCH_FORWARD</symbol>,
+      <symbol>FETCH_BACKWARD</symbol>,
+      <symbol>FETCH_ABSOLUTE</symbol> or
+      <symbol>FETCH_RELATIVE</symbol>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>long <parameter>count</parameter></literal></term>
+    <listitem>
+     <para>
+      number of rows to move for
+      <symbol>FETCH_FORWARD</symbol> or
+      <symbol>FETCH_BACKWARD</symbol>; absolute row number to move to for
+      <symbol>FETCH_ABSOLUTE</symbol>; or relative row number to move to for
+      <symbol>FETCH_RELATIVE</symbol>
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   <varname>SPI_processed</varname> and
+   <varname>SPI_tuptable</varname> are set as in
+   <function>SPI_execute</function> if successful.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   See the SQL <xref linkend="sql-fetch" endterm="sql-fetch-title"> command
+   for details of the interpretation of the 
+   <parameter>direction</parameter> and
+   <parameter>count</parameter> parameters.
+  </para>
+
+  <para>
+   Direction values other than <symbol>FETCH_FORWARD</symbol>
+   may fail if the cursor's plan was not created
+   with the <symbol>CURSOR_OPT_SCROLL</symbol> option.
+  </para>
+ </refsect1>
 </refentry>
 
 <!-- *********************************************** -->
index 99d347f590796941d2338e57e1fac031b9e8a3fc..42121f3fe45d6af3f3101c858b0965c707da5f57 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.279 2007/03/29 00:15:38 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.280 2007/04/16 01:14:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1023,7 +1023,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
                                         errmsg("COPY (SELECT INTO) is not supported")));
 
                /* plan the query */
-               plan = planner(query, false, 0, NULL);
+               plan = planner(query, 0, NULL);
 
                /*
                 * Update snapshot command ID to ensure this query sees results of any
index 1b2cfccce9f7779c0a22ac45eb164bac9f78836e..bb7d43035989e05684af9d8811dbf55736d5eec2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.160 2007/03/13 00:33:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.161 2007/04/16 01:14:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@ typedef struct ExplainState
        List       *rtable;                     /* range table */
 } ExplainState;
 
-static void ExplainOneQuery(Query *query, bool isCursor, int cursorOptions,
+static void ExplainOneQuery(Query *query, int cursorOptions,
                                                        ExplainStmt *stmt, const char *queryString,
                                                        ParamListInfo params, TupOutputState *tstate);
 static double elapsed_time(instr_time *starttime);
@@ -102,7 +102,7 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
                /* Explain every plan */
                foreach(l, rewritten)
                {
-                       ExplainOneQuery((Query *) lfirst(l), false, 0,
+                       ExplainOneQuery((Query *) lfirst(l), 0,
                                                        stmt, queryString, params, tstate);
                        /* put a blank line between plans */
                        if (lnext(l) != NULL)
@@ -134,7 +134,7 @@ ExplainResultDesc(ExplainStmt *stmt)
  *       print out the execution plan for one Query
  */
 static void
-ExplainOneQuery(Query *query, bool isCursor, int cursorOptions,
+ExplainOneQuery(Query *query, int cursorOptions,
                                ExplainStmt *stmt, const char *queryString,
                                ParamListInfo params, TupOutputState *tstate)
 {
@@ -150,7 +150,7 @@ ExplainOneQuery(Query *query, bool isCursor, int cursorOptions,
        }
 
        /* plan the query */
-       plan = planner(query, isCursor, cursorOptions, params);
+       plan = planner(query, cursorOptions, params);
 
        /*
         * Update snapshot command ID to ensure this query sees results of any
@@ -229,7 +229,7 @@ ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
                /* do not actually execute the underlying query! */
                memcpy(&newstmt, stmt, sizeof(ExplainStmt));
                newstmt.analyze = false;
-               ExplainOneQuery(query, true, dcstmt->options, &newstmt,
+               ExplainOneQuery(query, dcstmt->options, &newstmt,
                                                queryString, params, tstate);
        }
        else if (IsA(utilityStmt, ExecuteStmt))
index d759ed4ac2d99df9bac0aaf8375eef266c771c9c..eb381ebb7052ce609a4e09a759294b1e4ba75395 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.63 2007/04/12 06:53:46 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.64 2007/04/16 01:14:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,7 +111,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
                                 errdetail("Cursors must be READ ONLY.")));
 
        /* plan the query */
-       plan = planner(query, true, stmt->options, params);
+       plan = planner(query, stmt->options, params);
 
        /*
         * Create a portal and copy the plan into its memory context.
index fe1a8532f070fd81f2b73301ad8cf38d5ef8b274..9a4f88d1a0b1f1e3c966e3ce0217d595c00d743f 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.71 2007/04/12 06:53:46 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.72 2007/04/16 01:14:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -160,7 +160,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
        query_list = QueryRewrite(query);
 
        /* Generate plans for queries.  Snapshot is already set. */
-       plan_list = pg_plan_queries(query_list, NULL, false);
+       plan_list = pg_plan_queries(query_list, 0, NULL, false);
 
        /*
         * Save the results.
index 1e1d450be57d2735a03f5b8197fed3d074bf18b0..b59228b0c9ee199112e1a999cded253e936b94da 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.114 2007/04/02 18:49:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.115 2007/04/16 01:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -115,7 +115,7 @@ init_execution_state(List *queryTree_list, bool readonly_func)
                if (queryTree->commandType == CMD_UTILITY)
                        stmt = queryTree->utilityStmt;
                else
-                       stmt = (Node *) pg_plan_query(queryTree, NULL);
+                       stmt = (Node *) pg_plan_query(queryTree, 0, NULL);
 
                /* Precheck all commands for validity in a function */
                if (IsA(stmt, TransactionStmt))
index 655503cd708e94014bac92ba1bbd92624108afa7..bf3bbdbceb3724203e954b204a891c5c71fdf575 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.175 2007/03/25 23:42:43 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.176 2007/04/16 01:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,8 @@ static int    _SPI_stack_depth = 0;           /* allocated size of _SPI_stack */
 static int     _SPI_connected = -1;
 static int     _SPI_curid = -1;
 
-static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
+static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
+                                                         int cursorOptions);
 
 static int _SPI_execute_plan(SPIPlanPtr plan,
                                  Datum *Values, const char *Nulls,
@@ -45,8 +46,9 @@ static int    _SPI_pquery(QueryDesc *queryDesc, long tcount);
 
 static void _SPI_error_callback(void *arg);
 
-static void _SPI_cursor_operation(Portal portal, bool forward, long count,
-                                         DestReceiver *dest);
+static void _SPI_cursor_operation(Portal portal,
+                                                                 FetchDirection direction, long count,
+                                                                 DestReceiver *dest);
 
 static SPIPlanPtr _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt);
 static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
@@ -310,7 +312,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
 
-       _SPI_prepare_plan(src, &plan);
+       _SPI_prepare_plan(src, &plan, 0);
 
        res = _SPI_execute_plan(&plan, NULL, NULL,
                                                        InvalidSnapshot, InvalidSnapshot,
@@ -398,6 +400,13 @@ SPI_execute_snapshot(SPIPlanPtr plan,
 
 SPIPlanPtr
 SPI_prepare(const char *src, int nargs, Oid *argtypes)
+{
+       return SPI_prepare_cursor(src, nargs, argtypes, 0);
+}
+
+SPIPlanPtr
+SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
+                                  int cursorOptions)
 {
        _SPI_plan       plan;
        SPIPlanPtr      result;
@@ -417,7 +426,7 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes)
        plan.nargs = nargs;
        plan.argtypes = argtypes;
 
-       _SPI_prepare_plan(src, &plan);
+       _SPI_prepare_plan(src, &plan, cursorOptions);
 
        /* copy plan to procedure context */
        result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
@@ -1032,7 +1041,8 @@ SPI_cursor_find(const char *name)
 void
 SPI_cursor_fetch(Portal portal, bool forward, long count)
 {
-       _SPI_cursor_operation(portal, forward, count,
+       _SPI_cursor_operation(portal,
+                                                 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
                                                  CreateDestReceiver(DestSPI, NULL));
        /* we know that the DestSPI receiver doesn't need a destroy call */
 }
@@ -1046,7 +1056,36 @@ SPI_cursor_fetch(Portal portal, bool forward, long count)
 void
 SPI_cursor_move(Portal portal, bool forward, long count)
 {
-       _SPI_cursor_operation(portal, forward, count, None_Receiver);
+       _SPI_cursor_operation(portal,
+                                                 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
+                                                 None_Receiver);
+}
+
+
+/*
+ * SPI_scroll_cursor_fetch()
+ *
+ *     Fetch rows in a scrollable cursor
+ */
+void
+SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
+{
+       _SPI_cursor_operation(portal,
+                                                 direction, count,
+                                                 CreateDestReceiver(DestSPI, NULL));
+       /* we know that the DestSPI receiver doesn't need a destroy call */
+}
+
+
+/*
+ * SPI_scroll_cursor_move()
+ *
+ *     Move in a scrollable cursor
+ */
+void
+SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
+{
+       _SPI_cursor_operation(portal, direction, count, None_Receiver);
 }
 
 
@@ -1299,7 +1338,7 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
  * and need to be copied somewhere to survive.
  */
 static void
-_SPI_prepare_plan(const char *src, SPIPlanPtr plan)
+_SPI_prepare_plan(const char *src, SPIPlanPtr plan, int cursorOptions)
 {
        List       *raw_parsetree_list;
        List       *plancache_list;
@@ -1345,7 +1384,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                /* Need a copyObject here to keep parser from modifying raw tree */
                stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
                                                                                   src, argtypes, nargs);
-               stmt_list = pg_plan_queries(stmt_list, NULL, false);
+               stmt_list = pg_plan_queries(stmt_list, cursorOptions, NULL, false);
 
                plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
                cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
@@ -1739,7 +1778,7 @@ _SPI_error_callback(void *arg)
  *     Do a FETCH or MOVE in a cursor
  */
 static void
-_SPI_cursor_operation(Portal portal, bool forward, long count,
+_SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
                                          DestReceiver *dest)
 {
        long            nfetched;
@@ -1760,7 +1799,7 @@ _SPI_cursor_operation(Portal portal, bool forward, long count,
 
        /* Run the cursor */
        nfetched = PortalRunFetch(portal,
-                                                         forward ? FETCH_FORWARD : FETCH_BACKWARD,
+                                                         direction,
                                                          count,
                                                          dest);
 
index 9bf5516ca4c30d13fbf9692994bcb904d78efa73..5c65a78d6f727be5e75b1271ae2944aa6ee9359a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.216 2007/02/27 01:11:25 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.217 2007/04/16 01:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,8 +80,7 @@ static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
  *
  *****************************************************************************/
 PlannedStmt *
-planner(Query *parse, bool isCursor, int cursorOptions,
-               ParamListInfo boundParams)
+planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 {
        PlannedStmt *result;
        PlannerGlobal *glob;
@@ -107,7 +106,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
        glob->finalrtable = NIL;
 
        /* Determine what fraction of the plan is likely to be scanned */
-       if (isCursor)
+       if (cursorOptions & CURSOR_OPT_FAST_PLAN)
        {
                /*
                 * We have no real idea how many tuples the user will ultimately FETCH
@@ -130,7 +129,7 @@ planner(Query *parse, bool isCursor, int cursorOptions,
         * 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 (cursorOptions & CURSOR_OPT_SCROLL)
        {
                if (!ExecSupportsBackwardScan(top_plan))
                        top_plan = materialize_finished_plan(top_plan);
index 6aa81eec3efd4d41d5b757e8963e4fe14ed0297c..b9728deaada5e9b1d09625bcd391589b84d496ac 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.588 2007/04/12 06:53:46 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.589 2007/04/16 01:14:56 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -279,9 +279,9 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 %type <boolean> opt_freeze opt_default opt_recheck
 %type <defelt> opt_binary opt_oids copy_delimiter
 
-%type <boolean> copy_from opt_hold
+%type <boolean> copy_from
 
-%type <ival>   opt_column event cursor_options
+%type <ival>   opt_column event cursor_options opt_hold
 %type <objtype>        reindex_type drop_type comment_type
 
 %type <node>   fetch_direction select_limit_value select_offset_value
@@ -5821,10 +5821,9 @@ DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
                                {
                                        DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
                                        n->portalname = $2;
-                                       n->options = $3;
+                                       /* currently we always set FAST_PLAN option */
+                                       n->options = $3 | $5 | CURSOR_OPT_FAST_PLAN;
                                        n->query = $7;
-                                       if ($5)
-                                               n->options |= CURSOR_OPT_HOLD;
                                        $$ = (Node *)n;
                                }
                ;
@@ -5836,9 +5835,9 @@ cursor_options: /*EMPTY*/                                 { $$ = 0; }
                        | cursor_options INSENSITIVE    { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
                ;
 
-opt_hold: /* EMPTY */                                          { $$ = FALSE; }
-                       | WITH HOLD                                             { $$ = TRUE; }
-                       | WITHOUT HOLD                                  { $$ = FALSE; }
+opt_hold: /* EMPTY */                                          { $$ = 0; }
+                       | WITH HOLD                                             { $$ = CURSOR_OPT_HOLD; }
+                       | WITHOUT HOLD                                  { $$ = 0; }
                ;
 
 /*****************************************************************************
index bfcc271996e41e5299998f560e5378b722c64f39..7722eddf3474e6dd8637279782944f06e4a87135 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.530 2007/03/29 19:10:10 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.531 2007/04/16 01:14:57 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -656,9 +656,12 @@ pg_rewrite_queries(List *querytree_list)
 }
 
 
-/* Generate a plan for a single already-rewritten query. */
+/*
+ * Generate a plan for a single already-rewritten query.
+ * This is a thin wrapper around planner() and takes the same parameters.
+ */
 PlannedStmt *
-pg_plan_query(Query *querytree, ParamListInfo boundParams)
+pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 {
        PlannedStmt *plan;
 
@@ -670,7 +673,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
                ResetUsage();
 
        /* call the optimizer */
-       plan = planner(querytree, false, 0, boundParams);
+       plan = planner(querytree, cursorOptions, boundParams);
 
        if (log_planner_stats)
                ShowUsage("PLANNER STATISTICS");
@@ -718,7 +721,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
  * list.  Utility statements are simply represented by their statement nodes.
  */
 List *
-pg_plan_queries(List *querytrees, ParamListInfo boundParams,
+pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams,
                                bool needSnapshot)
 {
        List       *stmt_list = NIL;
@@ -741,7 +744,7 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                                ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
                                needSnapshot = false;
                        }
-                       stmt = (Node *) pg_plan_query(query, boundParams);
+                       stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
                }
 
                stmt_list = lappend(stmt_list, stmt);
@@ -892,7 +895,7 @@ exec_simple_query(const char *query_string)
                querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
                                                                                                NULL, 0);
 
-               plantree_list = pg_plan_queries(querytree_list, NULL, true);
+               plantree_list = pg_plan_queries(querytree_list, 0, NULL, true);
 
                /* If we got a cancel signal in analysis or planning, quit */
                CHECK_FOR_INTERRUPTS();
@@ -1207,7 +1210,7 @@ exec_parse_message(const char *query_string,      /* string to execute */
                }
                else
                {
-                       stmt_list = pg_plan_queries(querytree_list, NULL, true);
+                       stmt_list = pg_plan_queries(querytree_list, 0, NULL, true);
                        fully_planned = true;
                }
        }
@@ -1621,7 +1624,7 @@ exec_bind_message(StringInfo input_message)
                 */
                oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
                query_list = copyObject(cplan->stmt_list);
-               plan_list = pg_plan_queries(query_list, params, true);
+               plan_list = pg_plan_queries(query_list, 0, params, true);
                MemoryContextSwitchTo(oldContext);
 
                /* We no longer need the cached plan refcount ... */
index 6165e59c6e6c09231c041149c05939f063b1b610..28838eba09a88d7002fee9808b634309970a5ff1 100644 (file)
@@ -33,7 +33,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.6 2007/04/12 06:53:47 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.7 2007/04/16 01:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -458,10 +458,13 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
                if (plansource->fully_planned)
                {
                        /*
-                        * Generate plans for queries.  Assume snapshot is not set yet
+                        * Generate plans for queries.  We don't need any boundParams, and
+                        * currently we don't need to worry about cursor options because
+                        * cursor plans are never saved in the plancache (that might have
+                        * to change someday).  Also, assume snapshot is not set yet
                         * (XXX this may be wasteful, won't all callers have done that?)
                         */
-                       slist = pg_plan_queries(slist, NULL, true);
+                       slist = pg_plan_queries(slist, 0, NULL, true);
                }
 
                /*
index 80c8993c7c3cba70a7904e547d94fe8da23f1e37..c996f644c03a94a2b1c98b25b28b08baff1026f7 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.60 2007/03/25 23:27:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.61 2007/04/16 01:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,8 +20,8 @@
 #include "postgres.h"
 
 /*
- *     These are not needed by this file, but used by other programs
- *     using SPI
+ *     Most of these are not needed by this file, but may be used by
+ *     user-written code that uses SPI
  */
 #include "access/heapam.h"
 #include "access/xact.h"
@@ -32,6 +32,7 @@
 #include "executor/executor.h"
 #include "nodes/execnodes.h"
 #include "nodes/params.h"
+#include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/primnodes.h"
 #include "nodes/relation.h"
@@ -105,6 +106,8 @@ extern int  SPI_execute_snapshot(SPIPlanPtr plan,
                                         Snapshot crosscheck_snapshot,
                                         bool read_only, long tcount);
 extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
+extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
+                                                                        int cursorOptions);
 extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
 extern int     SPI_freeplan(SPIPlanPtr plan);
 
@@ -136,6 +139,8 @@ extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
 extern Portal SPI_cursor_find(const char *name);
 extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
 extern void SPI_cursor_move(Portal portal, bool forward, long count);
+extern void SPI_scroll_cursor_fetch(Portal, FetchDirection direction, long count);
+extern void SPI_scroll_cursor_move(Portal, FetchDirection direction, long count);
 extern void SPI_cursor_close(Portal portal);
 
 extern void AtEOXact_SPI(bool isCommit);
index bc421e265fcb1b825a2250e0f5b938791a4b4c08..dbc29642f06620a33b235c6db4f963c5c99fbf40 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.345 2007/04/12 06:53:48 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.346 2007/04/16 01:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1429,11 +1429,12 @@ typedef struct CommentStmt
  *             Declare Cursor Statement
  * ----------------------
  */
-#define CURSOR_OPT_BINARY              0x0001
-#define CURSOR_OPT_SCROLL              0x0002
-#define CURSOR_OPT_NO_SCROLL   0x0004
-#define CURSOR_OPT_INSENSITIVE 0x0008
-#define CURSOR_OPT_HOLD                        0x0010
+#define CURSOR_OPT_BINARY              0x0001          /* BINARY */
+#define CURSOR_OPT_SCROLL              0x0002          /* SCROLL explicitly given */
+#define CURSOR_OPT_NO_SCROLL   0x0004          /* NO SCROLL explicitly given */
+#define CURSOR_OPT_INSENSITIVE 0x0008          /* INSENSITIVE (unimplemented) */
+#define CURSOR_OPT_HOLD                        0x0010          /* WITH HOLD */
+#define CURSOR_OPT_FAST_PLAN   0x0020          /* prefer fast-start plan */
 
 typedef struct DeclareCursorStmt
 {
index c243cdbc356aa2206bf81484a905549943213fe4..b568a7dbe8206f0a4cf0748be21752ee8b61a01a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.38 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.39 2007/04/16 01:14:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 #include "nodes/relation.h"
 
 
-extern PlannedStmt *planner(Query *parse, bool isCursor, int cursorOptions,
+extern PlannedStmt *planner(Query *parse, int cursorOptions,
                ParamListInfo boundParams);
 extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
                                                          Index level, double tuple_fraction,
index 098cb1da1ed948e95be84f65f78b541825f4d2fe..613d9a99837246bd85243023d60553dbd470f398 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.88 2007/03/07 13:35:03 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.89 2007/04/16 01:14:58 tgl Exp $
  *
  * OLD COMMENTS
  *       This file was created so that other c files could get the two
@@ -49,9 +49,10 @@ extern List *pg_parse_and_rewrite(const char *query_string,
 extern List *pg_parse_query(const char *query_string);
 extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
                                           Oid *paramTypes, int numParams);
-extern PlannedStmt *pg_plan_query(Query *querytree, ParamListInfo boundParams);
-extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
-                               bool needSnapshot);
+extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
+                                                                 ParamListInfo boundParams);
+extern List *pg_plan_queries(List *querytrees, int cursorOptions,
+                                                        ParamListInfo boundParams, bool needSnapshot);
 
 extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);