LCOV - differential code coverage report
Current view: top level - src/backend/access/transam - transam.c (source / functions) Coverage Total Hit LBC UIC UBC GIC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 80.7 % 83 67 2 9 5 44 23 11 42
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 12 12 11 1 11
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * transam.c
       4                 :  *    postgres transaction (commit) log interface routines
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, 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 CBC    19064836 : 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        19064836 :     if (TransactionIdEquals(transactionId, cachedFetchXid))
      62        13428106 :         return cachedFetchXidStatus;
      63                 : 
      64                 :     /*
      65                 :      * Also, check to see if the transaction ID is a permanent one.
      66                 :      */
      67         5636730 :     if (!TransactionIdIsNormal(transactionId))
      68                 :     {
      69         3836731 :         if (TransactionIdEquals(transactionId, BootstrapTransactionId))
      70         3832941 :             return TRANSACTION_STATUS_COMMITTED;
      71            3790 :         if (TransactionIdEquals(transactionId, FrozenTransactionId))
      72            3786 :             return TRANSACTION_STATUS_COMMITTED;
      73               4 :         return TRANSACTION_STATUS_ABORTED;
      74                 :     }
      75                 : 
      76                 :     /*
      77                 :      * Get the transaction status.
      78                 :      */
      79         1799999 :     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         1799999 :     if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
      86                 :         xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
      87                 :     {
      88         1756052 :         cachedFetchXid = transactionId;
      89         1756052 :         cachedFetchXidStatus = xidstatus;
      90         1756052 :         cachedCommitLSN = xidlsn;
      91                 :     }
      92                 : 
      93         1799999 :     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 GIC    19045219 : TransactionIdDidCommit(TransactionId transactionId)
     127 ECB             : {
     128                 :     XidStatus   xidstatus;
     129                 : 
     130 GIC    19045219 :     xidstatus = TransactionLogFetch(transactionId);
     131 ECB             : 
     132                 :     /*
     133                 :      * If it's marked committed, it's committed.
     134                 :      */
     135 GIC    19045219 :     if (xidstatus == TRANSACTION_STATUS_COMMITTED)
     136 CBC    18948944 :         return true;
     137 ECB             : 
     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 GIC       96275 :     if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
     153 ECB             :     {
     154                 :         TransactionId parentXid;
     155                 : 
     156 UIC           0 :         if (TransactionIdPrecedes(transactionId, TransactionXmin))
     157 UBC           0 :             return false;
     158               0 :         parentXid = SubTransGetParent(transactionId);
     159               0 :         if (!TransactionIdIsValid(parentXid))
     160 EUB             :         {
     161 UIC           0 :             elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
     162 EUB             :                  transactionId);
     163 UIC           0 :             return false;
     164 EUB             :         }
     165 UIC           0 :         return TransactionIdDidCommit(parentXid);
     166 EUB             :     }
     167                 : 
     168                 :     /*
     169                 :      * It's not committed.
     170                 :      */
     171 GIC       96275 :     return false;
     172 ECB             : }
     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 GIC       19617 : TransactionIdDidAbort(TransactionId transactionId)
     189                 : {
     190                 :     XidStatus   xidstatus;
     191                 : 
     192           19617 :     xidstatus = TransactionLogFetch(transactionId);
     193                 : 
     194                 :     /*
     195 ECB             :      * If it's marked aborted, it's aborted.
     196                 :      */
     197 GIC       19617 :     if (xidstatus == TRANSACTION_STATUS_ABORTED)
     198              18 :         return true;
     199 ECB             : 
     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 GIC       19599 :     if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
     207                 :     {
     208                 :         TransactionId parentXid;
     209                 : 
     210 UIC           0 :         if (TransactionIdPrecedes(transactionId, TransactionXmin))
     211               0 :             return true;
     212               0 :         parentXid = SubTransGetParent(transactionId);
     213 LBC           0 :         if (!TransactionIdIsValid(parentXid))
     214                 :         {
     215                 :             /* see notes in TransactionIdDidCommit */
     216 UIC           0 :             elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
     217 EUB             :                  transactionId);
     218 UBC           0 :             return true;
     219 EUB             :         }
     220 UBC           0 :         return TransactionIdDidAbort(parentXid);
     221                 :     }
     222                 : 
     223 EUB             :     /*
     224                 :      * It's not aborted.
     225                 :      */
     226 GIC       19599 :     return false;
     227 EUB             : }
     228                 : 
     229                 : /*
     230                 :  * TransactionIdCommitTree
     231                 :  *      Marks the given transaction and children as committed
     232                 :  *
     233 ECB             :  * "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 GIC      289010 : TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
     241                 : {
     242          289010 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     243                 :                                TRANSACTION_STATUS_COMMITTED,
     244                 :                                InvalidXLogRecPtr);
     245          289010 : }
     246                 : 
     247 ECB             : /*
     248                 :  * TransactionIdAsyncCommitTree
     249                 :  *      Same as above, but for async commits.  The commit record LSN is needed.
     250                 :  */
     251                 : void
     252 CBC       23735 : TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
     253                 :                              XLogRecPtr lsn)
     254                 : {
     255 GIC       23735 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     256                 :                                TRANSACTION_STATUS_COMMITTED, lsn);
     257           23735 : }
     258                 : 
     259 ECB             : /*
     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 GIC        6247 : TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
     271                 : {
     272            6247 :     TransactionIdSetTreeStatus(xid, nxids, xids,
     273                 :                                TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
     274            6247 : }
     275                 : 
     276                 : /*
     277 ECB             :  * TransactionIdPrecedes --- is id1 logically < id2?
     278                 :  */
     279                 : bool
     280 GIC   251095624 : TransactionIdPrecedes(TransactionId id1, TransactionId id2)
     281 ECB             : {
     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 GIC   251095624 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     289       172994981 :         return (id1 < id2);
     290                 : 
     291        78100643 :     diff = (int32) (id1 - id2);
     292        78100643 :     return (diff < 0);
     293                 : }
     294                 : 
     295 ECB             : /*
     296                 :  * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
     297                 :  */
     298                 : bool
     299 CBC     6819928 : TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
     300                 : {
     301                 :     int32       diff;
     302                 : 
     303 GIC     6819928 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     304             797 :         return (id1 <= id2);
     305                 : 
     306 CBC     6819131 :     diff = (int32) (id1 - id2);
     307 GIC     6819131 :     return (diff <= 0);
     308                 : }
     309                 : 
     310 ECB             : /*
     311                 :  * TransactionIdFollows --- is id1 logically > id2?
     312                 :  */
     313                 : bool
     314 CBC    29262100 : TransactionIdFollows(TransactionId id1, TransactionId id2)
     315                 : {
     316                 :     int32       diff;
     317                 : 
     318 GIC    29262100 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     319        11190110 :         return (id1 > id2);
     320                 : 
     321 CBC    18071990 :     diff = (int32) (id1 - id2);
     322 GIC    18071990 :     return (diff > 0);
     323                 : }
     324                 : 
     325 ECB             : /*
     326                 :  * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
     327                 :  */
     328                 : bool
     329 CBC    20064602 : TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
     330                 : {
     331                 :     int32       diff;
     332                 : 
     333 GIC    20064602 :     if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
     334           40303 :         return (id1 >= id2);
     335                 : 
     336 CBC    20024299 :     diff = (int32) (id1 - id2);
     337 GIC    20024299 :     return (diff >= 0);
     338                 : }
     339                 : 
     340 ECB             : 
     341                 : /*
     342                 :  * TransactionIdLatest --- get latest XID among a main xact and its children
     343                 :  */
     344                 : TransactionId
     345 GIC      366668 : 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 ECB             :      * 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 GIC      366668 :     result = mainxid;
     358          372154 :     while (--nxids >= 0)
     359                 :     {
     360            5486 :         if (TransactionIdPrecedes(result, xids[nxids]))
     361             722 :             result = xids[nxids];
     362                 :     }
     363          366668 :     return result;
     364 ECB             : }
     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 GIC    15755108 : 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 ECB             :      * checking TransactionLogFetch's cache will usually succeed and avoid an
     390                 :      * extra trip to shared memory.
     391                 :      */
     392 GIC    15755108 :     if (TransactionIdEquals(xid, cachedFetchXid))
     393        11937728 :         return cachedCommitLSN;
     394                 : 
     395                 :     /* Special XIDs are always known committed */
     396         3817380 :     if (!TransactionIdIsNormal(xid))
     397         3817380 :         return InvalidXLogRecPtr;
     398                 : 
     399 ECB             :     /*
     400                 :      * Get the transaction status.
     401                 :      */
     402 UIC           0 :     (void) TransactionIdGetStatus(xid, &result);
     403 ECB             : 
     404 LBC           0 :     return result;
     405                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a