diff options
-rw-r--r-- | doc/src/sgml/plperl.sgml | 52 | ||||
-rw-r--r-- | doc/src/sgml/plpgsql.sgml | 27 | ||||
-rw-r--r-- | doc/src/sgml/spi.sgml | 28 | ||||
-rw-r--r-- | src/backend/commands/portalcmds.c | 4 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 4 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 20 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 57 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 3 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 11 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 4 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 4 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 5 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 34 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 5 | ||||
-rw-r--r-- | src/include/tcop/utility.h | 4 |
16 files changed, 152 insertions, 114 deletions
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index 12ac0157c3e..32a7b12c710 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.55 2006/05/30 11:40:21 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.56 2006/08/12 20:05:54 tgl Exp $ --> <chapter id="plperl"> <title>PL/Perl - Perl Procedural Language</title> @@ -244,18 +244,8 @@ $$ LANGUAGE plperl; SELECT * FROM perl_set(); </programlisting> - </para> - <para> - <application>PL/Perl</> does not currently have full support for - domain types: it treats a domain the same as the underlying scalar - type. This means that constraints associated with the domain will - not be enforced. This is not an issue for function arguments, but - it is a hazard if you declare a <application>PL/Perl</> function - as returning a domain type. - </para> - <para> If you wish to use the <literal>strict</> pragma with your code, the easiest way to do so is to <command>SET</> @@ -439,26 +429,26 @@ SELECT * from lotsa_md5(500); <para> The advantage of prepared queries is that is it possible to use one prepared plan for more - than one query execution. After the plan is not needed anymore, it must be freed with + than one query execution. After the plan is not needed anymore, it may be freed with <literal>spi_freeplan</literal>: </para> <para> <programlisting> CREATE OR REPLACE FUNCTION init() RETURNS INTEGER AS $$ - $_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL'); + $_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL'); $$ LANGUAGE plperl; CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$ - return spi_exec_prepared( - $_SHARED{my_plan}, - $_[0], - )->{rows}->[0]->{now}; + return spi_exec_prepared( + $_SHARED{my_plan}, + $_[0], + )->{rows}->[0]->{now}; $$ LANGUAGE plperl; CREATE OR REPLACE FUNCTION done() RETURNS INTEGER AS $$ - spi_freeplan( $_SHARED{my_plan}); - undef $_SHARED{my_plan}; + spi_freeplan( $_SHARED{my_plan}); + undef $_SHARED{my_plan}; $$ LANGUAGE plperl; SELECT init(); @@ -478,16 +468,14 @@ SELECT done(); </para> <para> - <literal>spi_cursor_close</literal> can be used to abort sequence of - <literal>spi_fetchrow</literal> calls. Normally, the call to - <literal>spi_fetchrow</literal> that returns <literal>undef</literal> is - the signal that there are no more rows to read. Also - that call automatically frees the cursor associated with the query. If it is desired not - to read all retuned rows, <literal>spi_cursor_close</literal> must be - called to avoid memory leaks. + Normally, <function>spi_fetchrow</> should be repeated until it + returns <literal>undef</literal>, indicating that there are no more + rows to read. The cursor is automatically freed when + <function>spi_fetchrow</> returns <literal>undef</literal>. + If you do not wish to read all the rows, instead call + <function>spi_cursor_close</> to free the cursor. + Failure to do so will result in memory leaks. </para> - - </listitem> </varlistentry> @@ -630,8 +618,8 @@ CREATE FUNCTION badfunc() RETURNS integer AS $$ return 1; $$ LANGUAGE plperl; </programlisting> - The creation of this function will fail as its use of a forbidden - operation will be be caught by the validator. + The creation of this function will fail as its use of a forbidden + operation will be be caught by the validator. </para> <para> @@ -748,8 +736,8 @@ $$ LANGUAGE plperl; <listitem> <para> Name of the table on which the trigger fired. This has been deprecated, - and could be removed in a future release. - Please use $_TD->{table_name} instead. + and could be removed in a future release. + Please use $_TD->{table_name} instead. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index ac5b2b4cfc6..fb2fe735a6c 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.97 2006/06/16 23:29:26 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.98 2006/08/12 20:05:54 tgl Exp $ --> <chapter id="plpgsql"> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> @@ -2040,9 +2040,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>; The <replaceable>target</replaceable> is a record variable, row variable, or comma-separated list of scalar variables. The <replaceable>target</replaceable> is successively assigned each row - resulting from the <replaceable>query</replaceable> (which must be a - <command>SELECT</command> command) and the loop body is executed for each - row. Here is an example: + resulting from the <replaceable>query</replaceable> and the loop body is + executed for each row. Here is an example: <programlisting> CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$ DECLARE @@ -2070,6 +2069,15 @@ $$ LANGUAGE plpgsql; </para> <para> + The <replaceable>query</replaceable> used in this type of <literal>FOR</> + statement can be any query that returns rows to the caller: + <command>SELECT</> (without <literal>INTO</>) is the most common case, + but you can also use <command>INSERT</>, <command>UPDATE</>, or + <command>DELETE</> with a <literal>RETURNING</> clause. Some utility + commands such as <command>EXPLAIN</> will work too. + </para> + + <para> The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over rows: <synopsis> @@ -2078,12 +2086,11 @@ FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</r <replaceable>statements</replaceable> END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> - This is like the previous form, except that the source - <command>SELECT</command> statement is specified as a string - expression, which is evaluated and replanned on each entry to - the <literal>FOR</> loop. This allows the programmer to choose the speed of - a preplanned query or the flexibility of a dynamic query, just - as with a plain <command>EXECUTE</command> statement. + This is like the previous form, except that the source query + is specified as a string expression, which is evaluated and replanned + on each entry to the <literal>FOR</> loop. This allows the programmer to + choose the speed of a preplanned query or the flexibility of a dynamic + query, just as with a plain <command>EXECUTE</command> statement. </para> <note> diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml index 4430b3e4c71..016874dce70 100644 --- a/doc/src/sgml/spi.sgml +++ b/doc/src/sgml/spi.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.45 2006/03/10 19:10:49 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.46 2006/08/12 20:05:54 tgl Exp $ --> <chapter id="spi"> <title>Server Programming Interface</title> @@ -535,15 +535,15 @@ typedef struct <term><symbol>SPI_ERROR_TRANSACTION</symbol></term> <listitem> <para> - if any command involving transaction manipulation was attempted - (<command>BEGIN</>, - <command>COMMIT</>, - <command>ROLLBACK</>, - <command>SAVEPOINT</>, - <command>PREPARE TRANSACTION</>, - <command>COMMIT PREPARED</>, - <command>ROLLBACK PREPARED</>, - or any variant thereof) + if any command involving transaction manipulation was attempted + (<command>BEGIN</>, + <command>COMMIT</>, + <command>ROLLBACK</>, + <command>SAVEPOINT</>, + <command>PREPARE TRANSACTION</>, + <command>COMMIT PREPARED</>, + <command>ROLLBACK PREPARED</>, + or any variant thereof) </para> </listitem> </varlistentry> @@ -917,10 +917,12 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>) <para> <function>SPI_is_cursor_plan</function> returns <symbol>true</symbol> if a plan prepared by <function>SPI_prepare</function> can be passed - as an argument to <function>SPI_cursor_open</function> and <symbol> - false</symbol> if that is not the case. The criteria are that the + as an argument to <function>SPI_cursor_open</function>, or + <symbol>false</symbol> if that is not the case. The criteria are that the <parameter>plan</parameter> represents one single command and that this - command is a <command>SELECT</command> without an <command>INTO</command> + command returns tuples to the caller; for example, <command>SELECT</> + is allowed unless it contains an <literal>INTO</> clause, and + <command>UPDATE</> is allowed only if it contains a <literal>RETURNING</> clause. </para> </refsect1> diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 8b9dd99d4e4..0685426a2f2 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.49 2006/08/08 01:23:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.50 2006/08/12 20:05:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,7 +87,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params) if (query->into) ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("DECLARE CURSOR may not specify INTO"))); if (query->rowMarks != NIL) ereport(ERROR, diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 05c4a542b84..4b8a166ffae 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.278 2006/08/12 02:52:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.279 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -497,7 +497,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) /* * if there is a result relation, initialize result relation stuff */ - if (parseTree->resultRelation != 0 && operation != CMD_SELECT) + if (parseTree->resultRelation) { List *resultRelations = parseTree->resultRelations; int numResultRelations; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 0da2abba77c..28462ba8b8b 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.104 2006/07/14 14:52:19 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.105 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -361,7 +361,9 @@ postquel_getnext(execution_state *es) * run it to completion. (If we run to completion then * ExecutorRun is guaranteed to return NULL.) */ - if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT) + if (LAST_POSTQUEL_COMMAND(es) && + es->qd->operation == CMD_SELECT && + es->qd->parsetree->into == NULL) count = 1L; else count = 0L; @@ -868,7 +870,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, JunkFilter **junkFilter) { Query *parse; - int cmd; + bool isSelect; List *tlist; ListCell *tlistitem; int tlistlen; @@ -893,15 +895,18 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* find the final query */ parse = (Query *) lfirst(list_tail(queryTreeList)); - cmd = parse->commandType; - tlist = parse->targetList; + /* + * Note: eventually replace this with QueryReturnsTuples? We'd need + * a more general method of determining the output type, though. + */ + isSelect = (parse->commandType == CMD_SELECT && parse->into == NULL); /* * The last query must be a SELECT if and only if return type isn't VOID. */ if (rettype == VOIDOID) { - if (cmd == CMD_SELECT) + if (isSelect) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("return type mismatch in function declared to return %s", @@ -911,7 +916,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, } /* by here, the function is declared to return some type */ - if (cmd != CMD_SELECT) + if (!isSelect) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("return type mismatch in function declared to return %s", @@ -921,6 +926,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* * Count the non-junk entries in the result targetlist. */ + tlist = parse->targetList; tlistlen = ExecCleanTargetListLength(tlist); fn_typtype = get_typtype(rettype); diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index c1f39f7c4f7..6c4cc585460 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.154 2006/08/12 02:52:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.155 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -837,26 +837,12 @@ SPI_cursor_open(const char *name, void *plan, planTree = (Plan *) linitial(ptlist); /* Must be a query that returns tuples */ - switch (queryTree->commandType) - { - case CMD_SELECT: - if (queryTree->into != NULL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open SELECT INTO query as cursor"))); - break; - case CMD_UTILITY: - if (!UtilityReturnsTuples(queryTree->utilityStmt)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open non-SELECT query as cursor"))); - break; - default: - ereport(ERROR, - (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("cannot open non-SELECT query as cursor"))); - break; - } + if (!QueryReturnsTuples(queryTree)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + /* translator: %s is name of a SQL command, eg INSERT */ + errmsg("cannot open %s query as cursor", + CreateQueryTag(queryTree)))); /* Reset SPI result (note we deliberately don't touch lastoid) */ SPI_processed = 0; @@ -876,7 +862,7 @@ SPI_cursor_open(const char *name, void *plan, portal = CreatePortal(name, false, false); } - /* Switch to portals memory and copy the parsetree and plan to there */ + /* Switch to portal's memory and copy the parsetree and plan to there */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); queryTree = copyObject(queryTree); planTree = copyObject(planTree); @@ -919,9 +905,9 @@ SPI_cursor_open(const char *name, void *plan, * Set up the portal. */ PortalDefineQuery(portal, - NULL, + NULL, /* no statement name */ spiplan->query, - "SELECT", /* don't have the raw parse tree... */ + CreateQueryTag(queryTree), list_make1(queryTree), list_make1(planTree), PortalGetHeapMemory(portal)); @@ -954,9 +940,16 @@ SPI_cursor_open(const char *name, void *plan, */ PortalStart(portal, paramLI, snapshot); - Assert(portal->strategy == PORTAL_ONE_SELECT || - portal->strategy == PORTAL_ONE_RETURNING || - portal->strategy == PORTAL_UTIL_SELECT); + /* + * If this test fails then we're out of sync with pquery.c about + * which queries can return tuples... + */ + if (portal->strategy == PORTAL_MULTI_QUERY) + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + /* translator: %s is name of a SQL command, eg INSERT */ + errmsg("cannot open %s query as cursor", + CreateQueryTag(queryTree)))); /* Return the created portal */ return portal; @@ -1046,12 +1039,12 @@ SPI_getargcount(void *plan) /* * Returns true if the plan contains exactly one command - * and that command originates from normal SELECT (i.e. - * *not* a SELECT ... INTO). In essence, the result indicates - * if the command can be used with SPI_cursor_open + * and that command returns tuples to the caller (eg, SELECT or + * INSERT ... RETURNING, but not SELECT ... INTO). In essence, + * the result indicates if the command can be used with SPI_cursor_open * * Parameters - * plan A plan previously prepared using SPI_prepare + * plan: A plan previously prepared using SPI_prepare */ bool SPI_is_cursor_plan(void *plan) @@ -1070,7 +1063,7 @@ SPI_is_cursor_plan(void *plan) { Query *queryTree = (Query *) linitial((List *) linitial(qtlist)); - if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL) + if (QueryReturnsTuples(queryTree)) return true; } return false; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index ad128605dcc..ef92d9d3c9f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.41 2006/08/12 02:52:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.42 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -623,7 +623,6 @@ is_simple_subquery(Query *subquery) */ if (!IsA(subquery, Query) || subquery->commandType != CMD_SELECT || - subquery->resultRelation != 0 || subquery->into != NULL) elog(ERROR, "subquery is bogus"); @@ -686,7 +685,6 @@ is_simple_union_all(Query *subquery) /* Let's just make sure it's a valid subselect ... */ if (!IsA(subquery, Query) || subquery->commandType != CMD_SELECT || - subquery->resultRelation != 0 || subquery->into != NULL) elog(ERROR, "subquery is bogus"); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 04f346d4625..71e727a7b4c 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.218 2006/08/12 02:52:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.219 2006/08/12 20:05:55 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2689,7 +2689,6 @@ inline_function(Oid funcid, Oid result_type, List *args, */ if (!IsA(querytree, Query) || querytree->commandType != CMD_SELECT || - querytree->resultRelation != 0 || querytree->into || querytree->hasAggs || querytree->hasSubLinks || diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 61242ef712e..39c7372733f 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.345 2006/08/12 02:52:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.346 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3116,6 +3116,15 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) /* Shouldn't get any extras, since grammar only allows SelectStmt */ if (extras_before || extras_after) elog(ERROR, "unexpected extra stuff in cursor statement"); + if (!IsA(stmt->query, Query) || + ((Query *) stmt->query)->commandType != CMD_SELECT) + elog(ERROR, "unexpected non-SELECT command in cursor statement"); + + /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */ + if (((Query *) stmt->query)->into) + ereport(ERROR, + (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), + errmsg("DECLARE CURSOR may not specify INTO"))); return result; } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d34529c74ee..021fb3fa553 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.155 2006/07/26 19:31:51 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.156 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -447,7 +447,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) if (query->commandType != CMD_SELECT) elog(ERROR, "expected SELECT query from subquery in FROM"); - if (query->resultRelation != 0 || query->into != NULL) + if (query->into != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("subquery in FROM may not have SELECT INTO"))); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 6a7117e98bf..0812c3d4418 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.196 2006/08/02 01:59:46 joe Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.197 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1104,7 +1104,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) elog(ERROR, "bad query in sub-select"); qtree = (Query *) linitial(qtrees); if (qtree->commandType != CMD_SELECT || - qtree->resultRelation != 0) + qtree->into != NULL) elog(ERROR, "bad query in sub-select"); sublink->subselect = (Node *) qtree; diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 3e1f65d8681..df24c6751c2 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.111 2006/07/18 17:42:00 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.112 2006/08/12 20:05:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -264,7 +264,8 @@ DefineQueryRewrite(RuleStmt *stmt) * ... the one action must be a SELECT, ... */ query = (Query *) linitial(action); - if (!is_instead || query->commandType != CMD_SELECT) + if (!is_instead || + query->commandType != CMD_SELECT || query->into != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rules on SELECT must have action INSTEAD SELECT"))); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 807b64360c8..7d6941ddcef 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.264 2006/08/12 02:52:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.265 2006/08/12 20:05:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1225,6 +1225,38 @@ UtilityTupleDescriptor(Node *parsetree) /* + * QueryReturnsTuples + * Return "true" if this Query will send output to the destination. + */ +bool +QueryReturnsTuples(Query *parsetree) +{ + switch (parsetree->commandType) + { + case CMD_SELECT: + /* returns tuples ... unless it's SELECT INTO */ + if (parsetree->into == NULL) + return true; + break; + case CMD_INSERT: + case CMD_UPDATE: + case CMD_DELETE: + /* the forms with RETURNING return tuples */ + if (parsetree->returningList) + return true; + break; + case CMD_UTILITY: + return UtilityReturnsTuples(parsetree->utilityStmt); + case CMD_UNKNOWN: + case CMD_NOTHING: + /* probably shouldn't get here */ + break; + } + return false; /* default */ +} + + +/* * CreateCommandTag * utility to get a string representation of the * command operation, given a raw (un-analyzed) parsetree. diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f0f40e002e1..222f5d26cc3 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.322 2006/08/12 02:52:06 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.323 2006/08/12 20:05:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,7 +89,8 @@ typedef struct Query Node *utilityStmt; /* non-null if this is a non-optimizable * statement */ - int resultRelation; /* target relation (index into rtable) */ + int resultRelation; /* rtable index of target relation for + * INSERT/UPDATE/DELETE; 0 for SELECT */ RangeVar *into; /* target relation for SELECT INTO */ List *intoOptions; /* options from WITH clause */ diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h index 09f96a262c7..6674dec129e 100644 --- a/src/include/tcop/utility.h +++ b/src/include/tcop/utility.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.27 2006/03/05 15:59:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.28 2006/08/12 20:05:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,8 @@ extern const char *CreateCommandTag(Node *parsetree); extern const char *CreateQueryTag(Query *parsetree); +extern bool QueryReturnsTuples(Query *parsetree); + extern bool QueryIsReadOnly(Query *parsetree); extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs); |