From 5ab4599c9d99d06ce14b38ac6f7cb25fc8285893 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 20 Oct 2026 20:02:49 -0400 Subject: [PATCH v2 1/2] Set HEAP_XMAX_INVALID for aborted updates of in-progress tuples HeapTupleSatisfiesVacuumHorizon() left the hint unset when the inserting transaction was still open and the updating subtransaction had rolled back. rewriteheap.c treated such a tuple as updated and dropped it when it saw the dead new version, so CLUSTER and VACUUM FULL lost the row. Discussion: https://postgr.es/m/19760-8f9a0eba17bc8c92@postgresql.org --- src/backend/access/heap/heapam_visibility.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c index 361b76e5065..7184adfe221 100644 --- a/src/backend/access/heap/heapam_visibility.c +++ b/src/backend/access/heap/heapam_visibility.c @@ -1178,10 +1178,30 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple))) return HEAPTUPLE_DELETE_IN_PROGRESS; /* deleting subtransaction must have aborted */ + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, + InvalidTransactionId); return HEAPTUPLE_INSERT_IN_PROGRESS; } else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) { + /* + * If the updater of the tuple has already aborted (a rolled-back + * subtransaction of the inserter), remember that, as callers like + * rewriteheap.c rely on HEAP_XMAX_INVALID to tell a live tuple + * from an updated one. + */ + if (!(tuple->t_infomask & HEAP_XMAX_INVALID) && + !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) && + !HeapTupleHeaderIsOnlyLocked(tuple)) + { + TransactionId xmax = HeapTupleHeaderGetUpdateXid(tuple); + + if (!TransactionIdIsInProgress(xmax) && + !TransactionIdDidCommit(xmax)) + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, + InvalidTransactionId); + } + /* * It'd be possible to discern between INSERT/DELETE in progress * here by looking at xmax - but that doesn't seem beneficial for -- 2.37.1 (Apple Git-137.1)