Expose more cursor-related functionality in SPI: specifically, allow

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.
This commit is contained in:
Tom Lane 2007-04-16 01:14:58 +00:00
parent fa92d21a48
commit 66888f7424
15 changed files with 435 additions and 71 deletions

View 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>
<!-- *********************************************** -->

View 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

View 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))

View 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.

View 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.

View 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))

View 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);

View 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);

View 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; }
;
/*****************************************************************************

View 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 ... */

View 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);
}
/*

View 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);

View 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
{

View 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,

View 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);