diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 2c4bd9c92771e9682ad268789824963f72fc87ee..9c03f8ae16680b57fa76e46f956093b032d40295 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -25,6 +25,7 @@ #include "executor/executor.h" #include "executor/nodeValuesscan.h" +#include "utils/expandeddatum.h" static TupleTableSlot *ValuesNext(ValuesScanState *node); @@ -94,6 +95,7 @@ ValuesNext(ValuesScanState *node) List *exprstatelist; Datum *values; bool *isnull; + Form_pg_attribute *att; ListCell *lc; int resind; @@ -129,6 +131,7 @@ ValuesNext(ValuesScanState *node) */ values = slot->tts_values; isnull = slot->tts_isnull; + att = slot->tts_tupleDescriptor->attrs; resind = 0; foreach(lc, exprstatelist) @@ -139,6 +142,17 @@ ValuesNext(ValuesScanState *node) econtext, &isnull[resind], NULL); + + /* + * We must force any R/W expanded datums to read-only state, in + * case they are multiply referenced in the plan node's output + * expressions, or in case we skip the output projection and the + * output column is multiply referenced in higher plan nodes. + */ + values[resind] = MakeExpandedObjectReadOnly(values[resind], + isnull[resind], + att[resind]->attlen); + resind++; } diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 9563210080a223ca88280da1a583f3587d53255b..a2c36e44ba0aee3bff52c4884d855cff15cb7c45 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -5404,6 +5404,38 @@ select i, a from 1 | {1,1} (1 row) +explain (verbose, costs off) +select consumes_rw_array(a), a from returns_rw_array(1) a; + QUERY PLAN +-------------------------------------------- + Function Scan on public.returns_rw_array a + Output: consumes_rw_array(a), a + Function Call: returns_rw_array(1) +(3 rows) + +select consumes_rw_array(a), a from returns_rw_array(1) a; + consumes_rw_array | a +-------------------+------- + 1 | {1,1} +(1 row) + +explain (verbose, costs off) +select consumes_rw_array(a), a from + (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); + QUERY PLAN +--------------------------------------------------------------------- + Values Scan on "*VALUES*" + Output: consumes_rw_array("*VALUES*".column1), "*VALUES*".column1 +(2 rows) + +select consumes_rw_array(a), a from + (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); + consumes_rw_array | a +-------------------+------- + 1 | {1,1} + 2 | {2,2} +(2 rows) + -- -- Test access to call stack -- diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index b1226ea4a0f668b536276b8e5ac7e80e06733e14..776f2292ea59a4f3b3b2a92db50d9ce96902ac1a 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -4263,6 +4263,18 @@ select i, a from (select returns_rw_array(1) as a offset 0) ss, lateral consumes_rw_array(a) i; +explain (verbose, costs off) +select consumes_rw_array(a), a from returns_rw_array(1) a; + +select consumes_rw_array(a), a from returns_rw_array(1) a; + +explain (verbose, costs off) +select consumes_rw_array(a), a from + (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); + +select consumes_rw_array(a), a from + (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); + -- -- Test access to call stack