/*-------------------------------------------------------------------------
*
* transam . c
* postgres transaction ( commit ) log interface routines
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
*
* IDENTIFICATION
* src / backend / access / transam / transam . c
*
* NOTES
* This file contains the high level access - method interface to the
* transaction system .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "utils/snapmgr.h"
/*
* Single - item cache for results of TransactionLogFetch . It ' s worth having
* such a cache because we frequently find ourselves repeatedly checking the
* same XID , for example when scanning a table just after a bulk insert ,
* update , or delete .
*/
static TransactionId cachedFetchXid = InvalidTransactionId;
static XidStatus cachedFetchXidStatus;
static XLogRecPtr cachedCommitLSN;
/* Local functions */
static XidStatus TransactionLogFetch(TransactionId transactionId);
/* ----------------------------------------------------------------
* Postgres log access method interface
*
* TransactionLogFetch
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
* TransactionLogFetch - - - fetch commit status of specified transaction id
*/
static XidStatus
TransactionLogFetch(TransactionId transactionId)
{
XidStatus xidstatus;
XLogRecPtr xidlsn;
/*
* Before going to the commit log manager , check our single item cache to
* see if we didn ' t just check the transaction status a moment ago .
*/
if (TransactionIdEquals(transactionId, cachedFetchXid))
return cachedFetchXidStatus;
/*
* Also , check to see if the transaction ID is a permanent one .
*/
if (!TransactionIdIsNormal(transactionId))
{
if (TransactionIdEquals(transactionId, BootstrapTransactionId))
return TRANSACTION_STATUS_COMMITTED;
if (TransactionIdEquals(transactionId, FrozenTransactionId))
return TRANSACTION_STATUS_COMMITTED;
return TRANSACTION_STATUS_ABORTED;
}
/*
* Get the transaction status .
*/
xidstatus = TransactionIdGetStatus(transactionId, &xidlsn);
/*
* Cache it , but DO NOT cache status for unfinished or sub - committed
* transactions ! We only cache status that is guaranteed not to change .
*/
if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
{
cachedFetchXid = transactionId;
cachedFetchXidStatus = xidstatus;
cachedCommitLSN = xidlsn;
}
return xidstatus;
}
/* ----------------------------------------------------------------
* Interface functions
*
* TransactionIdDidCommit
* TransactionIdDidAbort
* = = = = = = = =
* these functions test the transaction status of
* a specified transaction id .
*
* TransactionIdCommitTree
* TransactionIdAsyncCommitTree
* TransactionIdAbortTree
* = = = = = = = =
* these functions set the transaction status of the specified
* transaction tree .
*
* See also TransactionIdIsInProgress , which once was in this module
* but now lives in procarray . c , as well as comments at the top of
* heapam_visibility . c that explain how everything fits together .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
* TransactionIdDidCommit
* True iff transaction associated with the identifier did commit .
*
* Note :
* Assumes transaction identifier is valid and exists in clog .
*/
bool /* true if given transaction committed */
TransactionIdDidCommit(TransactionId transactionId)
{
XidStatus xidstatus;
xidstatus = TransactionLogFetch(transactionId);
/*
* If it ' s marked committed , it ' s committed .
*/
if (xidstatus == TRANSACTION_STATUS_COMMITTED)
return true ;
/*
* If it ' s marked subcommitted , we have to check the parent recursively .
* However , if it ' s older than TransactionXmin , we can ' t look at
* pg_subtrans ; instead assume that the parent crashed without cleaning up
* its children .
*
* Originally we Assert ' ed that the result of SubTransGetParent was not
* zero . However with the introduction of prepared transactions , there can
* be a window just after database startup where we do not have complete
* knowledge in pg_subtrans of the transactions after TransactionXmin .
* StartupSUBTRANS ( ) has ensured that any missing information will be
* zeroed . Since this case should not happen under normal conditions , it
* seems reasonable to emit a WARNING for it .
*/
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
{
TransactionId parentXid;
if (TransactionIdPrecedes(transactionId, TransactionXmin))
return false ;
parentXid = SubTransGetParent(transactionId);
if (!TransactionIdIsValid(parentXid))
{
elog(WARNING, "no pg_subtrans entry for subcommitted XID %u" ,
transactionId);
return false ;
}
return TransactionIdDidCommit(parentXid);
}
/*
* It ' s not committed .
*/
return false ;
}
/*
* TransactionIdDidAbort
* True iff transaction associated with the identifier did abort .
*
* Note :
* Assumes transaction identifier is valid and exists in clog .
*
* Returns true only for explicitly aborted transactions , as transactions
* implicitly aborted due to a crash will commonly still appear to be
* in - progress in the clog . Most of the time TransactionIdDidCommit ( ) ,
* with a preceding TransactionIdIsInProgress ( ) check , should be used
* instead of TransactionIdDidAbort ( ) .
*/
bool /* true if given transaction aborted */
TransactionIdDidAbort(TransactionId transactionId)
{
XidStatus xidstatus;
xidstatus = TransactionLogFetch(transactionId);
/*
* If it ' s marked aborted , it ' s aborted .
*/
if (xidstatus == TRANSACTION_STATUS_ABORTED)
return true ;
/*
* If it ' s marked subcommitted , we have to check the parent recursively .
* However , if it ' s older than TransactionXmin , we can ' t look at
* pg_subtrans ; instead assume that the parent crashed without cleaning up
* its children .
*/
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
{
TransactionId parentXid;
if (TransactionIdPrecedes(transactionId, TransactionXmin))
return true ;
parentXid = SubTransGetParent(transactionId);
if (!TransactionIdIsValid(parentXid))
{
/* see notes in TransactionIdDidCommit */
elog(WARNING, "no pg_subtrans entry for subcommitted XID %u" ,
transactionId);
return true ;
}
return TransactionIdDidAbort(parentXid);
}
/*
* It ' s not aborted .
*/
return false ;
}
/*
* TransactionIdCommitTree
* Marks the given transaction and children as committed
*
* " xid " is a toplevel transaction commit , and the xids array contains its
* committed subtransactions .
*
* This commit operation is not guaranteed to be atomic , but if not , subxids
* are correctly marked subcommit first .
*/
void
TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
{
TransactionIdSetTreeStatus(xid, nxids, xids,
TRANSACTION_STATUS_COMMITTED,
InvalidXLogRecPtr);
}
/*
* TransactionIdAsyncCommitTree
* Same as above , but for async commits . The commit record LSN is needed .
*/
void
TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
XLogRecPtr lsn)
{
TransactionIdSetTreeStatus(xid, nxids, xids,
TRANSACTION_STATUS_COMMITTED, lsn);
}
/*
* TransactionIdAbortTree
* Marks the given transaction and children as aborted .
*
* " xid " is a toplevel transaction commit , and the xids array contains its
* committed subtransactions .
*
* We don ' t need to worry about the non - atomic behavior , since any onlookers
* will consider all the xacts as not - yet - committed anyway .
*/
void
TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
{
TransactionIdSetTreeStatus(xid, nxids, xids,
TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
}
/*
* TransactionIdPrecedes - - - is id1 logically < id2 ?
*/
bool
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
{
/*
* If either ID is a permanent XID then we can just do unsigned
* comparison . If both are normal , do a modulo - 2 ^ 32 comparison .
*/
int32 diff;
if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
return (id1 < id2);
diff = (int32) (id1 - id2);
return (diff < 0 );
}
/*
* TransactionIdPrecedesOrEquals - - - is id1 logically < = id2 ?
*/
bool
TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
{
int32 diff;
if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
return (id1 <= id2);
diff = (int32) (id1 - id2);
return (diff <= 0 );
}
/*
* TransactionIdFollows - - - is id1 logically > id2 ?
*/
bool
TransactionIdFollows(TransactionId id1, TransactionId id2)
{
int32 diff;
if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
return (id1 > id2);
diff = (int32) (id1 - id2);
return (diff > 0 );
}
/*
* TransactionIdFollowsOrEquals - - - is id1 logically > = id2 ?
*/
bool
TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
{
int32 diff;
if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
return (id1 >= id2);
diff = (int32) (id1 - id2);
return (diff >= 0 );
}
/*
* TransactionIdLatest - - - get latest XID among a main xact and its children
*/
TransactionId
TransactionIdLatest(TransactionId mainxid,
int nxids, const TransactionId *xids)
{
TransactionId result;
/*
* In practice it is highly likely that the xids [ ] array is sorted , and so
* we could save some cycles by just taking the last child XID , but this
* probably isn ' t so performance - critical that it ' s worth depending on
* that assumption . But just to show we ' re not totally stupid , scan the
* array back - to - front to avoid useless assignments .
*/
result = mainxid;
while (--nxids >= 0 )
{
if (TransactionIdPrecedes(result, xids[nxids]))
result = xids[nxids];
}
return result;
}
/*
* TransactionIdGetCommitLSN
*
* This function returns an LSN that is late enough to be able
* to guarantee that if we flush up to the LSN returned then we
* will have flushed the transaction ' s commit record to disk .
*
* The result is not necessarily the exact LSN of the transaction ' s
* commit record ! For example , for long - past transactions ( those whose
* clog pages already migrated to disk ) , we ' ll return InvalidXLogRecPtr .
* Also , because we group transactions on the same clog page to conserve
* storage , we might return the LSN of a later transaction that falls into
* the same group .
*/
XLogRecPtr
TransactionIdGetCommitLSN(TransactionId xid)
{
XLogRecPtr result;
/*
* Currently , all uses of this function are for xids that were just
* reported to be committed by TransactionLogFetch , so we expect that
* checking TransactionLogFetch ' s cache will usually succeed and avoid an
* extra trip to shared memory .
*/
if (TransactionIdEquals(xid, cachedFetchXid))
return cachedCommitLSN;
/* Special XIDs are always known committed */
if (!TransactionIdIsNormal(xid))
return InvalidXLogRecPtr;
/*
* Get the transaction status .
*/
(void ) TransactionIdGetStatus(xid, &result);
return result;
}
Messung V0.5 in Prozent C=93 H=96 G=94
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland