aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execMain.c
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2019-03-01 10:37:57 -0800
committerAndres Freund <andres@anarazel.de>2019-03-01 10:37:57 -0800
commitad0bda5d24ea2bcc72b5e50020e3c79bab10836b (patch)
tree9cf0810b842e4de24460fa8e20b21957a9e8ad01 /src/backend/executor/execMain.c
parent6cbdbd9e8d8f2986fde44f2431ed8d0c8fce7f5d (diff)
downloadpostgresql-ad0bda5d24ea2bcc72b5e50020e3c79bab10836b.tar.gz
postgresql-ad0bda5d24ea2bcc72b5e50020e3c79bab10836b.zip
Store tuples for EvalPlanQual in slots, rather than as HeapTuples.
For the upcoming pluggable table access methods it's quite inconvenient to store tuples as HeapTuples, as that'd require converting tuples from a their native format into HeapTuples. Instead use slots to manage epq tuples. To fit into that scheme, change the foreign data wrapper callback RefetchForeignRow, to store the tuple in a slot. Insist on using the caller provided slot, so it conveniently can be stored in the corresponding EPQ slot. As there is no in core user of RefetchForeignRow, that change was done blindly, but we plan to test that soon. To avoid duplicating that work for row locks, move row locks to just directly use the EPQ slots - it previously temporarily stored tuples in LockRowsState.lr_curtuples, but that doesn't seem beneficial, given we'd possibly end up with a significant number of additional slots. The behaviour of es_epqTupleSet[rti -1] is now checked by es_epqTupleSlot[rti -1] != NULL, as that is distinguishable from a slot containing an empty tuple. Author: Andres Freund, Haribabu Kommi, Ashutosh Bapat Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r--src/backend/executor/execMain.c183
1 files changed, 78 insertions, 105 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 00d8e8fc585..61be56fe0b7 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -977,8 +977,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_tupleTable = NIL;
/* mark EvalPlanQual not active */
- estate->es_epqTuple = NULL;
- estate->es_epqTupleSet = NULL;
+ estate->es_epqTupleSlot = NULL;
estate->es_epqScanDone = NULL;
/*
@@ -2441,35 +2440,29 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
ItemPointer tid, TransactionId priorXmax)
{
TupleTableSlot *slot;
- HeapTuple copyTuple;
+ TupleTableSlot *testslot;
Assert(rti > 0);
/*
- * Get and lock the updated version of the row; if fail, return NULL.
+ * Need to run a recheck subquery. Initialize or reinitialize EPQ state.
*/
- copyTuple = EvalPlanQualFetch(estate, relation, lockmode, LockWaitBlock,
- tid, priorXmax);
+ EvalPlanQualBegin(epqstate, estate);
- if (copyTuple == NULL)
+ /*
+ * Get and lock the updated version of the row; if fail, return NULL.
+ */
+ testslot = EvalPlanQualSlot(epqstate, relation, rti);
+ if (!EvalPlanQualFetch(estate, relation, lockmode, LockWaitBlock,
+ tid, priorXmax,
+ testslot))
return NULL;
/*
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
- *tid = copyTuple->t_self;
-
- /*
- * Need to run a recheck subquery. Initialize or reinitialize EPQ state.
- */
- EvalPlanQualBegin(epqstate, estate);
-
- /*
- * Free old test tuple, if any, and store new tuple where relation's scan
- * node will see it
- */
- EvalPlanQualSetTuple(epqstate, rti, copyTuple);
+ *tid = testslot->tts_tid;
/*
* Fetch any non-locked source rows
@@ -2496,7 +2489,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* re-used to test a tuple for a different relation. (Not clear that can
* really happen, but let's be safe.)
*/
- EvalPlanQualSetTuple(epqstate, rti, NULL);
+ ExecClearTuple(testslot);
return slot;
}
@@ -2510,21 +2503,22 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
* wait_policy - requested lock wait policy
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
+ * slot - slot to store newest tuple version
*
- * Returns a palloc'd copy of the newest tuple version, or NULL if we find
- * that there is no newest version (ie, the row was deleted not updated).
- * We also return NULL if the tuple is locked and the wait policy is to skip
+ * Returns true, with slot containing the newest tuple version, or false if we
+ * find that there is no newest version (ie, the row was deleted not updated).
+ * We also return false if the tuple is locked and the wait policy is to skip
* such tuples.
*
* If successful, we have locked the newest tuple version, so caller does not
* need to worry about it changing anymore.
*/
-HeapTuple
+bool
EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
LockWaitPolicy wait_policy,
- ItemPointer tid, TransactionId priorXmax)
+ ItemPointer tid, TransactionId priorXmax,
+ TupleTableSlot *slot)
{
- HeapTuple copyTuple = NULL;
HeapTupleData tuple;
SnapshotData SnapshotDirty;
@@ -2557,7 +2551,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
priorXmax))
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/* otherwise xmin should not be dirty... */
@@ -2580,7 +2574,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
break;
case LockWaitSkip:
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
- return NULL; /* skip instead of waiting */
+ return false; /* skip instead of waiting */
break;
case LockWaitError:
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
@@ -2608,7 +2602,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
@@ -2640,7 +2634,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
* now, treat the tuple as deleted and do not process.
*/
ReleaseBuffer(buffer);
- return NULL;
+ return false;
case HeapTupleMayBeUpdated:
/* successfully locked */
@@ -2668,11 +2662,11 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
continue;
}
/* tuple was deleted, so give up */
- return NULL;
+ return false;
case HeapTupleWouldBlock:
ReleaseBuffer(buffer);
- return NULL;
+ return false;
case HeapTupleInvisible:
elog(ERROR, "attempted to lock invisible tuple");
@@ -2682,14 +2676,14 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
ReleaseBuffer(buffer);
elog(ERROR, "unrecognized heap_lock_tuple status: %u",
test);
- return NULL; /* keep compiler quiet */
+ return false; /* keep compiler quiet */
}
/*
- * We got tuple - now copy it for use by recheck query.
+ * We got tuple - store it for use by the recheck query.
*/
- copyTuple = heap_copytuple(&tuple);
- ReleaseBuffer(buffer);
+ ExecStorePinnedBufferHeapTuple(&tuple, slot, buffer);
+ ExecMaterializeSlot(slot);
break;
}
@@ -2700,7 +2694,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
if (tuple.t_data == NULL)
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
@@ -2710,7 +2704,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
priorXmax))
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
@@ -2737,7 +2731,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/* updated, so look at the updated row */
@@ -2748,10 +2742,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, LockTupleMode lockmode,
/* loop back to fetch next in chain */
}
- /*
- * Return the copied tuple
- */
- return copyTuple;
+ /* signal success */
+ return true;
}
/*
@@ -2792,38 +2784,36 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
}
/*
- * Install one test tuple into EPQ state, or clear test tuple if tuple == NULL
- *
- * NB: passed tuple must be palloc'd; it may get freed later
+ * Return, and create if necessary, a slot for an EPQ test tuple.
*/
-void
-EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple)
+TupleTableSlot *
+EvalPlanQualSlot(EPQState *epqstate,
+ Relation relation, Index rti)
{
- EState *estate = epqstate->estate;
+ TupleTableSlot **slot;
- Assert(rti > 0);
+ Assert(rti > 0 && rti <= epqstate->estate->es_range_table_size);
+ slot = &epqstate->estate->es_epqTupleSlot[rti - 1];
- /*
- * free old test tuple, if any, and store new tuple where relation's scan
- * node will see it
- */
- if (estate->es_epqTuple[rti - 1] != NULL)
- heap_freetuple(estate->es_epqTuple[rti - 1]);
- estate->es_epqTuple[rti - 1] = tuple;
- estate->es_epqTupleSet[rti - 1] = true;
-}
+ if (*slot == NULL)
+ {
+ MemoryContext oldcontext;
-/*
- * Fetch back the current test tuple (if any) for the specified RTI
- */
-HeapTuple
-EvalPlanQualGetTuple(EPQState *epqstate, Index rti)
-{
- EState *estate = epqstate->estate;
+ oldcontext = MemoryContextSwitchTo(epqstate->estate->es_query_cxt);
- Assert(rti > 0);
+ if (relation)
+ *slot = ExecAllocTableSlot(&epqstate->estate->es_tupleTable,
+ RelationGetDescr(relation),
+ &TTSOpsBufferHeapTuple);
+ else
+ *slot = ExecAllocTableSlot(&epqstate->estate->es_tupleTable,
+ epqstate->origslot->tts_tupleDescriptor,
+ &TTSOpsVirtual);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
- return estate->es_epqTuple[rti - 1];
+ return *slot;
}
/*
@@ -2844,13 +2834,14 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
ExecRowMark *erm = aerm->rowmark;
Datum datum;
bool isNull;
- HeapTupleData tuple;
+ TupleTableSlot *slot;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
/* clear any leftover test tuple for this rel */
- EvalPlanQualSetTuple(epqstate, erm->rti, NULL);
+ slot = EvalPlanQualSlot(epqstate, erm->relation, erm->rti);
+ ExecClearTuple(slot);
/* if child rel, must check whether it produced this row */
if (erm->rti != erm->prti)
@@ -2875,8 +2866,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
@@ -2900,11 +2889,13 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
- erm,
- datum,
- &updated);
- if (copyTuple == NULL)
+
+ fdwroutine->RefetchForeignRow(epqstate->estate,
+ erm,
+ datum,
+ slot,
+ &updated);
+ if (TupIsNull(slot))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
/*
@@ -2916,6 +2907,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
else
{
/* ordinary table, fetch the tuple */
+ HeapTupleData tuple;
Buffer buffer;
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
@@ -2923,18 +2915,13 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
- /* successful, copy tuple */
- copyTuple = heap_copytuple(&tuple);
- ReleaseBuffer(buffer);
+ /* successful, store tuple */
+ ExecStorePinnedBufferHeapTuple(&tuple, slot, buffer);
+ ExecMaterializeSlot(slot);
}
-
- /* store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple);
}
else
{
- HeapTupleHeader td;
-
Assert(erm->markType == ROW_MARK_COPY);
/* fetch the whole-row Var for the relation */
@@ -2944,19 +2931,8 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* non-locked rels could be on the inside of outer joins */
if (isNull)
continue;
- td = DatumGetHeapTupleHeader(datum);
-
- /* build a temporary HeapTuple control structure */
- tuple.t_len = HeapTupleHeaderGetDatumLength(td);
- tuple.t_data = td;
- /* relation might be a foreign table, if so provide tableoid */
- tuple.t_tableOid = erm->relid;
- /* also copy t_ctid in case there's valid data there */
- tuple.t_self = td->t_ctid;
-
- /* copy and store tuple */
- EvalPlanQualSetTuple(epqstate, erm->rti,
- heap_copytuple(&tuple));
+
+ ExecStoreHeapTupleDatum(datum, slot);
}
}
}
@@ -3152,17 +3128,14 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
* sub-rechecks to inherit the values being examined by an outer recheck.
*/
estate->es_epqScanDone = (bool *) palloc0(rtsize * sizeof(bool));
- if (parentestate->es_epqTuple != NULL)
+ if (parentestate->es_epqTupleSlot != NULL)
{
- estate->es_epqTuple = parentestate->es_epqTuple;
- estate->es_epqTupleSet = parentestate->es_epqTupleSet;
+ estate->es_epqTupleSlot = parentestate->es_epqTupleSlot;
}
else
{
- estate->es_epqTuple = (HeapTuple *)
- palloc0(rtsize * sizeof(HeapTuple));
- estate->es_epqTupleSet = (bool *)
- palloc0(rtsize * sizeof(bool));
+ estate->es_epqTupleSlot = (TupleTableSlot **)
+ palloc0(rtsize * sizeof(TupleTableSlot *));
}
/*