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:
parent
fa92d21a48
commit
66888f7424
@ -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>
|
||||
|
||||
<!-- *********************************************** -->
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -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 ... */
|
||||
|
9
src/backend/utils/cache/plancache.c
vendored
9
src/backend/utils/cache/plancache.c
vendored
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user