From aa83bc04e089e13f2746ba55720e5993268c46f5 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Mon, 10 Mar 2003 03:53:52 +0000 Subject: [PATCH] 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. --- doc/src/sgml/ref/declare.sgml | 46 ++--- doc/src/sgml/ref/explain.sgml | 5 +- doc/src/sgml/ref/fetch.sgml | 22 ++- doc/src/sgml/ref/move.sgml | 6 +- doc/src/sgml/ref/prepare.sgml | 11 +- src/backend/commands/explain.c | 49 +++-- src/backend/commands/portalcmds.c | 208 +++++++++++++++++----- src/backend/executor/execAmi.c | 60 ++++++- src/backend/executor/execMain.c | 6 +- src/backend/executor/spi.c | 104 ++--------- src/backend/nodes/copyfuncs.c | 20 ++- src/backend/nodes/equalfuncs.c | 19 +- src/backend/nodes/outfuncs.c | 18 +- src/backend/nodes/readfuncs.c | 25 ++- src/backend/optimizer/path/allpaths.c | 5 +- src/backend/optimizer/plan/createplan.c | 37 +++- src/backend/optimizer/plan/planmain.c | 7 +- src/backend/optimizer/plan/planner.c | 63 +++---- src/backend/optimizer/plan/subselect.c | 28 +-- src/backend/optimizer/prep/prepjointree.c | 5 +- src/backend/optimizer/prep/prepunion.c | 5 +- src/backend/optimizer/util/clauses.c | 3 +- src/backend/parser/analyze.c | 90 ++++------ src/backend/parser/gram.y | 127 ++++++------- src/backend/parser/parse_clause.c | 4 +- src/backend/parser/parse_type.c | 3 +- src/backend/tcop/postgres.c | 10 +- src/backend/tcop/pquery.c | 101 +---------- src/backend/tcop/utility.c | 10 +- src/backend/utils/mmgr/portalmem.c | 17 +- src/include/catalog/catversion.h | 4 +- src/include/commands/portalcmds.h | 23 +-- src/include/executor/executor.h | 3 +- src/include/executor/spi.h | 5 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 55 +++--- src/include/optimizer/planmain.h | 3 +- src/include/optimizer/planner.h | 4 +- src/include/tcop/pquery.h | 5 +- src/include/utils/portal.h | 13 +- 40 files changed, 661 insertions(+), 571 deletions(-) diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml index 77c45d90b09..5f481220071 100644 --- a/doc/src/sgml/ref/declare.sgml +++ b/doc/src/sgml/ref/declare.sgml @@ -1,5 +1,5 @@ <!-- -$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. diff --git a/doc/src/sgml/ref/explain.sgml b/doc/src/sgml/ref/explain.sgml index 14639cc268c..be812bebf29 100644 --- a/doc/src/sgml/ref/explain.sgml +++ b/doc/src/sgml/ref/explain.sgml @@ -1,5 +1,5 @@ <!-- -$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> diff --git a/doc/src/sgml/ref/fetch.sgml b/doc/src/sgml/ref/fetch.sgml index 4770545cb83..0452cf0144f 100644 --- a/doc/src/sgml/ref/fetch.sgml +++ b/doc/src/sgml/ref/fetch.sgml @@ -1,5 +1,5 @@ <!-- -$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: diff --git a/doc/src/sgml/ref/move.sgml b/doc/src/sgml/ref/move.sgml index 69be788c35c..928faabc818 100644 --- a/doc/src/sgml/ref/move.sgml +++ b/doc/src/sgml/ref/move.sgml @@ -1,5 +1,5 @@ <!-- -$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 diff --git a/doc/src/sgml/ref/prepare.sgml b/doc/src/sgml/ref/prepare.sgml index 418bd83ace2..75401730915 100644 --- a/doc/src/sgml/ref/prepare.sgml +++ b/doc/src/sgml/ref/prepare.sgml @@ -1,5 +1,5 @@ <!-- -$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> 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); @@ -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; 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* @@ -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; 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); @@ -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); 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). @@ -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 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"); @@ -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) { 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: @@ -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; } ; /***************************************************************************** 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 */ @@ -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 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); -- GitLab