Hi Antonin,

On Tue, Nov 10, 2026 at 11:54 AM Antonin Houska <[email protected]> wrote:
> For now I commented the assertion out and added a comment on possible
> solutions.

Leaving it out looks safe to me, and the check can stay. Every other
caller passes GetCurrentTransactionId(), and in all the runs I made
(UPDATE, DELETE, savepoints, TOAST, row lockers, 150 replayed calls)
the replay reached the function with xmax still zero: the copy clears
it and only REPACK writes to the new heap. So the attached assertion
keeps the old check and states what REPACK relies on; it passes
Shihao's spec and the existing REPACK tests. With assertions off, a
REPEATABLE READ snapshot taken while REPACK waited for the lock saw the
same rows as an untouched copy of the table in every one of those runs.

A correction: in September I blamed the transient heap's XID, but that
run had failed before the final lock. Shihao has it right.

Regards,
Manu
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 19be1a3995b..9086b79bc0c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -5578,12 +5578,14 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 
old_infomask,
                                new_infomask2;
 
        /*
-        * TODO Move this statement to callers? REPACK (CONCURRENTLY) uses
-        * add_to_xmax different from the current transaction when replaying 
data
-        * changes. Alternatively, we can add a boolean argument to the function
-        * indicating the special case, but that does not look nice.
-        */
-       //Assert(TransactionIdIsCurrentTransactionId(add_to_xmax));
+        * REPACK (CONCURRENTLY) replays data changes with the XID of the
+        * transaction that made them (TABLE_REUSE_XID), so add_to_xmax need not
+        * be ours.  It only replays them on tuples of the new heap that nobody
+        * else can lock or modify and whose xmax it has not set, so xmax is
+        * still invalid here and our own transaction does not matter below.
+        */
+       Assert(TransactionIdIsCurrentTransactionId(add_to_xmax) ||
+                  !TransactionIdIsValid(xmax));
 
 l5:
        new_infomask = 0;

Reply via email to