LCOV - code coverage report
Current view: top level - src/backend/access/transam - transam.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 74.6 % 63 47
Test Date: 2026-03-01 12:15:18 Functions: 100.0 % 8 8
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-2026, 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     10939825 : 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     10939825 :     if (TransactionIdEquals(transactionId, cachedFetchXid))
      62      9478302 :         return cachedFetchXidStatus;
      63              : 
      64              :     /*
      65              :      * Also, check to see if the transaction ID is a permanent one.
      66              :      */
      67      1461523 :     if (!TransactionIdIsNormal(transactionId))
      68              :     {
      69       657151 :         if (TransactionIdEquals(transactionId, BootstrapTransactionId))
      70       657141 :             return TRANSACTION_STATUS_COMMITTED;
      71           10 :         if (TransactionIdEquals(transactionId, FrozenTransactionId))
      72            6 :             return TRANSACTION_STATUS_COMMITTED;
      73            4 :         return TRANSACTION_STATUS_ABORTED;
      74              :     }
      75              : 
      76              :     /*
      77              :      * Get the transaction status.
      78              :      */
      79       804372 :     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       804372 :     if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
      86              :         xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
      87              :     {
      88       739252 :         cachedFetchXid = transactionId;
      89       739252 :         cachedFetchXidStatus = xidstatus;
      90       739252 :         cachedCommitLSN = xidlsn;
      91              :     }
      92              : 
      93       804372 :     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     10907369 : TransactionIdDidCommit(TransactionId transactionId)
     127              : {
     128              :     XidStatus   xidstatus;
     129              : 
     130     10907369 :     xidstatus = TransactionLogFetch(transactionId);
     131              : 
     132              :     /*
     133              :      * If it's marked committed, it's committed.
     134              :      */
     135     10907369 :     if (xidstatus == TRANSACTION_STATUS_COMMITTED)
     136     10773424 :         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       133945 :     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       133945 :     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        32456 : TransactionIdDidAbort(TransactionId transactionId)
     189              : {
     190              :     XidStatus   xidstatus;
     191              : 
     192        32456 :     xidstatus = TransactionLogFetch(transactionId);
     193              : 
     194              :     /*
     195              :      * If it's marked aborted, it's aborted.
     196              :      */
     197        32456 :     if (xidstatus == TRANSACTION_STATUS_ABORTED)
     198          232 :         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        32224 :     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        32224 :     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       126073 : TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
     241              : {
     242       126073 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     243              :                                TRANSACTION_STATUS_COMMITTED,
     244              :                                InvalidXLogRecPtr);
     245       126073 : }
     246              : 
     247              : /*
     248              :  * TransactionIdAsyncCommitTree
     249              :  *      Same as above, but for async commits.  The commit record LSN is needed.
     250              :  */
     251              : void
     252        26826 : TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
     253              :                              XLogRecPtr lsn)
     254              : {
     255        26826 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     256              :                                TRANSACTION_STATUS_COMMITTED, lsn);
     257        26826 : }
     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         8916 : TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
     271              : {
     272         8916 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     273              :                                TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
     274         8916 : }
     275              : 
     276              : 
     277              : /*
     278              :  * TransactionIdLatest --- get latest XID among a main xact and its children
     279              :  */
     280              : TransactionId
     281       197407 : TransactionIdLatest(TransactionId mainxid,
     282              :                     int nxids, const TransactionId *xids)
     283              : {
     284              :     TransactionId result;
     285              : 
     286              :     /*
     287              :      * In practice it is highly likely that the xids[] array is sorted, and so
     288              :      * we could save some cycles by just taking the last child XID, but this
     289              :      * probably isn't so performance-critical that it's worth depending on
     290              :      * that assumption.  But just to show we're not totally stupid, scan the
     291              :      * array back-to-front to avoid useless assignments.
     292              :      */
     293       197407 :     result = mainxid;
     294       203764 :     while (--nxids >= 0)
     295              :     {
     296         6357 :         if (TransactionIdPrecedes(result, xids[nxids]))
     297          679 :             result = xids[nxids];
     298              :     }
     299       197407 :     return result;
     300              : }
     301              : 
     302              : 
     303              : /*
     304              :  * TransactionIdGetCommitLSN
     305              :  *
     306              :  * This function returns an LSN that is late enough to be able
     307              :  * to guarantee that if we flush up to the LSN returned then we
     308              :  * will have flushed the transaction's commit record to disk.
     309              :  *
     310              :  * The result is not necessarily the exact LSN of the transaction's
     311              :  * commit record!  For example, for long-past transactions (those whose
     312              :  * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
     313              :  * Also, because we group transactions on the same clog page to conserve
     314              :  * storage, we might return the LSN of a later transaction that falls into
     315              :  * the same group.
     316              :  */
     317              : XLogRecPtr
     318      9569824 : TransactionIdGetCommitLSN(TransactionId xid)
     319              : {
     320              :     XLogRecPtr  result;
     321              : 
     322              :     /*
     323              :      * Currently, all uses of this function are for xids that were just
     324              :      * reported to be committed by TransactionLogFetch, so we expect that
     325              :      * checking TransactionLogFetch's cache will usually succeed and avoid an
     326              :      * extra trip to shared memory.
     327              :      */
     328      9569824 :     if (TransactionIdEquals(xid, cachedFetchXid))
     329      8912689 :         return cachedCommitLSN;
     330              : 
     331              :     /* Special XIDs are always known committed */
     332       657135 :     if (!TransactionIdIsNormal(xid))
     333       657135 :         return InvalidXLogRecPtr;
     334              : 
     335              :     /*
     336              :      * Get the transaction status.
     337              :      */
     338            0 :     (void) TransactionIdGetStatus(xid, &result);
     339              : 
     340            0 :     return result;
     341              : }
        

Generated by: LCOV version 2.0-1