LCOV - code coverage report
Current view: top level - src/backend/access/transam - transam.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 67 83 80.7 %
Date: 2024-12-02 20:15:07 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * transam.c
       4             :  *    postgres transaction (commit) log interface routines
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/transam/transam.c
      12             :  *
      13             :  * NOTES
      14             :  *    This file contains the high level access-method interface to the
      15             :  *    transaction system.
      16             :  *
      17             :  *-------------------------------------------------------------------------
      18             :  */
      19             : 
      20             : #include "postgres.h"
      21             : 
      22             : #include "access/clog.h"
      23             : #include "access/subtrans.h"
      24             : #include "access/transam.h"
      25             : #include "utils/snapmgr.h"
      26             : 
      27             : /*
      28             :  * Single-item cache for results of TransactionLogFetch.  It's worth having
      29             :  * such a cache because we frequently find ourselves repeatedly checking the
      30             :  * same XID, for example when scanning a table just after a bulk insert,
      31             :  * update, or delete.
      32             :  */
      33             : static TransactionId cachedFetchXid = InvalidTransactionId;
      34             : static XidStatus cachedFetchXidStatus;
      35             : static XLogRecPtr cachedCommitLSN;
      36             : 
      37             : /* Local functions */
      38             : static XidStatus TransactionLogFetch(TransactionId transactionId);
      39             : 
      40             : 
      41             : /* ----------------------------------------------------------------
      42             :  *      Postgres log access method interface
      43             :  *
      44             :  *      TransactionLogFetch
      45             :  * ----------------------------------------------------------------
      46             :  */
      47             : 
      48             : /*
      49             :  * TransactionLogFetch --- fetch commit status of specified transaction id
      50             :  */
      51             : static XidStatus
      52    20343812 : TransactionLogFetch(TransactionId transactionId)
      53             : {
      54             :     XidStatus   xidstatus;
      55             :     XLogRecPtr  xidlsn;
      56             : 
      57             :     /*
      58             :      * Before going to the commit log manager, check our single item cache to
      59             :      * see if we didn't just check the transaction status a moment ago.
      60             :      */
      61    20343812 :     if (TransactionIdEquals(transactionId, cachedFetchXid))
      62    17831414 :         return cachedFetchXidStatus;
      63             : 
      64             :     /*
      65             :      * Also, check to see if the transaction ID is a permanent one.
      66             :      */
      67     2512398 :     if (!TransactionIdIsNormal(transactionId))
      68             :     {
      69     1140332 :         if (TransactionIdEquals(transactionId, BootstrapTransactionId))
      70     1140312 :             return TRANSACTION_STATUS_COMMITTED;
      71          20 :         if (TransactionIdEquals(transactionId, FrozenTransactionId))
      72          12 :             return TRANSACTION_STATUS_COMMITTED;
      73           8 :         return TRANSACTION_STATUS_ABORTED;
      74             :     }
      75             : 
      76             :     /*
      77             :      * Get the transaction status.
      78             :      */
      79     1372066 :     xidstatus = TransactionIdGetStatus(transactionId, &xidlsn);
      80             : 
      81             :     /*
      82             :      * Cache it, but DO NOT cache status for unfinished or sub-committed
      83             :      * transactions!  We only cache status that is guaranteed not to change.
      84             :      */
      85     1372066 :     if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
      86             :         xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
      87             :     {
      88     1262260 :         cachedFetchXid = transactionId;
      89     1262260 :         cachedFetchXidStatus = xidstatus;
      90     1262260 :         cachedCommitLSN = xidlsn;
      91             :     }
      92             : 
      93     1372066 :     return xidstatus;
      94             : }
      95             : 
      96             : /* ----------------------------------------------------------------
      97             :  *                      Interface functions
      98             :  *
      99             :  *      TransactionIdDidCommit
     100             :  *      TransactionIdDidAbort
     101             :  *      ========
     102             :  *         these functions test the transaction status of
     103             :  *         a specified transaction id.
     104             :  *
     105             :  *      TransactionIdCommitTree
     106             :  *      TransactionIdAsyncCommitTree
     107             :  *      TransactionIdAbortTree
     108             :  *      ========
     109             :  *         these functions set the transaction status of the specified
     110             :  *         transaction tree.
     111             :  *
     112             :  * See also TransactionIdIsInProgress, which once was in this module
     113             :  * but now lives in procarray.c, as well as comments at the top of
     114             :  * heapam_visibility.c that explain how everything fits together.
     115             :  * ----------------------------------------------------------------
     116             :  */
     117             : 
     118             : /*
     119             :  * TransactionIdDidCommit
     120             :  *      True iff transaction associated with the identifier did commit.
     121             :  *
     122             :  * Note:
     123             :  *      Assumes transaction identifier is valid and exists in clog.
     124             :  */
     125             : bool                            /* true if given transaction committed */
     126    20294370 : TransactionIdDidCommit(TransactionId transactionId)
     127             : {
     128             :     XidStatus   xidstatus;
     129             : 
     130    20294370 :     xidstatus = TransactionLogFetch(transactionId);
     131             : 
     132             :     /*
     133             :      * If it's marked committed, it's committed.
     134             :      */
     135    20294370 :     if (xidstatus == TRANSACTION_STATUS_COMMITTED)
     136    20041928 :         return true;
     137             : 
     138             :     /*
     139             :      * If it's marked subcommitted, we have to check the parent recursively.
     140             :      * However, if it's older than TransactionXmin, we can't look at
     141             :      * pg_subtrans; instead assume that the parent crashed without cleaning up
     142             :      * its children.
     143             :      *
     144             :      * Originally we Assert'ed that the result of SubTransGetParent was not
     145             :      * zero. However with the introduction of prepared transactions, there can
     146             :      * be a window just after database startup where we do not have complete
     147             :      * knowledge in pg_subtrans of the transactions after TransactionXmin.
     148             :      * StartupSUBTRANS() has ensured that any missing information will be
     149             :      * zeroed.  Since this case should not happen under normal conditions, it
     150             :      * seems reasonable to emit a WARNING for it.
     151             :      */
     152      252442 :     if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
     153             :     {
     154             :         TransactionId parentXid;
     155             : 
     156           0 :         if (TransactionIdPrecedes(transactionId, TransactionXmin))
     157           0 :             return false;
     158           0 :         parentXid = SubTransGetParent(transactionId);
     159           0 :         if (!TransactionIdIsValid(parentXid))
     160             :         {
     161           0 :             elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
     162             :                  transactionId);
     163           0 :             return false;
     164             :         }
     165           0 :         return TransactionIdDidCommit(parentXid);
     166             :     }
     167             : 
     168             :     /*
     169             :      * It's not committed.
     170             :      */
     171      252442 :     return false;
     172             : }
     173             : 
     174             : /*
     175             :  * TransactionIdDidAbort
     176             :  *      True iff transaction associated with the identifier did abort.
     177             :  *
     178             :  * Note:
     179             :  *      Assumes transaction identifier is valid and exists in clog.
     180             :  *
     181             :  *      Returns true only for explicitly aborted transactions, as transactions
     182             :  *      implicitly aborted due to a crash will commonly still appear to be
     183             :  *      in-progress in the clog.  Most of the time TransactionIdDidCommit(),
     184             :  *      with a preceding TransactionIdIsInProgress() check, should be used
     185             :  *      instead of TransactionIdDidAbort().
     186             :  */
     187             : bool                            /* true if given transaction aborted */
     188       49442 : TransactionIdDidAbort(TransactionId transactionId)
     189             : {
     190             :     XidStatus   xidstatus;
     191             : 
     192       49442 :     xidstatus = TransactionLogFetch(transactionId);
     193             : 
     194             :     /*
     195             :      * If it's marked aborted, it's aborted.
     196             :      */
     197       49442 :     if (xidstatus == TRANSACTION_STATUS_ABORTED)
     198          36 :         return true;
     199             : 
     200             :     /*
     201             :      * If it's marked subcommitted, we have to check the parent recursively.
     202             :      * However, if it's older than TransactionXmin, we can't look at
     203             :      * pg_subtrans; instead assume that the parent crashed without cleaning up
     204             :      * its children.
     205             :      */
     206       49406 :     if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
     207             :     {
     208             :         TransactionId parentXid;
     209             : 
     210           0 :         if (TransactionIdPrecedes(transactionId, TransactionXmin))
     211           0 :             return true;
     212           0 :         parentXid = SubTransGetParent(transactionId);
     213           0 :         if (!TransactionIdIsValid(parentXid))
     214             :         {
     215             :             /* see notes in TransactionIdDidCommit */
     216           0 :             elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
     217             :                  transactionId);
     218           0 :             return true;
     219             :         }
     220           0 :         return TransactionIdDidAbort(parentXid);
     221             :     }
     222             : 
     223             :     /*
     224             :      * It's not aborted.
     225             :      */
     226       49406 :     return false;
     227             : }
     228             : 
     229             : /*
     230             :  * TransactionIdCommitTree
     231             :  *      Marks the given transaction and children as committed
     232             :  *
     233             :  * "xid" is a toplevel transaction commit, and the xids array contains its
     234             :  * committed subtransactions.
     235             :  *
     236             :  * This commit operation is not guaranteed to be atomic, but if not, subxids
     237             :  * are correctly marked subcommit first.
     238             :  */
     239             : void
     240      226330 : TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
     241             : {
     242      226330 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     243             :                                TRANSACTION_STATUS_COMMITTED,
     244             :                                InvalidXLogRecPtr);
     245      226330 : }
     246             : 
     247             : /*
     248             :  * TransactionIdAsyncCommitTree
     249             :  *      Same as above, but for async commits.  The commit record LSN is needed.
     250             :  */
     251             : void
     252       47804 : TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
     253             :                              XLogRecPtr lsn)
     254             : {
     255       47804 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     256             :                                TRANSACTION_STATUS_COMMITTED, lsn);
     257       47804 : }
     258             : 
     259             : /*
     260             :  * TransactionIdAbortTree
     261             :  *      Marks the given transaction and children as aborted.
     262             :  *
     263             :  * "xid" is a toplevel transaction commit, and the xids array contains its
     264             :  * committed subtransactions.
     265             :  *
     266             :  * We don't need to worry about the non-atomic behavior, since any onlookers
     267             :  * will consider all the xacts as not-yet-committed anyway.
     268             :  */
     269             : void
     270       14462 : TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
     271             : {
     272       14462 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     273             :                                TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
     274       14462 : }
     275             : 
     276             : /*
     277             :  * TransactionIdPrecedes --- is id1 logically < id2?
     278             :  */
     279             : bool
     280   236662808 : TransactionIdPrecedes(TransactionId id1, TransactionId id2)
     281             : {
     282             :     /*
     283             :      * If either ID is a permanent XID then we can just do unsigned
     284             :      * comparison.  If both are normal, do a modulo-2^32 comparison.
     285             :      */
     286             :     int32       diff;
     287             : 
     288   236662808 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     289    71397408 :         return (id1 < id2);
     290             : 
     291   165265400 :     diff = (int32) (id1 - id2);
     292   165265400 :     return (diff < 0);
     293             : }
     294             : 
     295             : /*
     296             :  * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
     297             :  */
     298             : bool
     299      105108 : TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
     300             : {
     301             :     int32       diff;
     302             : 
     303      105108 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     304        2018 :         return (id1 <= id2);
     305             : 
     306      103090 :     diff = (int32) (id1 - id2);
     307      103090 :     return (diff <= 0);
     308             : }
     309             : 
     310             : /*
     311             :  * TransactionIdFollows --- is id1 logically > id2?
     312             :  */
     313             : bool
     314    32170736 : TransactionIdFollows(TransactionId id1, TransactionId id2)
     315             : {
     316             :     int32       diff;
     317             : 
     318    32170736 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     319     9368810 :         return (id1 > id2);
     320             : 
     321    22801926 :     diff = (int32) (id1 - id2);
     322    22801926 :     return (diff > 0);
     323             : }
     324             : 
     325             : /*
     326             :  * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
     327             :  */
     328             : bool
     329   133192528 : TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
     330             : {
     331             :     int32       diff;
     332             : 
     333   133192528 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     334       88518 :         return (id1 >= id2);
     335             : 
     336   133104010 :     diff = (int32) (id1 - id2);
     337   133104010 :     return (diff >= 0);
     338             : }
     339             : 
     340             : 
     341             : /*
     342             :  * TransactionIdLatest --- get latest XID among a main xact and its children
     343             :  */
     344             : TransactionId
     345      349698 : TransactionIdLatest(TransactionId mainxid,
     346             :                     int nxids, const TransactionId *xids)
     347             : {
     348             :     TransactionId result;
     349             : 
     350             :     /*
     351             :      * In practice it is highly likely that the xids[] array is sorted, and so
     352             :      * we could save some cycles by just taking the last child XID, but this
     353             :      * probably isn't so performance-critical that it's worth depending on
     354             :      * that assumption.  But just to show we're not totally stupid, scan the
     355             :      * array back-to-front to avoid useless assignments.
     356             :      */
     357      349698 :     result = mainxid;
     358      362480 :     while (--nxids >= 0)
     359             :     {
     360       12782 :         if (TransactionIdPrecedes(result, xids[nxids]))
     361        1426 :             result = xids[nxids];
     362             :     }
     363      349698 :     return result;
     364             : }
     365             : 
     366             : 
     367             : /*
     368             :  * TransactionIdGetCommitLSN
     369             :  *
     370             :  * This function returns an LSN that is late enough to be able
     371             :  * to guarantee that if we flush up to the LSN returned then we
     372             :  * will have flushed the transaction's commit record to disk.
     373             :  *
     374             :  * The result is not necessarily the exact LSN of the transaction's
     375             :  * commit record!  For example, for long-past transactions (those whose
     376             :  * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
     377             :  * Also, because we group transactions on the same clog page to conserve
     378             :  * storage, we might return the LSN of a later transaction that falls into
     379             :  * the same group.
     380             :  */
     381             : XLogRecPtr
     382    18460576 : TransactionIdGetCommitLSN(TransactionId xid)
     383             : {
     384             :     XLogRecPtr  result;
     385             : 
     386             :     /*
     387             :      * Currently, all uses of this function are for xids that were just
     388             :      * reported to be committed by TransactionLogFetch, so we expect that
     389             :      * checking TransactionLogFetch's cache will usually succeed and avoid an
     390             :      * extra trip to shared memory.
     391             :      */
     392    18460576 :     if (TransactionIdEquals(xid, cachedFetchXid))
     393    17320276 :         return cachedCommitLSN;
     394             : 
     395             :     /* Special XIDs are always known committed */
     396     1140300 :     if (!TransactionIdIsNormal(xid))
     397     1140300 :         return InvalidXLogRecPtr;
     398             : 
     399             :     /*
     400             :      * Get the transaction status.
     401             :      */
     402           0 :     (void) TransactionIdGetStatus(xid, &result);
     403             : 
     404           0 :     return result;
     405             : }

Generated by: LCOV version 1.14