From 9a527f18488cb336b39ce86520dd25e438a59144 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 2 Apr 2007 18:49:29 +0000 Subject: [PATCH] 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. --- src/backend/executor/functions.c | 46 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 4781bd903a..1e1d450be5 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.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) * ANYELEMENT as rettype. (This means we can't check the type during function * definition of a polymorphic function.) * - * The return value is true if the function returns the entire tuple result - * of its final SELECT, and false otherwise. Note that because we allow - * "SELECT rowtype_expression", this may be false even when the declared + * This function returns true if the sql function returns the entire tuple + * result of its final SELECT, and false otherwise. Note that because we + * allow "SELECT rowtype_expression", this may be false even when the declared * function return type is a rowtype. * * 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, JunkFilter **junkFilter) { Query *parse; - bool isSelect; List *tlist; ListCell *tlistitem; int tlistlen; @@ -890,32 +889,30 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, parse = (Query *) lfirst(list_tail(queryTreeList)); /* - * 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 the last query isn't a SELECT, the return type must be VOID. + * + * Note: eventually replace this test with QueryReturnsTuples? We'd need + * a more general method of determining the output type, though. */ - if (rettype == VOIDOID) + if (!(parse->commandType == CMD_SELECT && parse->into == NULL)) { - if (isSelect) + if (rettype != VOIDOID) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("return type mismatch in function declared to return %s", 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; } - /* by here, the function is declared to return some type */ - if (!isSelect) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("return type mismatch in function declared to return %s", - format_type_be(rettype)), - errdetail("Function's final statement must be a SELECT."))); + /* + * OK, it's a SELECT, so it must return something matching the declared + * type. (We used to insist that the declared type not be VOID in this + * case, but that makes it hard to write a void function that exits + * after calling another void function. Instead, we insist that the + * SELECT return void ... so void is treated as if it were a scalar type + * below.) + */ /* * Count the non-junk entries in the result targetlist. @@ -927,10 +924,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, if (fn_typtype == TYPTYPE_BASE || 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 * of Postgres 7.2, we accept binary-compatible types too.) */ -- 2.40.0