1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * transam.c |
4 | * postgres transaction (commit) log interface routines |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 | 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 | if (TransactionIdEquals(transactionId, cachedFetchXid)) |
62 | return cachedFetchXidStatus; |
63 | |
64 | /* |
65 | * Also, check to see if the transaction ID is a permanent one. |
66 | */ |
67 | if (!TransactionIdIsNormal(transactionId)) |
68 | { |
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; |
74 | } |
75 | |
76 | /* |
77 | * Get the transaction status. |
78 | */ |
79 | 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 | if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS && |
86 | xidstatus != TRANSACTION_STATUS_SUB_COMMITTED) |
87 | { |
88 | cachedFetchXid = transactionId; |
89 | cachedFetchXidStatus = xidstatus; |
90 | cachedCommitLSN = xidlsn; |
91 | } |
92 | |
93 | 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. |
114 | * ---------------------------------------------------------------- |
115 | */ |
116 | |
117 | /* |
118 | * TransactionIdDidCommit |
119 | * True iff transaction associated with the identifier did commit. |
120 | * |
121 | * Note: |
122 | * Assumes transaction identifier is valid and exists in clog. |
123 | */ |
124 | bool /* true if given transaction committed */ |
125 | TransactionIdDidCommit(TransactionId transactionId) |
126 | { |
127 | XidStatus xidstatus; |
128 | |
129 | xidstatus = TransactionLogFetch(transactionId); |
130 | |
131 | /* |
132 | * If it's marked committed, it's committed. |
133 | */ |
134 | if (xidstatus == TRANSACTION_STATUS_COMMITTED) |
135 | return true; |
136 | |
137 | /* |
138 | * If it's marked subcommitted, we have to check the parent recursively. |
139 | * However, if it's older than TransactionXmin, we can't look at |
140 | * pg_subtrans; instead assume that the parent crashed without cleaning up |
141 | * its children. |
142 | * |
143 | * Originally we Assert'ed that the result of SubTransGetParent was not |
144 | * zero. However with the introduction of prepared transactions, there can |
145 | * be a window just after database startup where we do not have complete |
146 | * knowledge in pg_subtrans of the transactions after TransactionXmin. |
147 | * StartupSUBTRANS() has ensured that any missing information will be |
148 | * zeroed. Since this case should not happen under normal conditions, it |
149 | * seems reasonable to emit a WARNING for it. |
150 | */ |
151 | if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED) |
152 | { |
153 | TransactionId parentXid; |
154 | |
155 | if (TransactionIdPrecedes(transactionId, TransactionXmin)) |
156 | return false; |
157 | parentXid = SubTransGetParent(transactionId); |
158 | if (!TransactionIdIsValid(parentXid)) |
159 | { |
160 | elog(WARNING, "no pg_subtrans entry for subcommitted XID %u" , |
161 | transactionId); |
162 | return false; |
163 | } |
164 | return TransactionIdDidCommit(parentXid); |
165 | } |
166 | |
167 | /* |
168 | * It's not committed. |
169 | */ |
170 | return false; |
171 | } |
172 | |
173 | /* |
174 | * TransactionIdDidAbort |
175 | * True iff transaction associated with the identifier did abort. |
176 | * |
177 | * Note: |
178 | * Assumes transaction identifier is valid and exists in clog. |
179 | */ |
180 | bool /* true if given transaction aborted */ |
181 | TransactionIdDidAbort(TransactionId transactionId) |
182 | { |
183 | XidStatus xidstatus; |
184 | |
185 | xidstatus = TransactionLogFetch(transactionId); |
186 | |
187 | /* |
188 | * If it's marked aborted, it's aborted. |
189 | */ |
190 | if (xidstatus == TRANSACTION_STATUS_ABORTED) |
191 | return true; |
192 | |
193 | /* |
194 | * If it's marked subcommitted, we have to check the parent recursively. |
195 | * However, if it's older than TransactionXmin, we can't look at |
196 | * pg_subtrans; instead assume that the parent crashed without cleaning up |
197 | * its children. |
198 | */ |
199 | if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED) |
200 | { |
201 | TransactionId parentXid; |
202 | |
203 | if (TransactionIdPrecedes(transactionId, TransactionXmin)) |
204 | return true; |
205 | parentXid = SubTransGetParent(transactionId); |
206 | if (!TransactionIdIsValid(parentXid)) |
207 | { |
208 | /* see notes in TransactionIdDidCommit */ |
209 | elog(WARNING, "no pg_subtrans entry for subcommitted XID %u" , |
210 | transactionId); |
211 | return true; |
212 | } |
213 | return TransactionIdDidAbort(parentXid); |
214 | } |
215 | |
216 | /* |
217 | * It's not aborted. |
218 | */ |
219 | return false; |
220 | } |
221 | |
222 | /* |
223 | * TransactionIdIsKnownCompleted |
224 | * True iff transaction associated with the identifier is currently |
225 | * known to have either committed or aborted. |
226 | * |
227 | * This does NOT look into pg_xact but merely probes our local cache |
228 | * (and so it's not named TransactionIdDidComplete, which would be the |
229 | * appropriate name for a function that worked that way). The intended |
230 | * use is just to short-circuit TransactionIdIsInProgress calls when doing |
231 | * repeated heapam_visibility.c checks for the same XID. If this isn't |
232 | * extremely fast then it will be counterproductive. |
233 | * |
234 | * Note: |
235 | * Assumes transaction identifier is valid. |
236 | */ |
237 | bool |
238 | TransactionIdIsKnownCompleted(TransactionId transactionId) |
239 | { |
240 | if (TransactionIdEquals(transactionId, cachedFetchXid)) |
241 | { |
242 | /* If it's in the cache at all, it must be completed. */ |
243 | return true; |
244 | } |
245 | |
246 | return false; |
247 | } |
248 | |
249 | /* |
250 | * TransactionIdCommitTree |
251 | * Marks the given transaction and children as committed |
252 | * |
253 | * "xid" is a toplevel transaction commit, and the xids array contains its |
254 | * committed subtransactions. |
255 | * |
256 | * This commit operation is not guaranteed to be atomic, but if not, subxids |
257 | * are correctly marked subcommit first. |
258 | */ |
259 | void |
260 | TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids) |
261 | { |
262 | TransactionIdSetTreeStatus(xid, nxids, xids, |
263 | TRANSACTION_STATUS_COMMITTED, |
264 | InvalidXLogRecPtr); |
265 | } |
266 | |
267 | /* |
268 | * TransactionIdAsyncCommitTree |
269 | * Same as above, but for async commits. The commit record LSN is needed. |
270 | */ |
271 | void |
272 | TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, |
273 | XLogRecPtr lsn) |
274 | { |
275 | TransactionIdSetTreeStatus(xid, nxids, xids, |
276 | TRANSACTION_STATUS_COMMITTED, lsn); |
277 | } |
278 | |
279 | /* |
280 | * TransactionIdAbortTree |
281 | * Marks the given transaction and children as aborted. |
282 | * |
283 | * "xid" is a toplevel transaction commit, and the xids array contains its |
284 | * committed subtransactions. |
285 | * |
286 | * We don't need to worry about the non-atomic behavior, since any onlookers |
287 | * will consider all the xacts as not-yet-committed anyway. |
288 | */ |
289 | void |
290 | TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids) |
291 | { |
292 | TransactionIdSetTreeStatus(xid, nxids, xids, |
293 | TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr); |
294 | } |
295 | |
296 | /* |
297 | * TransactionIdPrecedes --- is id1 logically < id2? |
298 | */ |
299 | bool |
300 | TransactionIdPrecedes(TransactionId id1, TransactionId id2) |
301 | { |
302 | /* |
303 | * If either ID is a permanent XID then we can just do unsigned |
304 | * comparison. If both are normal, do a modulo-2^32 comparison. |
305 | */ |
306 | int32 diff; |
307 | |
308 | if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2)) |
309 | return (id1 < id2); |
310 | |
311 | diff = (int32) (id1 - id2); |
312 | return (diff < 0); |
313 | } |
314 | |
315 | /* |
316 | * TransactionIdPrecedesOrEquals --- is id1 logically <= id2? |
317 | */ |
318 | bool |
319 | TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2) |
320 | { |
321 | int32 diff; |
322 | |
323 | if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2)) |
324 | return (id1 <= id2); |
325 | |
326 | diff = (int32) (id1 - id2); |
327 | return (diff <= 0); |
328 | } |
329 | |
330 | /* |
331 | * TransactionIdFollows --- is id1 logically > id2? |
332 | */ |
333 | bool |
334 | TransactionIdFollows(TransactionId id1, TransactionId id2) |
335 | { |
336 | int32 diff; |
337 | |
338 | if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2)) |
339 | return (id1 > id2); |
340 | |
341 | diff = (int32) (id1 - id2); |
342 | return (diff > 0); |
343 | } |
344 | |
345 | /* |
346 | * TransactionIdFollowsOrEquals --- is id1 logically >= id2? |
347 | */ |
348 | bool |
349 | TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2) |
350 | { |
351 | int32 diff; |
352 | |
353 | if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2)) |
354 | return (id1 >= id2); |
355 | |
356 | diff = (int32) (id1 - id2); |
357 | return (diff >= 0); |
358 | } |
359 | |
360 | |
361 | /* |
362 | * TransactionIdLatest --- get latest XID among a main xact and its children |
363 | */ |
364 | TransactionId |
365 | TransactionIdLatest(TransactionId mainxid, |
366 | int nxids, const TransactionId *xids) |
367 | { |
368 | TransactionId result; |
369 | |
370 | /* |
371 | * In practice it is highly likely that the xids[] array is sorted, and so |
372 | * we could save some cycles by just taking the last child XID, but this |
373 | * probably isn't so performance-critical that it's worth depending on |
374 | * that assumption. But just to show we're not totally stupid, scan the |
375 | * array back-to-front to avoid useless assignments. |
376 | */ |
377 | result = mainxid; |
378 | while (--nxids >= 0) |
379 | { |
380 | if (TransactionIdPrecedes(result, xids[nxids])) |
381 | result = xids[nxids]; |
382 | } |
383 | return result; |
384 | } |
385 | |
386 | |
387 | /* |
388 | * TransactionIdGetCommitLSN |
389 | * |
390 | * This function returns an LSN that is late enough to be able |
391 | * to guarantee that if we flush up to the LSN returned then we |
392 | * will have flushed the transaction's commit record to disk. |
393 | * |
394 | * The result is not necessarily the exact LSN of the transaction's |
395 | * commit record! For example, for long-past transactions (those whose |
396 | * clog pages already migrated to disk), we'll return InvalidXLogRecPtr. |
397 | * Also, because we group transactions on the same clog page to conserve |
398 | * storage, we might return the LSN of a later transaction that falls into |
399 | * the same group. |
400 | */ |
401 | XLogRecPtr |
402 | TransactionIdGetCommitLSN(TransactionId xid) |
403 | { |
404 | XLogRecPtr result; |
405 | |
406 | /* |
407 | * Currently, all uses of this function are for xids that were just |
408 | * reported to be committed by TransactionLogFetch, so we expect that |
409 | * checking TransactionLogFetch's cache will usually succeed and avoid an |
410 | * extra trip to shared memory. |
411 | */ |
412 | if (TransactionIdEquals(xid, cachedFetchXid)) |
413 | return cachedCommitLSN; |
414 | |
415 | /* Special XIDs are always known committed */ |
416 | if (!TransactionIdIsNormal(xid)) |
417 | return InvalidXLogRecPtr; |
418 | |
419 | /* |
420 | * Get the transaction status. |
421 | */ |
422 | (void) TransactionIdGetStatus(xid, &result); |
423 | |
424 | return result; |
425 | } |
426 | |