diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 84398e8a960..f974febeee5 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -949,6 +949,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, * This can happen, for example, where the body of the function is * 'SELECT func2()', where func2 has the same return type as the * function that's calling it. + * + * XXX Note that if rettype is RECORD, the IsBinaryCoercible check + * will succeed for any composite restype. For the moment we rely on + * runtime type checking to catch any discrepancy, but it'd be nice to + * do better at parse time. */ if (tlistlen == 1) { diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 7029ee1738d..bc5df1f6f97 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -2289,6 +2289,10 @@ evaluate_function(Oid funcid, Oid result_type, List *args, * We must also beware of changing the volatility or strictness status of * functions by inlining them. * + * Also, at the moment we can't inline functions returning RECORD. This + * doesn't work in the general case because it discards information such + * as OUT-parameter declarations. + * * Returns a simplified expression if successful, or NULL if cannot * simplify the function. */ @@ -2320,6 +2324,7 @@ inline_function(Oid funcid, Oid result_type, List *args, if (funcform->prolang != SQLlanguageId || funcform->prosecdef || funcform->proretset || + funcform->prorettype == RECORDOID || funcform->pronargs != list_length(args)) return NULL; diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 49fc121f04f..a043bc1d338 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -528,3 +528,23 @@ CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; ERROR: cannot determine result data type DETAIL: A function returning "anyarray" or "anyelement" must have at least one argument of either type. +-- check handling of a SQL function with multiple OUT params (bug #5777) +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1) $$ language sql; +select * from foobar(); + column1 | column2 +---------+--------- + 1 | 2.1 +(1 row) + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2) $$ language sql; +select * from foobar(); -- fail +ERROR: function return row and query-specified return row do not match +DETAIL: Returned type integer at ordinal position 2, but query expects numeric. +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1, 3) $$ language sql; +select * from foobar(); -- fail +ERROR: function return row and query-specified return row do not match +DETAIL: Returned row contains 3 attributes, but query expects 2. +drop function foobar(); diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index 50495897acd..06bbd1b537d 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -261,3 +261,22 @@ DROP FUNCTION dup(anyelement); -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; + +-- check handling of a SQL function with multiple OUT params (bug #5777) + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1) $$ language sql; + +select * from foobar(); + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2) $$ language sql; + +select * from foobar(); -- fail + +create or replace function foobar(out integer, out numeric) as +$$ select (1, 2.1, 3) $$ language sql; + +select * from foobar(); -- fail + +drop function foobar();