diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 3630f5d9668..22c167574be 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -902,6 +902,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache, if (nargs > 0) { ParamListInfo paramLI; + Oid *argtypes = fcache->pinfo->argtypes; int i; if (fcache->paramLI == NULL) @@ -928,10 +929,24 @@ postquel_sub_params(SQLFunctionCachePtr fcache, { ParamExternData *prm = ¶mLI->params[i]; - prm->value = fcinfo->arg[i]; + /* + * If an incoming parameter value is a R/W expanded datum, we + * force it to R/O. We'd be perfectly entitled to scribble on it, + * but the problem is that if the parameter is referenced more + * than once in the function, earlier references might mutate the + * value seen by later references, which won't do at all. We + * could do better if we could be sure of the number of Param + * nodes in the function's plans; but we might not have planned + * all the statements yet, nor do we have plan tree walker + * infrastructure. (Examining the parse trees is not good enough, + * because of possible function inlining during planning.) + */ prm->isnull = fcinfo->argnull[i]; + prm->value = MakeExpandedObjectReadOnly(fcinfo->arg[i], + prm->isnull, + get_typlen(argtypes[i])); prm->pflags = 0; - prm->ptype = fcache->pinfo->argtypes[i]; + prm->ptype = argtypes[i]; } } else diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out index b5e19485e56..e43aebb148a 100644 --- a/src/test/regress/expected/create_function_3.out +++ b/src/test/regress/expected/create_function_3.out @@ -227,9 +227,25 @@ ERROR: could not find a function named "functest_b_1" DROP FUNCTION functest_b_2; -- error, ambiguous ERROR: function name "functest_b_2" is not unique HINT: Specify the argument list to select the function unambiguously. --- Cleanups +-- Regression tests for bugs: +-- Check that arguments that are R/W expanded datums aren't corrupted by +-- multiple uses. This test knows that array_append() returns a R/W datum +-- and will modify a R/W array input in-place. We use SETOF to prevent +-- inlining of the SQL function. +CREATE FUNCTION double_append(anyarray, anyelement) RETURNS SETOF anyarray +LANGUAGE SQL IMMUTABLE AS +$$ SELECT array_append($1, $2) || array_append($1, $2) $$; +SELECT double_append(array_append(ARRAY[q1], q2), q3) + FROM (VALUES(1,2,3), (4,5,6)) v(q1,q2,q3); + double_append +--------------- + {1,2,3,1,2,3} + {4,5,6,4,5,6} +(2 rows) + +-- Cleanup DROP SCHEMA temp_func_test CASCADE; -NOTICE: drop cascades to 16 other objects +NOTICE: drop cascades to 17 other objects DETAIL: drop cascades to function functest_a_1(text,date) drop cascades to function functest_a_2(text[]) drop cascades to function functest_a_3() @@ -246,5 +262,6 @@ drop cascades to function functext_f_2(integer) drop cascades to function functext_f_3(integer) drop cascades to function functext_f_4(integer) drop cascades to function functest_b_2(bigint) +drop cascades to function double_append(anyarray,anyelement) DROP USER regress_unpriv_user; RESET search_path; diff --git a/src/test/regress/sql/create_function_3.sql b/src/test/regress/sql/create_function_3.sql index 0a0e407aaba..645102b178c 100644 --- a/src/test/regress/sql/create_function_3.sql +++ b/src/test/regress/sql/create_function_3.sql @@ -166,8 +166,20 @@ DROP FUNCTION functest_b_1; DROP FUNCTION functest_b_1; -- error, not found DROP FUNCTION functest_b_2; -- error, ambiguous +-- Regression tests for bugs: --- Cleanups +-- Check that arguments that are R/W expanded datums aren't corrupted by +-- multiple uses. This test knows that array_append() returns a R/W datum +-- and will modify a R/W array input in-place. We use SETOF to prevent +-- inlining of the SQL function. +CREATE FUNCTION double_append(anyarray, anyelement) RETURNS SETOF anyarray +LANGUAGE SQL IMMUTABLE AS +$$ SELECT array_append($1, $2) || array_append($1, $2) $$; + +SELECT double_append(array_append(ARRAY[q1], q2), q3) + FROM (VALUES(1,2,3), (4,5,6)) v(q1,q2,q3); + +-- Cleanup DROP SCHEMA temp_func_test CASCADE; DROP USER regress_unpriv_user; RESET search_path;