Fix assertions with RI triggers in heap_update and heap_delete.
authorHeikki Linnakangas <[email protected]>
Tue, 28 Nov 2023 09:59:09 +0000 (11:59 +0200)
committerHeikki Linnakangas <[email protected]>
Tue, 28 Nov 2023 09:59:51 +0000 (11:59 +0200)
If the tuple being updated is not visible to the crosscheck snapshot,
we return TM_Updated but the assertions would not hold in that case.
Move them to before the cross-check.

Fixes bug #17893. Backpatch to all supported versions.

Author: Alexander Lakhin
Backpatch-through: 12
Discussion: https://www.postgresql.org/message-id/17893-35847009eec517b5%40postgresql.org

src/backend/access/heap/heapam.c
src/include/access/tableam.h

index fd30ce82ae1501620d12c24b5de3c99e1e3c80c5..e92e257026816c387cd6bb5f7f58abc1a4f6749b 100644 (file)
@@ -2918,13 +2918,7 @@ l1:
            result = TM_Deleted;
    }
 
-   if (crosscheck != InvalidSnapshot && result == TM_Ok)
-   {
-       /* Perform additional check for transaction-snapshot mode RI updates */
-       if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
-           result = TM_Updated;
-   }
-
+   /* sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
    if (result != TM_Ok)
    {
        Assert(result == TM_SelfModified ||
@@ -2934,6 +2928,17 @@ l1:
        Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID));
        Assert(result != TM_Updated ||
               !ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
+   }
+
+   if (crosscheck != InvalidSnapshot && result == TM_Ok)
+   {
+       /* Perform additional check for transaction-snapshot mode RI updates */
+       if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+           result = TM_Updated;
+   }
+
+   if (result != TM_Ok)
+   {
        tmfd->ctid = tp.t_data->t_ctid;
        tmfd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
        if (result == TM_SelfModified)
@@ -3563,16 +3568,7 @@ l2:
            result = TM_Deleted;
    }
 
-   if (crosscheck != InvalidSnapshot && result == TM_Ok)
-   {
-       /* Perform additional check for transaction-snapshot mode RI updates */
-       if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
-       {
-           result = TM_Updated;
-           Assert(!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid));
-       }
-   }
-
+   /* Sanity check the result HeapTupleSatisfiesUpdate() and the logic above */
    if (result != TM_Ok)
    {
        Assert(result == TM_SelfModified ||
@@ -3582,6 +3578,17 @@ l2:
        Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
        Assert(result != TM_Updated ||
               !ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid));
+   }
+
+   if (crosscheck != InvalidSnapshot && result == TM_Ok)
+   {
+       /* Perform additional check for transaction-snapshot mode RI updates */
+       if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+           result = TM_Updated;
+   }
+
+   if (result != TM_Ok)
+   {
        tmfd->ctid = oldtup.t_data->t_ctid;
        tmfd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
        if (result == TM_SelfModified)
index 9f1e4a1ac9693326b923162c9bd7fbba445c57b8..307d6262a9f66edefcd70bb172740aec5a9895d3 100644 (file)
@@ -1453,8 +1453,8 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
  * TM_BeingModified (the last only possible if wait == false).
  *
  * In the failure cases, the routine fills *tmfd with the tuple's t_ctid,
- * t_xmax, and, if possible, and, if possible, t_cmax.  See comments for
- * struct TM_FailureData for additional info.
+ * t_xmax, and, if possible, t_cmax.  See comments for struct
+ * TM_FailureData for additional info.
  */
 static inline TM_Result
 table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid,