diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execMain.c | 183 | ||||
-rw-r--r-- | src/backend/executor/execScan.c | 16 | ||||
-rw-r--r-- | src/backend/executor/execTuples.c | 26 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 3 | ||||
-rw-r--r-- | src/backend/executor/nodeIndexonlyscan.c | 8 | ||||
-rw-r--r-- | src/backend/executor/nodeIndexscan.c | 8 | ||||
-rw-r--r-- | src/backend/executor/nodeLockRows.c | 92 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 2 |
8 files changed, 156 insertions, 182 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 *)); } /* diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 79dbc38b174..881131aff26 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -40,7 +40,7 @@ ExecScanFetch(ScanState *node, CHECK_FOR_INTERRUPTS(); - if (estate->es_epqTuple != NULL) + if (estate->es_epqTupleSlot != NULL) { /* * We are inside an EvalPlanQual recheck. Return the test tuple if @@ -63,7 +63,7 @@ ExecScanFetch(ScanState *node, 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; @@ -73,17 +73,15 @@ ExecScanFetch(ScanState *node, /* 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; } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index e1948670490..2a12bd56ea9 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1543,6 +1543,32 @@ ExecStoreAllNullTuple(TupleTableSlot *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 * * The returned HeapTuple represents the slot's content as closely as diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 51362693481..8e9214833d8 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -155,8 +155,7 @@ CreateExecutorState(void) 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index b3f61dd1fc6..72c04b528f5 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -426,7 +426,7 @@ ExecIndexOnlyMarkPos(IndexOnlyScanState *node) { 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 @@ -440,7 +440,7 @@ ExecIndexOnlyMarkPos(IndexOnlyScanState *node) 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]) @@ -461,13 +461,13 @@ ExecIndexOnlyRestrPos(IndexOnlyScanState *node) { 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]) diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 8b294378936..337b561c241 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -850,7 +850,7 @@ ExecIndexMarkPos(IndexScanState *node) { 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 @@ -864,7 +864,7 @@ ExecIndexMarkPos(IndexScanState *node) 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]) @@ -885,13 +885,13 @@ ExecIndexRestrPos(IndexScanState *node) { 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]) diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index 5a12b445a69..76f0f9d66e5 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -67,6 +67,12 @@ lnext: 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.) */ @@ -74,7 +80,6 @@ lnext: { ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc); ExecRowMark *erm = aerm->rowmark; - HeapTuple *testTuple; Datum datum; bool isNull; HeapTupleData tuple; @@ -82,13 +87,11 @@ lnext: 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) @@ -135,19 +138,18 @@ lnext: (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 @@ -230,11 +232,10 @@ lnext: } /* 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 @@ -243,10 +244,7 @@ lnext: 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; @@ -272,42 +270,34 @@ lnext: */ 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); @@ -318,11 +308,9 @@ lnext: 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 */ } /* @@ -402,13 +390,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) 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 * built the global list of ExecRowMarks.) @@ -425,9 +406,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) 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); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 267ecc7c7f7..14d25fd2aa8 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1803,7 +1803,7 @@ ExecModifyTable(PlanState *pstate) * 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"); /* |