diff options
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r-- | src/backend/executor/execMain.c | 183 |
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 *)); } /* |