1 /*-------------------------------------------------------------------------
4 * postgres transaction (commit) log interface routines
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/transam/transam.c
14 * This file contains the high level access-method interface to the
17 *-------------------------------------------------------------------------
22 #include "access/clog.h"
23 #include "access/subtrans.h"
24 #include "access/transam.h"
25 #include "utils/snapmgr.h"
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,
33 static TransactionId cachedFetchXid
= InvalidTransactionId
;
34 static XidStatus cachedFetchXidStatus
;
35 static XLogRecPtr cachedCommitLSN
;
38 static XidStatus
TransactionLogFetch(TransactionId transactionId
);
41 /* ----------------------------------------------------------------
42 * Postgres log access method interface
45 * ----------------------------------------------------------------
49 * TransactionLogFetch --- fetch commit status of specified transaction id
52 TransactionLogFetch(TransactionId transactionId
)
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.
61 if (TransactionIdEquals(transactionId
, cachedFetchXid
))
62 return cachedFetchXidStatus
;
65 * Also, check to see if the transaction ID is a permanent one.
67 if (!TransactionIdIsNormal(transactionId
))
69 if (TransactionIdEquals(transactionId
, BootstrapTransactionId
))
70 return TRANSACTION_STATUS_COMMITTED
;
71 if (TransactionIdEquals(transactionId
, FrozenTransactionId
))
72 return TRANSACTION_STATUS_COMMITTED
;
73 return TRANSACTION_STATUS_ABORTED
;
77 * Get the transaction status.
79 xidstatus
= TransactionIdGetStatus(transactionId
, &xidlsn
);
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.
85 if (xidstatus
!= TRANSACTION_STATUS_IN_PROGRESS
&&
86 xidstatus
!= TRANSACTION_STATUS_SUB_COMMITTED
)
88 cachedFetchXid
= transactionId
;
89 cachedFetchXidStatus
= xidstatus
;
90 cachedCommitLSN
= xidlsn
;
96 /* ----------------------------------------------------------------
99 * TransactionIdDidCommit
100 * TransactionIdDidAbort
102 * these functions test the transaction status of
103 * a specified transaction id.
105 * TransactionIdCommitTree
106 * TransactionIdAsyncCommitTree
107 * TransactionIdAbortTree
109 * these functions set the transaction status of the specified
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 * ----------------------------------------------------------------
119 * TransactionIdDidCommit
120 * True iff transaction associated with the identifier did commit.
123 * Assumes transaction identifier is valid and exists in clog.
125 bool /* true if given transaction committed */
126 TransactionIdDidCommit(TransactionId transactionId
)
130 xidstatus
= TransactionLogFetch(transactionId
);
133 * If it's marked committed, it's committed.
135 if (xidstatus
== TRANSACTION_STATUS_COMMITTED
)
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
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.
152 if (xidstatus
== TRANSACTION_STATUS_SUB_COMMITTED
)
154 TransactionId parentXid
;
156 if (TransactionIdPrecedes(transactionId
, TransactionXmin
))
158 parentXid
= SubTransGetParent(transactionId
);
159 if (!TransactionIdIsValid(parentXid
))
161 elog(WARNING
, "no pg_subtrans entry for subcommitted XID %u",
165 return TransactionIdDidCommit(parentXid
);
169 * It's not committed.
175 * TransactionIdDidAbort
176 * True iff transaction associated with the identifier did abort.
179 * Assumes transaction identifier is valid and exists in clog.
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().
187 bool /* true if given transaction aborted */
188 TransactionIdDidAbort(TransactionId transactionId
)
192 xidstatus
= TransactionLogFetch(transactionId
);
195 * If it's marked aborted, it's aborted.
197 if (xidstatus
== TRANSACTION_STATUS_ABORTED
)
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
206 if (xidstatus
== TRANSACTION_STATUS_SUB_COMMITTED
)
208 TransactionId parentXid
;
210 if (TransactionIdPrecedes(transactionId
, TransactionXmin
))
212 parentXid
= SubTransGetParent(transactionId
);
213 if (!TransactionIdIsValid(parentXid
))
215 /* see notes in TransactionIdDidCommit */
216 elog(WARNING
, "no pg_subtrans entry for subcommitted XID %u",
220 return TransactionIdDidAbort(parentXid
);
230 * TransactionIdCommitTree
231 * Marks the given transaction and children as committed
233 * "xid" is a toplevel transaction commit, and the xids array contains its
234 * committed subtransactions.
236 * This commit operation is not guaranteed to be atomic, but if not, subxids
237 * are correctly marked subcommit first.
240 TransactionIdCommitTree(TransactionId xid
, int nxids
, TransactionId
*xids
)
242 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
243 TRANSACTION_STATUS_COMMITTED
,
248 * TransactionIdAsyncCommitTree
249 * Same as above, but for async commits. The commit record LSN is needed.
252 TransactionIdAsyncCommitTree(TransactionId xid
, int nxids
, TransactionId
*xids
,
255 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
256 TRANSACTION_STATUS_COMMITTED
, lsn
);
260 * TransactionIdAbortTree
261 * Marks the given transaction and children as aborted.
263 * "xid" is a toplevel transaction commit, and the xids array contains its
264 * committed subtransactions.
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.
270 TransactionIdAbortTree(TransactionId xid
, int nxids
, TransactionId
*xids
)
272 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
273 TRANSACTION_STATUS_ABORTED
, InvalidXLogRecPtr
);
277 * TransactionIdPrecedes --- is id1 logically < id2?
280 TransactionIdPrecedes(TransactionId id1
, TransactionId id2
)
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.
288 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
291 diff
= (int32
) (id1
- id2
);
296 * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
299 TransactionIdPrecedesOrEquals(TransactionId id1
, TransactionId id2
)
303 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
306 diff
= (int32
) (id1
- id2
);
311 * TransactionIdFollows --- is id1 logically > id2?
314 TransactionIdFollows(TransactionId id1
, TransactionId id2
)
318 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
321 diff
= (int32
) (id1
- id2
);
326 * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
329 TransactionIdFollowsOrEquals(TransactionId id1
, TransactionId id2
)
333 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
336 diff
= (int32
) (id1
- id2
);
342 * TransactionIdLatest --- get latest XID among a main xact and its children
345 TransactionIdLatest(TransactionId mainxid
,
346 int nxids
, const TransactionId
*xids
)
348 TransactionId result
;
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.
360 if (TransactionIdPrecedes(result
, xids
[nxids
]))
361 result
= xids
[nxids
];
368 * TransactionIdGetCommitLSN
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.
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
382 TransactionIdGetCommitLSN(TransactionId xid
)
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.
392 if (TransactionIdEquals(xid
, cachedFetchXid
))
393 return cachedCommitLSN
;
395 /* Special XIDs are always known committed */
396 if (!TransactionIdIsNormal(xid
))
397 return InvalidXLogRecPtr
;
400 * Get the transaction status.
402 (void) TransactionIdGetStatus(xid
, &result
);