Commit 9a527f18 authored by Tom Lane's avatar Tom Lane

Fix check_sql_fn_retval to allow the case where a SQL function declared to

return void ends with a SELECT, if that SELECT has a single result that is
also of type void.  Without this, it's hard to write a void function that
calls another void function.  Per gripe from Peter.

Back-patch as far as 8.0.
parent cac01fc0
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.113 2007/04/02 03:49:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.114 2007/04/02 18:49:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -849,9 +849,9 @@ ShutdownSQLFunction(Datum arg) ...@@ -849,9 +849,9 @@ ShutdownSQLFunction(Datum arg)
* ANYELEMENT as rettype. (This means we can't check the type during function * ANYELEMENT as rettype. (This means we can't check the type during function
* definition of a polymorphic function.) * definition of a polymorphic function.)
* *
* The return value is true if the function returns the entire tuple result * This function returns true if the sql function returns the entire tuple
* of its final SELECT, and false otherwise. Note that because we allow * result of its final SELECT, and false otherwise. Note that because we
* "SELECT rowtype_expression", this may be false even when the declared * allow "SELECT rowtype_expression", this may be false even when the declared
* function return type is a rowtype. * function return type is a rowtype.
* *
* If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined * If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
...@@ -864,7 +864,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, ...@@ -864,7 +864,6 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
JunkFilter **junkFilter) JunkFilter **junkFilter)
{ {
Query *parse; Query *parse;
bool isSelect;
List *tlist; List *tlist;
ListCell *tlistitem; ListCell *tlistitem;
int tlistlen; int tlistlen;
...@@ -890,32 +889,30 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, ...@@ -890,32 +889,30 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
parse = (Query *) lfirst(list_tail(queryTreeList)); parse = (Query *) lfirst(list_tail(queryTreeList));
/* /*
* Note: eventually replace this with QueryReturnsTuples? We'd need a * If the last query isn't a SELECT, the return type must be VOID.
* more general method of determining the output type, though. *
*/ * Note: eventually replace this test with QueryReturnsTuples? We'd need
isSelect = (parse->commandType == CMD_SELECT && parse->into == NULL); * a more general method of determining the output type, though.
/*
* The last query must be a SELECT if and only if return type isn't VOID.
*/ */
if (rettype == VOIDOID) if (!(parse->commandType == CMD_SELECT && parse->into == NULL))
{ {
if (isSelect) if (rettype != VOIDOID)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s", errmsg("return type mismatch in function declared to return %s",
format_type_be(rettype)), format_type_be(rettype)),
errdetail("Function's final statement must not be a SELECT."))); errdetail("Function's final statement must be a SELECT.")));
return false; return false;
} }
/* by here, the function is declared to return some type */ /*
if (!isSelect) * OK, it's a SELECT, so it must return something matching the declared
ereport(ERROR, * type. (We used to insist that the declared type not be VOID in this
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), * case, but that makes it hard to write a void function that exits
errmsg("return type mismatch in function declared to return %s", * after calling another void function. Instead, we insist that the
format_type_be(rettype)), * SELECT return void ... so void is treated as if it were a scalar type
errdetail("Function's final statement must be a SELECT."))); * below.)
*/
/* /*
* Count the non-junk entries in the result targetlist. * Count the non-junk entries in the result targetlist.
...@@ -927,10 +924,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, ...@@ -927,10 +924,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
if (fn_typtype == TYPTYPE_BASE || if (fn_typtype == TYPTYPE_BASE ||
fn_typtype == TYPTYPE_DOMAIN || fn_typtype == TYPTYPE_DOMAIN ||
fn_typtype == TYPTYPE_ENUM) fn_typtype == TYPTYPE_ENUM ||
rettype == VOIDOID)
{ {
/* /*
* For base-type returns, the target list should have exactly one * For scalar-type returns, the target list should have exactly one
* entry, and its type should agree with what the user declared. (As * entry, and its type should agree with what the user declared. (As
* of Postgres 7.2, we accept binary-compatible types too.) * of Postgres 7.2, we accept binary-compatible types too.)
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment