aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execQual.c19
-rw-r--r--src/backend/executor/execTuples.c47
-rw-r--r--src/backend/executor/nodeSubqueryscan.c8
-rw-r--r--src/include/executor/tuptable.h1
-rw-r--r--src/test/regress/expected/plpgsql.out40
-rw-r--r--src/test/regress/sql/plpgsql.sql32
6 files changed, 89 insertions, 58 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index c0ca58bbb4d..01e04d3b14e 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5350,15 +5350,24 @@ ExecCleanTargetListLength(List *targetlist)
* of *isDone = ExprMultipleResult signifies a set element, and a return
* of *isDone = ExprEndResult signifies end of the set of tuple.
* We assume that *isDone has been initialized to ExprSingleResult by caller.
+ *
+ * Since fields of the result tuple might be multiply referenced in higher
+ * plan nodes, we have to force any read/write expanded values to read-only
+ * status. It's a bit annoying to have to do that for every projected
+ * expression; in the future, consider teaching the planner to detect
+ * actually-multiply-referenced Vars and insert an expression node that
+ * would do that only where really required.
*/
static bool
ExecTargetList(List *targetlist,
+ TupleDesc tupdesc,
ExprContext *econtext,
Datum *values,
bool *isnull,
ExprDoneCond *itemIsDone,
ExprDoneCond *isDone)
{
+ Form_pg_attribute *att = tupdesc->attrs;
MemoryContext oldContext;
ListCell *tl;
bool haveDoneSets;
@@ -5384,6 +5393,10 @@ ExecTargetList(List *targetlist,
&isnull[resind],
&itemIsDone[resind]);
+ values[resind] = MakeExpandedObjectReadOnly(values[resind],
+ isnull[resind],
+ att[resind]->attlen);
+
if (itemIsDone[resind] != ExprSingleResult)
{
/* We have a set-valued expression in the tlist */
@@ -5437,6 +5450,10 @@ ExecTargetList(List *targetlist,
&isnull[resind],
&itemIsDone[resind]);
+ values[resind] = MakeExpandedObjectReadOnly(values[resind],
+ isnull[resind],
+ att[resind]->attlen);
+
if (itemIsDone[resind] == ExprEndResult)
{
/*
@@ -5470,6 +5487,7 @@ ExecTargetList(List *targetlist,
econtext,
&isnull[resind],
&itemIsDone[resind]);
+ /* no need for MakeExpandedObjectReadOnly */
}
}
@@ -5595,6 +5613,7 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
if (projInfo->pi_targetlist)
{
if (!ExecTargetList(projInfo->pi_targetlist,
+ slot->tts_tupleDescriptor,
econtext,
slot->tts_values,
slot->tts_isnull,
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index dfe56226568..2b81f60a519 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -88,7 +88,6 @@
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
#include "utils/builtins.h"
-#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
@@ -813,52 +812,6 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true);
}
-/* --------------------------------
- * ExecMakeSlotContentsReadOnly
- * Mark any R/W expanded datums in the slot as read-only.
- *
- * This is needed when a slot that might contain R/W datum references is to be
- * used as input for general expression evaluation. Since the expression(s)
- * might contain more than one Var referencing the same R/W datum, we could
- * get wrong answers if functions acting on those Vars thought they could
- * modify the expanded value in-place.
- *
- * For notational reasons, we return the same slot passed in.
- * --------------------------------
- */
-TupleTableSlot *
-ExecMakeSlotContentsReadOnly(TupleTableSlot *slot)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
- Assert(!slot->tts_isempty);
-
- /*
- * If the slot contains a physical tuple, it can't contain any expanded
- * datums, because we flatten those when making a physical tuple. This
- * might change later; but for now, we need do nothing unless the slot is
- * virtual.
- */
- if (slot->tts_tuple == NULL)
- {
- Form_pg_attribute *att = slot->tts_tupleDescriptor->attrs;
- int attnum;
-
- for (attnum = 0; attnum < slot->tts_nvalid; attnum++)
- {
- slot->tts_values[attnum] =
- MakeExpandedObjectReadOnly(slot->tts_values[attnum],
- slot->tts_isnull[attnum],
- att[attnum]->attlen);
- }
- }
-
- return slot;
-}
-
/* ----------------------------------------------------------------
* convenience initialization routines
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 0304b15b158..9bafc626772 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -56,15 +56,7 @@ SubqueryNext(SubqueryScanState *node)
* We just return the subplan's result slot, rather than expending extra
* cycles for ExecCopySlot(). (Our own ScanTupleSlot is used only for
* EvalPlanQual rechecks.)
- *
- * We do need to mark the slot contents read-only to prevent interference
- * between different functions reading the same datum from the slot. It's
- * a bit hokey to do this to the subplan's slot, but should be safe
- * enough.
*/
- if (!TupIsNull(slot))
- slot = ExecMakeSlotContentsReadOnly(slot);
-
return slot;
}
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index db05f9188ad..5ac0b6a1f61 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -163,7 +163,6 @@ extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
-extern TupleTableSlot *ExecMakeSlotContentsReadOnly(TupleTableSlot *slot);
/* in access/common/heaptuple.c */
extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index e30c579a2ae..9563210080a 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5368,7 +5368,45 @@ ERROR: value for domain orderedarray violates check constraint "sorted"
CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
drop function arrayassign1();
drop function testoa(x1 int, x2 int, x3 int);
--- access to call stack
+--
+-- Test handling of expanded arrays
+--
+create function returns_rw_array(int) returns int[]
+language plpgsql as $$
+ declare r int[];
+ begin r := array[$1, $1]; return r; end;
+$$ stable;
+create function consumes_rw_array(int[]) returns int
+language plpgsql as $$
+ begin return $1[1]; end;
+$$ stable;
+-- bug #14174
+explain (verbose, costs off)
+select i, a from
+ (select returns_rw_array(1) as a offset 0) ss,
+ lateral consumes_rw_array(a) i;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Nested Loop
+ Output: i.i, (returns_rw_array(1))
+ -> Result
+ Output: returns_rw_array(1)
+ -> Function Scan on public.consumes_rw_array i
+ Output: i.i
+ Function Call: consumes_rw_array((returns_rw_array(1)))
+(7 rows)
+
+select i, a from
+ (select returns_rw_array(1) as a offset 0) ss,
+ lateral consumes_rw_array(a) i;
+ i | a
+---+-------
+ 1 | {1,1}
+(1 row)
+
+--
+-- Test access to call stack
+--
create function inner_func(int)
returns int as $$
declare _context text;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 7ffef895b6b..b1226ea4a0f 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4237,7 +4237,37 @@ select testoa(1,2,1); -- fail at update
drop function arrayassign1();
drop function testoa(x1 int, x2 int, x3 int);
--- access to call stack
+
+--
+-- Test handling of expanded arrays
+--
+
+create function returns_rw_array(int) returns int[]
+language plpgsql as $$
+ declare r int[];
+ begin r := array[$1, $1]; return r; end;
+$$ stable;
+
+create function consumes_rw_array(int[]) returns int
+language plpgsql as $$
+ begin return $1[1]; end;
+$$ stable;
+
+-- bug #14174
+explain (verbose, costs off)
+select i, a from
+ (select returns_rw_array(1) as a offset 0) ss,
+ lateral consumes_rw_array(a) i;
+
+select i, a from
+ (select returns_rw_array(1) as a offset 0) ss,
+ lateral consumes_rw_array(a) i;
+
+
+--
+-- Test access to call stack
+--
+
create function inner_func(int)
returns int as $$
declare _context text;