Skip to content

Commit 309857f

Browse files
committed
Fix handling of R/W expanded datums that are passed to SQL functions.
fmgr_sql must make expanded-datum arguments read-only, because it's possible that the function body will pass the argument to more than one callee function. If one of those functions takes the datum's R/W property as license to scribble on it, then later callees will see an unexpected value, leading to wrong answers. From a performance standpoint, it'd be nice to skip this in the common case that the argument value is passed to only one callee. However, detecting that seems fairly hard, and certainly not something that I care to attempt in a back-patched bug fix. Per report from Adam Mackler. This has been broken since we invented expanded datums, so back-patch to all supported branches. Discussion: https://postgr.es/m/WScDU5qfoZ7PB2gXwNqwGGgDPmWzz08VdydcPFLhOwUKZcdWbblbo-0Lku-qhuEiZoXJ82jpiQU4hOjOcrevYEDeoAvz6nR0IU4IHhXnaCA=@mackler.email Discussion: https://postgr.es/m/[email protected]
1 parent 92dc33a commit 309857f

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

src/backend/executor/functions.c

+17-2
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
939939
if (nargs > 0)
940940
{
941941
ParamListInfo paramLI;
942+
Oid *argtypes = fcache->pinfo->argtypes;
942943

943944
if (fcache->paramLI == NULL)
944945
{
@@ -955,10 +956,24 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
955956
{
956957
ParamExternData *prm = &paramLI->params[i];
957958

958-
prm->value = fcinfo->args[i].value;
959+
/*
960+
* If an incoming parameter value is a R/W expanded datum, we
961+
* force it to R/O. We'd be perfectly entitled to scribble on it,
962+
* but the problem is that if the parameter is referenced more
963+
* than once in the function, earlier references might mutate the
964+
* value seen by later references, which won't do at all. We
965+
* could do better if we could be sure of the number of Param
966+
* nodes in the function's plans; but we might not have planned
967+
* all the statements yet, nor do we have plan tree walker
968+
* infrastructure. (Examining the parse trees is not good enough,
969+
* because of possible function inlining during planning.)
970+
*/
959971
prm->isnull = fcinfo->args[i].isnull;
972+
prm->value = MakeExpandedObjectReadOnly(fcinfo->args[i].value,
973+
prm->isnull,
974+
get_typlen(argtypes[i]));
960975
prm->pflags = 0;
961-
prm->ptype = fcache->pinfo->argtypes[i];
976+
prm->ptype = argtypes[i];
962977
}
963978
}
964979
else

src/test/regress/expected/create_function_sql.out

+18-1
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,22 @@ SELECT * FROM voidtest5(3);
666666
-----------
667667
(0 rows)
668668

669+
-- Regression tests for bugs:
670+
-- Check that arguments that are R/W expanded datums aren't corrupted by
671+
-- multiple uses. This test knows that array_append() returns a R/W datum
672+
-- and will modify a R/W array input in-place. We use SETOF to prevent
673+
-- inlining of the SQL function.
674+
CREATE FUNCTION double_append(anyarray, anyelement) RETURNS SETOF anyarray
675+
LANGUAGE SQL IMMUTABLE AS
676+
$$ SELECT array_append($1, $2) || array_append($1, $2) $$;
677+
SELECT double_append(array_append(ARRAY[q1], q2), q3)
678+
FROM (VALUES(1,2,3), (4,5,6)) v(q1,q2,q3);
679+
double_append
680+
---------------
681+
{1,2,3,1,2,3}
682+
{4,5,6,4,5,6}
683+
(2 rows)
684+
669685
-- Things that shouldn't work:
670686
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
671687
AS 'SELECT ''not an integer'';';
@@ -692,7 +708,7 @@ CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
692708
ERROR: only one AS item needed for language "sql"
693709
-- Cleanup
694710
DROP SCHEMA temp_func_test CASCADE;
695-
NOTICE: drop cascades to 29 other objects
711+
NOTICE: drop cascades to 30 other objects
696712
DETAIL: drop cascades to function functest_a_1(text,date)
697713
drop cascades to function functest_a_2(text[])
698714
drop cascades to function functest_a_3()
@@ -722,5 +738,6 @@ drop cascades to function voidtest2(integer,integer)
722738
drop cascades to function voidtest3(integer)
723739
drop cascades to function voidtest4(integer)
724740
drop cascades to function voidtest5(integer)
741+
drop cascades to function double_append(anyarray,anyelement)
725742
DROP USER regress_unpriv_user;
726743
RESET search_path;

src/test/regress/sql/create_function_sql.sql

+13
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,19 @@ CREATE FUNCTION voidtest5(a int) RETURNS SETOF VOID LANGUAGE SQL AS
385385
$$ SELECT generate_series(1, a) $$ STABLE;
386386
SELECT * FROM voidtest5(3);
387387

388+
-- Regression tests for bugs:
389+
390+
-- Check that arguments that are R/W expanded datums aren't corrupted by
391+
-- multiple uses. This test knows that array_append() returns a R/W datum
392+
-- and will modify a R/W array input in-place. We use SETOF to prevent
393+
-- inlining of the SQL function.
394+
CREATE FUNCTION double_append(anyarray, anyelement) RETURNS SETOF anyarray
395+
LANGUAGE SQL IMMUTABLE AS
396+
$$ SELECT array_append($1, $2) || array_append($1, $2) $$;
397+
398+
SELECT double_append(array_append(ARRAY[q1], q2), q3)
399+
FROM (VALUES(1,2,3), (4,5,6)) v(q1,q2,q3);
400+
388401
-- Things that shouldn't work:
389402

390403
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL

0 commit comments

Comments
 (0)