From: Andres Freund Date: Tue, 11 Dec 2018 00:13:45 +0000 (-0800) Subject: WIP: Fetch tuples for triggers & EPQ using a proper snapshot. X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=9e743b50e366b12d4c934f8252a004aa9f0b56db;p=users%2Fandresfreund%2Fpostgres.git WIP: Fetch tuples for triggers & EPQ using a proper snapshot. This is required for zheap, where tids don't uniquely identify a tuple, due to in-place updates. Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch: tmp --- diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 587e166c2a..9851695fca 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2472,6 +2472,8 @@ CopyFrom(CopyState cstate) estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; + estate->es_snapshot = GetActiveSnapshot(); + estate->es_output_cid = GetCurrentCommandId(true); ExecInitRangeTable(estate, cstate->range_table); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 6a00a96f59..69de5cce41 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -3374,8 +3374,19 @@ GetTupleForTrigger(EState *estate, } else { - if (!table_fetch_row_version(relation, tid, SnapshotAny, oldslot, NULL)) - elog(ERROR, "couldn't fetch tuple"); + if (!table_fetch_row_version(relation, tid, estate->es_snapshot, oldslot, NULL)) + { + /* + * If the tuple is not visible to the current snapshot, it has to + * be one that we followed via EPQ. In that case, it needs to have + * been modified by an already committed transaction, otherwise + * we'd not get here. So get a new snapshot, and try to fetch it + * using that. + * PBORKED: ZBORKED: Better approach? + */ + if (!table_fetch_row_version(relation, tid, GetLatestSnapshot(), oldslot, NULL)) + elog(PANIC, "couldn't fetch tuple"); + } } return true; @@ -3603,7 +3614,9 @@ typedef struct AfterTriggerEventData *AfterTriggerEvent; typedef struct AfterTriggerEventData { TriggerFlags ate_flags; /* status bits and offset to shared data */ + CommandId ate_cid1; ItemPointerData ate_ctid1; /* inserted, deleted, or old updated tuple */ + CommandId ate_cid2; ItemPointerData ate_ctid2; /* new updated tuple */ } AfterTriggerEventData; @@ -3611,6 +3624,7 @@ typedef struct AfterTriggerEventData typedef struct AfterTriggerEventDataOneCtid { TriggerFlags ate_flags; /* status bits and offset to shared data */ + CommandId ate_cid1; ItemPointerData ate_ctid1; /* inserted, deleted, or old updated tuple */ } AfterTriggerEventDataOneCtid; @@ -4222,33 +4236,47 @@ AfterTriggerExecute(EState *estate, break; default: - - if (ItemPointerIsValid(&(event->ate_ctid1))) { - LocTriggerData.tg_trigslot = ExecTriggerGetOldSlot(estate, rel); - if (!table_fetch_row_version(rel, &(event->ate_ctid1), SnapshotAny, LocTriggerData.tg_trigslot, NULL)) - elog(ERROR, "failed to fetch tuple1 for AFTER trigger"); - LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, NULL); + Assert(ActiveSnapshotSet()); + if (ItemPointerIsValid(&(event->ate_ctid1))) + { + Snapshot snap = GetActiveSnapshot(); + CommandId saved_cid = snap->curcid; - } - else - { - LocTriggerData.tg_trigtuple = NULL; - } + snap->curcid = event->ate_cid1; - /* don't touch ctid2 if not there */ - if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) == - AFTER_TRIGGER_2CTID && - ItemPointerIsValid(&(event->ate_ctid2))) - { - LocTriggerData.tg_newslot = ExecTriggerGetNewSlot(estate, rel); - if (!table_fetch_row_version(rel, &(event->ate_ctid2), SnapshotAny, LocTriggerData.tg_newslot, NULL)) - elog(ERROR, "failed to fetch tuple2 for AFTER trigger"); - LocTriggerData.tg_newtuple = ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, NULL); - } - else - { - LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigslot = ExecTriggerGetOldSlot(estate, rel); + if (!table_fetch_row_version(rel, &(event->ate_ctid1), snap, LocTriggerData.tg_trigslot, NULL)) + elog(ERROR, "failed to fetch tuple1 for AFTER trigger"); + LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false, NULL); + snap->curcid = saved_cid; + } + else + { + LocTriggerData.tg_trigtuple = NULL; + } + + /* don't touch ctid2 if not there */ + if ((event->ate_flags & AFTER_TRIGGER_TUP_BITS) == + AFTER_TRIGGER_2CTID && + ItemPointerIsValid(&(event->ate_ctid2))) + { + Snapshot snap = GetActiveSnapshot(); + CommandId saved_cid = snap->curcid; + + snap->curcid = event->ate_cid2; + + LocTriggerData.tg_newslot = ExecTriggerGetNewSlot(estate, rel); + if (!table_fetch_row_version(rel, &(event->ate_ctid2), snap, LocTriggerData.tg_newslot, NULL)) + elog(ERROR, "failed to fetch tuple2 for AFTER trigger"); + LocTriggerData.tg_newtuple = ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false, NULL); + + snap->curcid = saved_cid; + } + else + { + LocTriggerData.tg_newtuple = NULL; + } } } @@ -5797,14 +5825,18 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, Assert(oldslot == NULL); Assert(newslot != NULL); ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1)); + new_event.ate_cid1 = estate->es_output_cid + 1; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; } else { Assert(oldslot == NULL); Assert(newslot == NULL); ItemPointerSetInvalid(&(new_event.ate_ctid1)); + new_event.ate_cid1 = InvalidCommandId; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; cancel_prior_stmt_triggers(RelationGetRelid(rel), CMD_INSERT, event); } @@ -5816,14 +5848,18 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, Assert(oldslot != NULL); Assert(newslot == NULL); ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1)); + new_event.ate_cid1 = estate->es_snapshot->curcid; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; } else { Assert(oldslot == NULL); Assert(newslot == NULL); ItemPointerSetInvalid(&(new_event.ate_ctid1)); + new_event.ate_cid1 = InvalidCommandId; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; cancel_prior_stmt_triggers(RelationGetRelid(rel), CMD_DELETE, event); } @@ -5835,14 +5871,18 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, Assert(oldslot != NULL); Assert(newslot != NULL); ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1)); + new_event.ate_cid1 = estate->es_snapshot->curcid; ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2)); + new_event.ate_cid2 = estate->es_output_cid + 1; } else { Assert(oldslot == NULL); Assert(newslot == NULL); ItemPointerSetInvalid(&(new_event.ate_ctid1)); + new_event.ate_cid1 = InvalidCommandId; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; cancel_prior_stmt_triggers(RelationGetRelid(rel), CMD_UPDATE, event); } @@ -5852,7 +5892,9 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, Assert(oldslot == NULL); Assert(newslot == NULL); ItemPointerSetInvalid(&(new_event.ate_ctid1)); + new_event.ate_cid1 = InvalidCommandId; ItemPointerSetInvalid(&(new_event.ate_ctid2)); + new_event.ate_cid2 = InvalidCommandId; break; default: elog(ERROR, "invalid after-trigger event code: %d", event); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 9fe420bfa8..c0cb0c1f38 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2672,7 +2672,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) /* ordinary table, fetch the tuple */ if (!table_fetch_row_version(erm->relation, (ItemPointer) DatumGetPointer(datum), - SnapshotAny, slot, NULL)) + epqstate->estate->es_snapshot, slot, NULL)) elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); } }