Commit aa83bc04 authored by Tom Lane's avatar Tom Lane

Restructure parsetree representation of DECLARE CURSOR: now it's a

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