diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2023-04-16 14:16:40 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2023-04-16 14:16:40 -0400 |
commit | 78d5952dd0e66afc4447eec07f770991fa406cce (patch) | |
tree | d2be84d4bcfb7230bc2957aa4b1daf3789a2cadb /src/test | |
parent | 1c54b93a8cf959a826dfabd6cae55dce255df2f5 (diff) | |
download | postgresql-78d5952dd0e66afc4447eec07f770991fa406cce.tar.gz |
Ensure result of an aggregate's finalfunc is made read-only.
The finalfunc might return a read-write expanded object. If we
de-duplicate multiple call sites for the aggregate, any function(s)
receiving the aggregate result earlier could alter or destroy the
value that reaches the ones called later. This is a brown-paper-bag
bug in commit 42b746d4c, because we actually considered the need
for read-only-ness but failed to realize that it applied to the case
with a finalfunc as well as the case without.
Per report from Justin Pryzby. New error in HEAD,
no need for back-patch.
Discussion: https://postgr.es/m/ZDm5TuKsh3tzoEjz@telsasoft.com
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/regress/expected/aggregates.out | 37 | ||||
-rw-r--r-- | src/test/regress/sql/aggregates.sql | 39 |
2 files changed, 76 insertions, 0 deletions
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index f0517f95b6..d8271da4d1 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -2759,6 +2759,43 @@ SELECT balk(hundred) FROM tenk1; (1 row) ROLLBACK; +-- test multiple usage of an aggregate whose finalfn returns a R/W datum +BEGIN; +CREATE FUNCTION rwagg_sfunc(x anyarray, y anyarray) RETURNS anyarray +LANGUAGE plpgsql IMMUTABLE AS $$ +BEGIN + RETURN array_fill(y[1], ARRAY[4]); +END; +$$; +CREATE FUNCTION rwagg_finalfunc(x anyarray) RETURNS anyarray +LANGUAGE plpgsql STRICT IMMUTABLE AS $$ +DECLARE + res x%TYPE; +BEGIN + -- assignment is essential for this test, it expands the array to R/W + res := array_fill(x[1], ARRAY[4]); + RETURN res; +END; +$$; +CREATE AGGREGATE rwagg(anyarray) ( + STYPE = anyarray, + SFUNC = rwagg_sfunc, + FINALFUNC = rwagg_finalfunc +); +CREATE FUNCTION eatarray(x real[]) RETURNS real[] +LANGUAGE plpgsql STRICT IMMUTABLE AS $$ +BEGIN + x[1] := x[1] + 1; + RETURN x; +END; +$$; +SELECT eatarray(rwagg(ARRAY[1.0::real])), eatarray(rwagg(ARRAY[1.0::real])); + eatarray | eatarray +-----------+----------- + {2,1,1,1} | {2,1,1,1} +(1 row) + +ROLLBACK; -- test coverage for aggregate combine/serial/deserial functions BEGIN; SET parallel_setup_cost = 0; diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 1783d19bd5..75c78be640 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -1207,6 +1207,45 @@ SELECT balk(hundred) FROM tenk1; ROLLBACK; +-- test multiple usage of an aggregate whose finalfn returns a R/W datum +BEGIN; + +CREATE FUNCTION rwagg_sfunc(x anyarray, y anyarray) RETURNS anyarray +LANGUAGE plpgsql IMMUTABLE AS $$ +BEGIN + RETURN array_fill(y[1], ARRAY[4]); +END; +$$; + +CREATE FUNCTION rwagg_finalfunc(x anyarray) RETURNS anyarray +LANGUAGE plpgsql STRICT IMMUTABLE AS $$ +DECLARE + res x%TYPE; +BEGIN + -- assignment is essential for this test, it expands the array to R/W + res := array_fill(x[1], ARRAY[4]); + RETURN res; +END; +$$; + +CREATE AGGREGATE rwagg(anyarray) ( + STYPE = anyarray, + SFUNC = rwagg_sfunc, + FINALFUNC = rwagg_finalfunc +); + +CREATE FUNCTION eatarray(x real[]) RETURNS real[] +LANGUAGE plpgsql STRICT IMMUTABLE AS $$ +BEGIN + x[1] := x[1] + 1; + RETURN x; +END; +$$; + +SELECT eatarray(rwagg(ARRAY[1.0::real])), eatarray(rwagg(ARRAY[1.0::real])); + +ROLLBACK; + -- test coverage for aggregate combine/serial/deserial functions BEGIN; |