<para>
<programlisting>
-HeapTuple
+void
RefetchForeignRow(EState *estate,
ExecRowMark *erm,
Datum rowid,
+ TupleTableSlot *slot,
bool *updated);
</programlisting>
- Re-fetch one tuple from the foreign table, after locking it if required.
+ Re-fetch one tuple slot from the foreign table, after locking it if required.
<literal>estate</literal> is global execution state for the query.
<literal>erm</literal> is the <structname>ExecRowMark</structname> struct describing
the target foreign table and the row lock type (if any) to acquire.
<literal>rowid</literal> identifies the tuple to be fetched.
- <literal>updated</literal> is an output parameter.
+ <literal>slot</literal> contains nothing useful upon call, but can be used to
+ hold the returned tuple. <literal>updated</literal> is an output parameter.
</para>
<para>
- This function should return a palloc'ed copy of the fetched tuple,
- or <literal>NULL</literal> if the row lock couldn't be obtained. The row lock
- type to acquire is defined by <literal>erm->markType</literal>, which is the
- value previously returned by <function>GetForeignRowMarkType</function>.
- (<literal>ROW_MARK_REFERENCE</literal> means to just re-fetch the tuple without
- acquiring any lock, and <literal>ROW_MARK_COPY</literal> will never be seen by
- this routine.)
+ This function should store the tuple into the provided, or clear it if if
+ the row lock couldn't be obtained. The row lock type to acquire is
+ defined by <literal>erm->markType</literal>, which is the value
+ previously returned by <function>GetForeignRowMarkType</function>.
+ (<literal>ROW_MARK_REFERENCE</literal> means to just re-fetch the tuple
+ without acquiring any lock, and <literal>ROW_MARK_COPY</literal> will
+ never be seen by this routine.)
</para>
<para>
<para>
Note that by default, failure to acquire a row lock should result in
- raising an error; a <literal>NULL</literal> return is only appropriate if
+ raising an error; returning with an empty slot is only appropriate if
the <literal>SKIP LOCKED</literal> option is specified
by <literal>erm->waitPolicy</literal>.
</para>
estate->es_tupleTable = NIL;
/* mark EvalPlanQual not active */
- estate->es_epqTuple = NULL;
- estate->es_epqTupleSet = NULL;
+ estate->es_epqTupleSlot = NULL;
estate->es_epqScanDone = NULL;
/*
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
* 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;
}
* 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;
priorXmax))
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/* otherwise xmin should not be dirty... */
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))
HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
* now, treat the tuple as deleted and do not process.
*/
ReleaseBuffer(buffer);
- return NULL;
+ return false;
case HeapTupleMayBeUpdated:
/* successfully locked */
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");
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;
}
if (tuple.t_data == NULL)
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
priorXmax))
{
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/*
{
/* deleted, so forget about it */
ReleaseBuffer(buffer);
- return NULL;
+ return false;
}
/* updated, so look at the updated row */
/* loop back to fetch next in chain */
}
- /*
- * Return the copied tuple
- */
- return copyTuple;
+ /* signal success */
+ return true;
}
/*
}
/*
- * 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;
}
/*
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)
if (erm->markType == ROW_MARK_REFERENCE)
{
- HeapTuple copyTuple;
-
Assert(erm->relation != NULL);
/* fetch the tuple's ctid */
(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");
/*
else
{
/* ordinary table, fetch the tuple */
+ HeapTupleData tuple;
Buffer buffer;
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
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 */
/* 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);
}
}
}
* 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 *));
}
/*
CHECK_FOR_INTERRUPTS();
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
{
/*
* We are inside an EvalPlanQual recheck. Return the test tuple if
ExecClearTuple(slot); /* would not be returned by scan */
return slot;
}
- else if (estate->es_epqTupleSet[scanrelid - 1])
+ else if (estate->es_epqTupleSlot[scanrelid - 1] != NULL)
{
TupleTableSlot *slot = node->ss_ScanTupleSlot;
/* Else mark to remember that we shouldn't return more */
estate->es_epqScanDone[scanrelid - 1] = true;
- /* Return empty slot if we haven't got a test tuple */
- if (estate->es_epqTuple[scanrelid - 1] == NULL)
- return ExecClearTuple(slot);
+ slot = estate->es_epqTupleSlot[scanrelid - 1];
- /* Store test tuple in the plan node's scan slot */
- ExecForceStoreHeapTuple(estate->es_epqTuple[scanrelid - 1],
- slot);
+ /* Return empty slot if we haven't got a test tuple */
+ if (TupIsNull(slot))
+ return NULL;
/* Check if it meets the access-method conditions */
if (!(*recheckMtd) (node, slot))
- ExecClearTuple(slot); /* would not be returned by scan */
+ return ExecClearTuple(slot); /* would not be returned by scan */
return slot;
}
return ExecStoreVirtualTuple(slot);
}
+/*
+ * Store a HeapTuple in datum form, into a slot. That always requires
+ * deforming it and storing it in virtual form.
+ *
+ * Until the slot is materialized, the contents of the slot depend on the
+ * datum.
+ */
+void
+ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot)
+{
+ HeapTupleData tuple = {0};
+ HeapTupleHeader td;
+
+ td = DatumGetHeapTupleHeader(data);
+
+ tuple.t_len = HeapTupleHeaderGetDatumLength(td);
+ tuple.t_self = td->t_ctid;
+ tuple.t_data = td;
+
+ ExecClearTuple(slot);
+
+ heap_deform_tuple(&tuple, slot->tts_tupleDescriptor,
+ slot->tts_values, slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+}
+
/*
* ExecFetchSlotHeapTuple - fetch HeapTuple representing the slot's content
*
estate->es_per_tuple_exprcontext = NULL;
- estate->es_epqTuple = NULL;
- estate->es_epqTupleSet = NULL;
+ estate->es_epqTupleSlot = NULL;
estate->es_epqScanDone = NULL;
estate->es_sourceText = NULL;
{
EState *estate = node->ss.ps.state;
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
{
/*
* We are inside an EvalPlanQual recheck. If a test tuple exists for
Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
Assert(scanrelid > 0);
- if (estate->es_epqTupleSet[scanrelid - 1])
+ if (estate->es_epqTupleSlot[scanrelid - 1] != NULL)
{
/* Verify the claim above */
if (!estate->es_epqScanDone[scanrelid - 1])
{
EState *estate = node->ss.ps.state;
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
{
/* See comments in ExecIndexOnlyMarkPos */
Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
Assert(scanrelid > 0);
- if (estate->es_epqTupleSet[scanrelid - 1])
+ if (estate->es_epqTupleSlot[scanrelid - 1])
{
/* Verify the claim above */
if (!estate->es_epqScanDone[scanrelid - 1])
{
EState *estate = node->ss.ps.state;
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
{
/*
* We are inside an EvalPlanQual recheck. If a test tuple exists for
Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
Assert(scanrelid > 0);
- if (estate->es_epqTupleSet[scanrelid - 1])
+ if (estate->es_epqTupleSlot[scanrelid - 1] != NULL)
{
/* Verify the claim above */
if (!estate->es_epqScanDone[scanrelid - 1])
{
EState *estate = node->ss.ps.state;
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
{
/* See comments in ExecIndexMarkPos */
Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
Assert(scanrelid > 0);
- if (estate->es_epqTupleSet[scanrelid - 1])
+ if (estate->es_epqTupleSlot[scanrelid - 1] != NULL)
{
/* Verify the claim above */
if (!estate->es_epqScanDone[scanrelid - 1])
/* We don't need EvalPlanQual unless we get updated tuple version(s) */
epq_needed = false;
+ /*
+ * Initialize EPQ machinery. Need to do that early because source tuples
+ * are stored in slots initialized therein.
+ */
+ EvalPlanQualBegin(&node->lr_epqstate, estate);
+
/*
* Attempt to lock the source tuple(s). (Note we only have locking
* rowmarks in lr_arowMarks.)
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
- HeapTuple *testTuple;
Datum datum;
bool isNull;
HeapTupleData tuple;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
HTSU_Result test;
- HeapTuple copyTuple;
+ TupleTableSlot *markSlot;
/* clear any leftover test tuple for this rel */
- testTuple = &(node->lr_curtuples[erm->rti - 1]);
- if (*testTuple != NULL)
- heap_freetuple(*testTuple);
- *testTuple = NULL;
+ markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
+ ExecClearTuple(markSlot);
/* if child rel, must check whether it produced this row */
if (erm->rti != erm->prti)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(erm->relation))));
- copyTuple = fdwroutine->RefetchForeignRow(estate,
- erm,
- datum,
- &updated);
- if (copyTuple == NULL)
+
+ fdwroutine->RefetchForeignRow(estate,
+ erm,
+ datum,
+ markSlot,
+ &updated);
+ if (TupIsNull(markSlot))
{
/* couldn't get the lock, so skip this row */
goto lnext;
}
- /* save locked tuple for possible EvalPlanQual testing below */
- *testTuple = copyTuple;
-
/*
* if FDW says tuple was updated before getting locked, we need to
* perform EPQ testing to see if quals are still satisfied
}
/* updated, so fetch and lock the updated version */
- copyTuple = EvalPlanQualFetch(estate, erm->relation,
- lockmode, erm->waitPolicy,
- &hufd.ctid, hufd.xmax);
-
- if (copyTuple == NULL)
+ if (!EvalPlanQualFetch(estate, erm->relation,
+ lockmode, erm->waitPolicy,
+ &hufd.ctid, hufd.xmax,
+ markSlot))
{
/*
* Tuple was deleted; or it's locked and we're under SKIP
goto lnext;
}
/* remember the actually locked tuple's TID */
- tuple.t_self = copyTuple->t_self;
-
- /* Save locked tuple for EvalPlanQual testing below */
- *testTuple = copyTuple;
+ tuple.t_self = markSlot->tts_tid;
/* Remember we need to do EPQ testing */
epq_needed = true;
*/
if (epq_needed)
{
- /* Initialize EPQ machinery */
- EvalPlanQualBegin(&node->lr_epqstate, estate);
-
/*
- * Transfer any already-fetched tuples into the EPQ state, and fetch a
- * copy of any rows that were successfully locked without any update
- * having occurred. (We do this in a separate pass so as to avoid
- * overhead in the common case where there are no concurrent updates.)
- * Make sure any inactive child rels have NULL test tuples in EPQ.
+ * Fetch a copy of any rows that were successfully locked without any
+ * update having occurred. (We do this in a separate pass so as to
+ * avoid overhead in the common case where there are no concurrent
+ * updates.) Make sure any inactive child rels have NULL test tuples
+ * in EPQ.
*/
foreach(lc, node->lr_arowMarks)
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
+ TupleTableSlot *markSlot;
HeapTupleData tuple;
- Buffer buffer;
+ Buffer buffer;
+
+ markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
/* skip non-active child tables, but clear their test tuples */
if (!erm->ermActive)
{
Assert(erm->rti != erm->prti); /* check it's child table */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, NULL);
+ ExecClearTuple(markSlot);
continue;
}
/* was tuple updated and fetched above? */
- if (node->lr_curtuples[erm->rti - 1] != NULL)
- {
- /* yes, so set it as the EPQ test tuple for this rel */
- EvalPlanQualSetTuple(&node->lr_epqstate,
- erm->rti,
- node->lr_curtuples[erm->rti - 1]);
- /* freeing this tuple is now the responsibility of EPQ */
- node->lr_curtuples[erm->rti - 1] = NULL;
+ if (!TupIsNull(markSlot))
continue;
- }
/* foreign tables should have been fetched above */
Assert(erm->relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE);
if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
false, NULL))
elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
-
- /* successful, copy and store tuple */
- EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
- heap_copytuple(&tuple));
- ReleaseBuffer(buffer);
+ ExecStorePinnedBufferHeapTuple(&tuple, markSlot, buffer);
+ ExecMaterializeSlot(markSlot);
+ /* successful, use tuple in slot */
}
/*
*/
lrstate->ps.ps_ProjInfo = NULL;
- /*
- * Create workspace in which we can remember per-RTE locked tuples
- */
- lrstate->lr_ntables = estate->es_range_table_size;
- lrstate->lr_curtuples = (HeapTuple *)
- palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
-
/*
* Locate the ExecRowMark(s) that this node is responsible for, and
* construct ExecAuxRowMarks for them. (InitPlan should already have
if (rc->isParent)
continue;
- /* safety check on size of lr_curtuples array */
- Assert(rc->rti > 0 && rc->rti <= lrstate->lr_ntables);
-
/* find ExecRowMark and build ExecAuxRowMark */
erm = ExecFindRowMark(estate, rc->rti, false);
aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
* case it is within a CTE subplan. Hence this test must be here, not in
* ExecInitModifyTable.)
*/
- if (estate->es_epqTuple != NULL)
+ if (estate->es_epqTupleSlot != NULL)
elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
/*
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, LockTupleMode lockmode,
ItemPointer tid, TransactionId priorXmax);
-extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
+extern bool EvalPlanQualFetch(EState *estate, Relation relation,
LockTupleMode lockmode, LockWaitPolicy wait_policy,
- ItemPointer tid, TransactionId priorXmax);
+ ItemPointer tid, TransactionId priorXmax,
+ TupleTableSlot *slot);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate,
Plan *subplan, List *auxrowmarks);
-extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
- HeapTuple tuple);
-extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
+extern TupleTableSlot *EvalPlanQualSlot(EPQState *epqstate,
+ Relation relation, Index rti);
#define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot))
extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
bool shouldFree);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
+extern void ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot);
extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
bool *shouldFree);
typedef RowMarkType (*GetForeignRowMarkType_function) (RangeTblEntry *rte,
LockClauseStrength strength);
-typedef HeapTuple (*RefetchForeignRow_function) (EState *estate,
- ExecRowMark *erm,
- Datum rowid,
- bool *updated);
+typedef void (*RefetchForeignRow_function) (EState *estate,
+ ExecRowMark *erm,
+ Datum rowid,
+ TupleTableSlot *slot,
+ bool *updated);
typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
struct ExplainState *es);
/*
* These fields are for re-evaluating plan quals when an updated tuple is
- * substituted in READ COMMITTED mode. es_epqTuple[] contains tuples that
- * scan plan nodes should return instead of whatever they'd normally
- * return, or NULL if nothing to return; es_epqTupleSet[] is true if a
- * particular array entry is valid; and es_epqScanDone[] is state to
- * remember if the tuple has been returned already. Arrays are of size
- * es_range_table_size and are indexed by scan node scanrelid - 1.
+ * substituted in READ COMMITTED mode. es_epqTupleSlot[] contains test
+ * tuples that scan plan nodes should return instead of whatever they'd
+ * normally return, or an empty slot if there is nothing to return; if
+ * es_epqTupleSlot[] is not NULL if a particular array entry is valid; and
+ * es_epqScanDone[] is state to remember if the tuple has been returned
+ * already. Arrays are of size es_range_table_size and are indexed by
+ * scan node scanrelid - 1.
*/
- HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */
- bool *es_epqTupleSet; /* true if EPQ tuple is provided */
+ TupleTableSlot **es_epqTupleSlot; /* array of EPQ substitute tuples */
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
bool es_use_parallel_mode; /* can we use parallel workers? */
PlanState ps; /* its first field is NodeTag */
List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
- HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */
- int lr_ntables; /* length of lr_curtuples[] array */
} LockRowsState;
/* ----------------