diff --git a/doc/src/sgml/ref/declare.sgml b/doc/src/sgml/ref/declare.sgml index 77c45d90b09e409d6e00bd6ee2b19cca332d5461..5f4812200710d73f48e081907b65437d05cd909f 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 14639cc268ce0e75c345f24aad7c63b21dca42d7..be812bebf294bc877040e21ab9120be86600a378 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 4770545cb838379e82d62bb3d6b3bdcaa2f80e42..0452cf0144ffb0a76778ab8332835237d022c32a 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 69be788c35c5fb12641f64c8b4e19db91c972faa..928faabc818bcee4c6696e2e5cf6842a0c3e513a 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 418bd83ace222495d9febd0f50ea2e240c5b69dc..754017309156856916c8852f697b3295058877d6 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 8bc39884b43dc53651d43e1d9b68ba9ca600f185..11425620e6b22974dc135fa23a3e8774a69e9b62 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 5881fe6c582288f74b894de41d2389b433ab1f11..0621cdd59036df38dc9acffc72e457a5a3927db7 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 b22ad7634982003f66f721dea37dc7bcf2079cd6..b189e3e94bc521d7d89168661dad9df103ef763d 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 9c4b6e748192b4bf28696b0a08dd3dc72cfc047f..f037e72fd911bba0fe2f458ab09cd35800750abf 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 d94e12cde5619fd119d79b6b402eb7c4a6fd4b86..e1ccdf08f97425bb69540364c59eef749e15feed 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 627f62c84f8c27d6cc541ca11ce9b0657fd27e09..9ea51d589b5d05b00cc8d43581c337596c0ca001 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 6a0f36600979976a32290728e51ef66745ec5dee..3dd552bdd523bcc6fdea845b135e5ce7cc9ae28f 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 8485244492cab297276ac95276985fda06be313c..899b93e4727944bdce290cdbc4319d5b68d4561f 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 410d092c916404f0ab59cb232de9c31c8035898b..fb1ea0c94d095c8fe7e3c0ceada5d2d43dbb73eb 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 99d979d57c07dd5fae16353531a5e0a877f5f1cd..0bf43cab24df73713e6cacc53221503d7184cea0 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 bfc11aa004662cd6de53f7fc69e2112c7e809b50..d01acdc6182afb172913718bc904bcc2cea1378c 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 daa840f789e197f65a16da5208481dc23c6f6402..97f6b76a8e48c61dd12414cfb1bfafa5fce605c8 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 c7c072fe2eba3e02a979726469a2229154c51f3b..9fa78b8a237acad26dcd4510c16d764746f6edcf 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 fc428977c3318281cecb27f357929f3bcdf52a31..417eecc1fe3fa8d87821e17bc73a0788e68a942d 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 207a813e8e019aefe1810260562019fe88333212..a265623bfb705284e1bf6d0cb202d57458df50a9 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 f0a64f2980c240924a1d82fa65460bd1fcea2354..1782bcc88b953a1c5e34258bb8f13302aeb62349 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 40e440a3754593fc9613dd1adbe475cfe53fbbd4..fcd583741893133c4b7b5cf31c15d749b6581761 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 2089008bbf55848ca25784f9491d8bef690291ba..3486a1e010b15358c7bb0cf7a40c4ce09b677699 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 c593196dfc869179e570ac07071fed0a71a0a573..045d3cc2ca24f2e20713526823852a91a89b4b35 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 33e7cce420390774765d89403e75935c6ed0222c..d4c13165e5a4ca158fe96459891c71bb57cda234 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 e5f1e88ffdb7250633f8c575aff57525cf471972..1f0b7639d5ffc778616c6d8cdf7904489ec44e09 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 b4f064d0e1b52c9e378bf36a6a5d866a79d3087f..ec717d8c122fbb0c5d6bd511de5e77864f4525c9 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 1e02e42193df31e0a30307b0b173b0fa1153ef85..29d5018440dee68ec5f58644aefa0c75f321de73 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 db7dc0945cd0c74e23cd3b3b3553f1e84577b81d..0fae711a2c320930aaae5fef4583e78929e48a06 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 520a55d64ae504cd45b975f1ed7df77ff0677b28..654247dd8c5a848f7157079065714ade58c1aa56 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 fc24db5d2e1a455bb9c711067aa1fe942c4561ef..6344f0bae535cfb338c93edc7ae7e77bb26ac72d 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 d143423f6a32586d71879e142fab37b821e5ab75..3f2a4221add76007db73e7b07fc2ea6276649338 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 ee30f51896974e74b279589bedf93f6437d9bb0f..785d21718b2b5328e8ddaaf39a6efecef73aab60 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 85a987b14b23bccfdd53820849af37ad0fd6647a..ead328da24758c18d916deef3bc24a68cdf75cc3 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 356f4b60fa3726a7701a7bc669c3df35336aea47..3304ea89e4e5a1912c76338086c04b524ea3613e 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 381c11c3893b72e0fa710150fdce4dccc5700f6a..c84348ded9e85383ed7ce95690866550420dc8b4 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 399b3bb1310eaafd512c1f33a21cedae04c9dacd..bd1d757e6a7d90f8e061832cc41c76c99eed380d 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 16885b2f1383ebf29d83123b8e809ee076f47f7a..f9500202ce85e2969bf690873ffb64c83d46a25b 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 b00142499777b306b50b5802e74cc038ce8273e3..c992306a9af377c9b5eb79edb04724c9ff87b3d1 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 41fa2f735c2b15b19504ecb3621658a5f2c50487..21469dd52df442631cb6632f10e96a25acaf09af 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);