WIP: Fetch tuples for triggers & EPQ using a proper snapshot.
authorAndres Freund <andres@anarazel.de>
Tue, 11 Dec 2018 00:13:45 +0000 (16:13 -0800)
committerAndres Freund <andres@anarazel.de>
Tue, 11 Dec 2018 01:36:51 +0000 (17:36 -0800)
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

src/backend/commands/copy.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c

index 587e166c2a2a8176d052d025c0be22030fa346d9..9851695fca7c621ef949c6a039f8cc89e27b5611 100644 (file)
@@ -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);
 
index 6a00a96f59c7869b2c500ccf9c635cfbebf2757f..69de5cce4151b3bc7e98034adeaeacb4f5b83ab5 100644 (file)
@@ -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);
index 9fe420bfa87e01d53cdd7018fbe32a30ebf02820..c0cb0c1f38d6259aea41de1bf3c42db01a56cc95 100644 (file)
@@ -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");
            }
        }