Age Owner Branch data TLA 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
7227 tgl@sss.pgh.pa.us 52 :CBC 9828403 : 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 [ + + ]: 9828403 : if (TransactionIdEquals(transactionId, cachedFetchXid))
62 : 8677434 : return cachedFetchXidStatus;
63 : :
64 : : /*
65 : : * Also, check to see if the transaction ID is a permanent one.
66 : : */
8207 bruce@momjian.us 67 [ + + ]: 1150969 : if (!TransactionIdIsNormal(transactionId))
68 : : {
8268 tgl@sss.pgh.pa.us 69 [ + + ]: 511059 : if (TransactionIdEquals(transactionId, BootstrapTransactionId))
7227 70 : 507149 : return TRANSACTION_STATUS_COMMITTED;
8268 71 [ + + ]: 3910 : if (TransactionIdEquals(transactionId, FrozenTransactionId))
7227 72 : 3906 : return TRANSACTION_STATUS_COMMITTED;
73 : 4 : return TRANSACTION_STATUS_ABORTED;
74 : : }
75 : :
76 : : /*
77 : : * Get the transaction status.
78 : : */
6101 79 : 639910 : 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 : : */
7227 85 [ + + + - ]: 639910 : if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
86 : : xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
87 : : {
6101 88 : 590462 : cachedFetchXid = transactionId;
7227 89 : 590462 : cachedFetchXidStatus = xidstatus;
6101 90 : 590462 : cachedCommitLSN = xidlsn;
91 : : }
92 : :
7227 93 : 639910 : 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 */
10141 scrappy@hub.org 126 : 9806355 : TransactionIdDidCommit(TransactionId transactionId)
127 : : {
128 : : XidStatus xidstatus;
129 : :
7227 tgl@sss.pgh.pa.us 130 : 9806355 : xidstatus = TransactionLogFetch(transactionId);
131 : :
132 : : /*
133 : : * If it's marked committed, it's committed.
134 : : */
135 [ + + ]: 9806355 : if (xidstatus == TRANSACTION_STATUS_COMMITTED)
136 : 9690141 : 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 [ - + ]: 116214 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
153 : : {
154 : : TransactionId parentXid;
155 : :
7150 tgl@sss.pgh.pa.us 156 [ # # ]:UBC 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
7175 157 : 0 : return false;
7227 158 : 0 : parentXid = SubTransGetParent(transactionId);
6876 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 : : }
7227 165 : 0 : return TransactionIdDidCommit(parentXid);
166 : : }
167 : :
168 : : /*
169 : : * It's not committed.
170 : : */
7227 tgl@sss.pgh.pa.us 171 :CBC 116214 : 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 */
10141 scrappy@hub.org 188 : 22048 : TransactionIdDidAbort(TransactionId transactionId)
189 : : {
190 : : XidStatus xidstatus;
191 : :
7227 tgl@sss.pgh.pa.us 192 : 22048 : xidstatus = TransactionLogFetch(transactionId);
193 : :
194 : : /*
195 : : * If it's marked aborted, it's aborted.
196 : : */
197 [ + + ]: 22048 : if (xidstatus == TRANSACTION_STATUS_ABORTED)
198 : 18 : 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 [ - + ]: 22030 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
207 : : {
208 : : TransactionId parentXid;
209 : :
7150 tgl@sss.pgh.pa.us 210 [ # # ]:UBC 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
7175 211 : 0 : return true;
212 : 0 : parentXid = SubTransGetParent(transactionId);
6876 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 : : }
7175 220 : 0 : return TransactionIdDidAbort(parentXid);
221 : : }
222 : :
223 : : /*
224 : : * It's not aborted.
225 : : */
7227 tgl@sss.pgh.pa.us 226 :CBC 22030 : 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
5655 alvherre@alvh.no-ip. 240 : 105648 : TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
241 : : {
242 : 105648 : TransactionIdSetTreeStatus(xid, nxids, xids,
243 : : TRANSACTION_STATUS_COMMITTED,
244 : : InvalidXLogRecPtr);
7227 tgl@sss.pgh.pa.us 245 : 105648 : }
246 : :
247 : : /*
248 : : * TransactionIdAsyncCommitTree
249 : : * Same as above, but for async commits. The commit record LSN is needed.
250 : : */
251 : : void
5655 alvherre@alvh.no-ip. 252 : 24053 : TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
253 : : XLogRecPtr lsn)
254 : : {
255 : 24053 : TransactionIdSetTreeStatus(xid, nxids, xids,
256 : : TRANSACTION_STATUS_COMMITTED, lsn);
6101 tgl@sss.pgh.pa.us 257 : 24053 : }
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
5655 alvherre@alvh.no-ip. 270 : 7033 : TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
271 : : {
272 : 7033 : TransactionIdSetTreeStatus(xid, nxids, xids,
273 : : TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
7227 tgl@sss.pgh.pa.us 274 : 7033 : }
275 : :
276 : : /*
277 : : * TransactionIdPrecedes --- is id1 logically < id2?
278 : : */
279 : : bool
8267 280 : 130618814 : 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 [ + + + + ]: 130618814 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
289 : 44012519 : return (id1 < id2);
290 : :
291 : 86606295 : diff = (int32) (id1 - id2);
292 : 86606295 : return (diff < 0);
293 : : }
294 : :
295 : : /*
296 : : * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
297 : : */
298 : : bool
299 : 8971524 : TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
300 : : {
301 : : int32 diff;
302 : :
303 [ + - + + ]: 8971524 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
304 : 1011 : return (id1 <= id2);
305 : :
306 : 8970513 : diff = (int32) (id1 - id2);
307 : 8970513 : return (diff <= 0);
308 : : }
309 : :
310 : : /*
311 : : * TransactionIdFollows --- is id1 logically > id2?
312 : : */
313 : : bool
314 : 29171416 : TransactionIdFollows(TransactionId id1, TransactionId id2)
315 : : {
316 : : int32 diff;
317 : :
318 [ + + + + ]: 29171416 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
319 : 16313140 : return (id1 > id2);
320 : :
321 : 12858276 : diff = (int32) (id1 - id2);
322 : 12858276 : return (diff > 0);
323 : : }
324 : :
325 : : /*
326 : : * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
327 : : */
328 : : bool
329 : 62604862 : TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
330 : : {
331 : : int32 diff;
332 : :
333 [ + + - + ]: 62604862 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
334 : 53072 : return (id1 >= id2);
335 : :
336 : 62551790 : diff = (int32) (id1 - id2);
337 : 62551790 : return (diff >= 0);
338 : : }
339 : :
340 : :
341 : : /*
342 : : * TransactionIdLatest --- get latest XID among a main xact and its children
343 : : */
344 : : TransactionId
6063 345 : 166180 : 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 : 166180 : result = mainxid;
358 [ + + ]: 174746 : while (--nxids >= 0)
359 : : {
360 [ + + ]: 8566 : if (TransactionIdPrecedes(result, xids[nxids]))
361 : 743 : result = xids[nxids];
362 : : }
363 : 166180 : 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
6101 382 : 8921133 : 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 [ + + ]: 8921133 : if (TransactionIdEquals(xid, cachedFetchXid))
393 : 8430591 : return cachedCommitLSN;
394 : :
395 : : /* Special XIDs are always known committed */
396 [ + - ]: 490542 : if (!TransactionIdIsNormal(xid))
397 : 490542 : return InvalidXLogRecPtr;
398 : :
399 : : /*
400 : : * Get the transaction status.
401 : : */
6101 tgl@sss.pgh.pa.us 402 :UBC 0 : (void) TransactionIdGetStatus(xid, &result);
403 : :
404 : 0 : return result;
405 : : }
|