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 : }
|