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