]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix wrong logic in TransactionIdInRecentPast()
authorAlexander Korotkov <akorotkov@postgresql.org>
Thu, 8 Feb 2024 10:45:26 +0000 (12:45 +0200)
committerAlexander Korotkov <akorotkov@postgresql.org>
Fri, 9 Feb 2024 10:37:21 +0000 (12:37 +0200)
The TransactionIdInRecentPast() should return false for all the transactions
older than TransamVariables->oldestClogXid.  However, the function contains
a bug in comparison FullTransactionId to TransactionID allowing full
transactions between nextXid - 2^32 and oldestClogXid - 2^31.

This commit fixes TransactionIdInRecentPast() by turning the oldestClogXid into
FullTransactionId first, then performing the comparison.

Backpatch to all supported versions.

Reported-by: Egor Chindyaskin
Bug: 18212
Discussion: https://postgr.es/m/18212-547307f8adf57262%40postgresql.org
Author: Karina Litskevich
Reviewed-by: Kyotaro Horiguchi
Backpatch-through: 12

src/backend/utils/adt/xid8funcs.c

index 06ae940df6fe4890646c68e62244c21718e91077..6fbfb3a1cc25807e0d8393788d6e309b3ecbddcb 100644 (file)
@@ -98,11 +98,12 @@ StaticAssertDecl(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
 static bool
 TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
 {
-       uint32          xid_epoch = EpochFromFullTransactionId(fxid);
        TransactionId xid = XidFromFullTransactionId(fxid);
        uint32          now_epoch;
        TransactionId now_epoch_next_xid;
        FullTransactionId now_fullxid;
+       TransactionId oldest_xid;
+       FullTransactionId oldest_fxid;
 
        now_fullxid = ReadNextFullTransactionId();
        now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
@@ -135,17 +136,24 @@ TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
        Assert(LWLockHeldByMe(XactTruncationLock));
 
        /*
-        * If the transaction ID has wrapped around, it's definitely too old to
-        * determine the commit status.  Otherwise, we can compare it to
-        * ShmemVariableCache->oldestClogXid to determine whether the relevant
-        * CLOG entry is guaranteed to still exist.
+        * If fxid is not older than ShmemVariableCache->oldestClogXid, the
+        * relevant CLOG entry is guaranteed to still exist.  Convert
+        * ShmemVariableCache->oldestClogXid into a FullTransactionId to compare
+        * it with fxid.  Determine the right epoch knowing that oldest_fxid
+        * shouldn't be more than 2^31 older than now_fullxid.
         */
-       if (xid_epoch + 1 < now_epoch
-               || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
-               || TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
-               return false;
-
-       return true;
+       oldest_xid = ShmemVariableCache->oldestClogXid;
+       Assert(TransactionIdPrecedesOrEquals(oldest_xid, now_epoch_next_xid));
+       if (oldest_xid <= now_epoch_next_xid)
+       {
+               oldest_fxid = FullTransactionIdFromEpochAndXid(now_epoch, oldest_xid);
+       }
+       else
+       {
+               Assert(now_epoch > 0);
+               oldest_fxid = FullTransactionIdFromEpochAndXid(now_epoch - 1, oldest_xid);
+       }
+       return !FullTransactionIdPrecedes(fxid, oldest_fxid);
 }
 
 /*