diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execQual.c | 12 | ||||
-rw-r--r-- | src/backend/executor/execTuples.c | 47 | ||||
-rw-r--r-- | src/backend/executor/nodeSubqueryscan.c | 8 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 21 |
4 files changed, 80 insertions, 8 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d94fe581df3..e5994112a42 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -4248,7 +4248,6 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr; Datum result; - ArrayType *array; FunctionCallInfoData locfcinfo; result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); @@ -4265,14 +4264,12 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, if (!OidIsValid(acoerce->elemfuncid)) { /* Detoast input array if necessary, and copy in any case */ - array = DatumGetArrayTypePCopy(result); + ArrayType *array = DatumGetArrayTypePCopy(result); + ARR_ELEMTYPE(array) = astate->resultelemtype; PG_RETURN_ARRAYTYPE_P(array); } - /* Detoast input array if necessary, but don't make a useless copy */ - array = DatumGetArrayTypeP(result); - /* Initialize function cache if first time through */ if (astate->elemfunc.fn_oid == InvalidOid) { @@ -4302,15 +4299,14 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, */ InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, InvalidOid, NULL, NULL); - locfcinfo.arg[0] = PointerGetDatum(array); + locfcinfo.arg[0] = result; locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.argnull[2] = false; - return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, - astate->amstate); + return array_map(&locfcinfo, astate->resultelemtype, astate->amstate); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 753754dce66..a05d8b11158 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -88,6 +88,7 @@ #include "nodes/nodeFuncs.h" #include "storage/bufmgr.h" #include "utils/builtins.h" +#include "utils/expandeddatum.h" #include "utils/lsyscache.h" #include "utils/typcache.h" @@ -812,6 +813,52 @@ 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 3f66e243d2a..e5d1e540c46 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -56,7 +56,15 @@ 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/backend/executor/spi.c b/src/backend/executor/spi.c index 557d153f2ab..472de41f9b4 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1015,6 +1015,27 @@ SPI_pfree(void *pointer) pfree(pointer); } +Datum +SPI_datumTransfer(Datum value, bool typByVal, int typLen) +{ + MemoryContext oldcxt = NULL; + Datum result; + + if (_SPI_curid + 1 == _SPI_connected) /* connected */ + { + if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) + elog(ERROR, "SPI stack corrupted"); + oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); + } + + result = datumTransfer(value, typByVal, typLen); + + if (oldcxt) + MemoryContextSwitchTo(oldcxt); + + return result; +} + void SPI_freetuple(HeapTuple tuple) { |