aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c49
-rw-r--r--src/backend/commands/portalcmds.c208
-rw-r--r--src/backend/executor/execAmi.c60
-rw-r--r--src/backend/executor/execMain.c6
-rw-r--r--src/backend/executor/spi.c104
-rw-r--r--src/backend/nodes/copyfuncs.c20
-rw-r--r--src/backend/nodes/equalfuncs.c19
-rw-r--r--src/backend/nodes/outfuncs.c18
-rw-r--r--src/backend/nodes/readfuncs.c25
-rw-r--r--src/backend/optimizer/path/allpaths.c5
-rw-r--r--src/backend/optimizer/plan/createplan.c37
-rw-r--r--src/backend/optimizer/plan/planmain.c7
-rw-r--r--src/backend/optimizer/plan/planner.c63
-rw-r--r--src/backend/optimizer/plan/subselect.c28
-rw-r--r--src/backend/optimizer/prep/prepjointree.c5
-rw-r--r--src/backend/optimizer/prep/prepunion.c5
-rw-r--r--src/backend/optimizer/util/clauses.c3
-rw-r--r--src/backend/parser/analyze.c90
-rw-r--r--src/backend/parser/gram.y127
-rw-r--r--src/backend/parser/parse_clause.c4
-rw-r--r--src/backend/parser/parse_type.c3
-rw-r--r--src/backend/tcop/postgres.c10
-rw-r--r--src/backend/tcop/pquery.c101
-rw-r--r--src/backend/tcop/utility.c10
-rw-r--r--src/backend/utils/mmgr/portalmem.c17
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/commands/portalcmds.h23
-rw-r--r--src/include/executor/executor.h3
-rw-r--r--src/include/executor/spi.h5
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/parsenodes.h55
-rw-r--r--src/include/optimizer/planmain.h3
-rw-r--r--src/include/optimizer/planner.h4
-rw-r--r--src/include/tcop/pquery.h5
-rw-r--r--src/include/utils/portal.h13
35 files changed, 606 insertions, 536 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 8bc39884b43..11425620e6b 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -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,
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 5881fe6c582..0621cdd5903 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -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));
+}
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index b22ad763498..b189e3e94bc 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -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;
+ }
+}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 9c4b6e74819..f037e72fd91 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -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;
/*
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index d94e12cde56..e1ccdf08f97 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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");
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 627f62c84f8..9ea51d589b5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6a0f3660097..3dd552bdd52 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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);
@@ -719,6 +715,16 @@ _equalInsertDefault(InsertDefault *a, InsertDefault *b)
}
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)
{
COMPARE_STRING_FIELD(portalname);
@@ -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;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8485244492c..899b93e4727 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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*
@@ -1082,6 +1082,16 @@ _outNotifyStmt(StringInfo str, NotifyStmt *node)
}
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)
{
WRITE_NODE_TYPE("SELECT");
@@ -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;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 410d092c916..fb1ea0c94d0 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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);
@@ -234,6 +232,21 @@ _readNotifyStmt(void)
}
/*
+ * _readDeclareCursorStmt
+ */
+static DeclareCursorStmt *
+_readDeclareCursorStmt(void)
+{
+ READ_LOCALS(DeclareCursorStmt);
+
+ READ_STRING_FIELD(portalname);
+ READ_INT_FIELD(options);
+ READ_NODE_FIELD(query);
+
+ READ_DONE();
+}
+
+/*
* _readSortClause
*/
static SortClause *
@@ -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);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 99d979d57c0..0bf43cab24d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -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;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index bfc11aa0046..d01acdc6182 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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,
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index daa840f789e..97f6b76a8e4 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -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
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index c7c072fe2eb..9fa78b8a237 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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).
@@ -694,29 +720,6 @@ grouping_planner(Query *parse, double tuple_fraction)
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
* the caller, since it reflects plan actions that this routine
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index fc428977c33..417eecc1fe3 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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);
}
}
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 207a813e8e0..a265623bfb7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -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");
/*
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f0a64f2980c..1782bcc88b9 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -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
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 40e440a3754..fcd58374189 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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 ||
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 2089008bbf5..3486a1e010b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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");
@@ -2328,6 +2285,27 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
}
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)
{
Query *result = makeNode(Query);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c593196dfc8..045d3cc2ca2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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:
@@ -4054,26 +4065,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:
* INSERT STATEMENTS
@@ -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; }
;
/*****************************************************************************
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 33e7cce4203..d4c13165e5a 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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");
/*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index e5f1e88ffdb..1f0b7639d5f 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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 ||
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b4f064d0e1b..ec717d8c122 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 1e02e42193d..29d5018440d 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index db7dc0945cd..0fae711a2c3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 520a55d64ae..654247dd8c5 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -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;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index fc24db5d2e1..6344f0bae53 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index d143423f6a3..3f2a4221add 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.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: 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);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index ee30f518969..785d21718b2 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.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: 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
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 85a987b14b2..ead328da247 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -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"
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 356f4b60fa3..3304ea89e4e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.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,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 381c11c3893..c84348ded9e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.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: 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 */
@@ -816,16 +811,6 @@ typedef struct PrivTarget
} 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
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 399b3bb1310..bd1d757e6a7 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.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: 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);
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 16885b2f138..f9500202ce8 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/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: 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 */
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index b0014249977..c992306a9af 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.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 */
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 41fa2f735c2..21469dd52df 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.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);