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