Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * transam.c
4 : * postgres transaction (commit) log interface routines
5 : *
6 : * Portions Copyright (c) 1996-2017, 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 741562 : 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 741562 : if (TransactionIdEquals(transactionId, cachedFetchXid))
62 703984 : return cachedFetchXidStatus;
63 :
64 : /*
65 : * Also, check to see if the transaction ID is a permanent one.
66 : */
67 37578 : if (!TransactionIdIsNormal(transactionId))
68 : {
69 7241 : if (TransactionIdEquals(transactionId, BootstrapTransactionId))
70 7240 : return TRANSACTION_STATUS_COMMITTED;
71 1 : if (TransactionIdEquals(transactionId, FrozenTransactionId))
72 1 : return TRANSACTION_STATUS_COMMITTED;
73 0 : return TRANSACTION_STATUS_ABORTED;
74 : }
75 :
76 : /*
77 : * Get the transaction status.
78 : */
79 30337 : 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 30337 : if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
86 : xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
87 : {
88 29612 : cachedFetchXid = transactionId;
89 29612 : cachedFetchXidStatus = xidstatus;
90 29612 : cachedCommitLSN = xidlsn;
91 : }
92 :
93 30337 : 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 741560 : TransactionIdDidCommit(TransactionId transactionId)
126 : {
127 : XidStatus xidstatus;
128 :
129 741560 : xidstatus = TransactionLogFetch(transactionId);
130 :
131 : /*
132 : * If it's marked committed, it's committed.
133 : */
134 741560 : if (xidstatus == TRANSACTION_STATUS_COMMITTED)
135 736222 : 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 5338 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
152 : {
153 : TransactionId parentXid;
154 :
155 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
156 0 : return false;
157 0 : parentXid = SubTransGetParent(transactionId);
158 0 : if (!TransactionIdIsValid(parentXid))
159 : {
160 0 : elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
161 : transactionId);
162 0 : return false;
163 : }
164 0 : return TransactionIdDidCommit(parentXid);
165 : }
166 :
167 : /*
168 : * It's not committed.
169 : */
170 5338 : 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 2 : TransactionIdDidAbort(TransactionId transactionId)
182 : {
183 : XidStatus xidstatus;
184 :
185 2 : xidstatus = TransactionLogFetch(transactionId);
186 :
187 : /*
188 : * If it's marked aborted, it's aborted.
189 : */
190 2 : if (xidstatus == TRANSACTION_STATUS_ABORTED)
191 2 : 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 0 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
200 : {
201 : TransactionId parentXid;
202 :
203 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
204 0 : return true;
205 0 : parentXid = SubTransGetParent(transactionId);
206 0 : if (!TransactionIdIsValid(parentXid))
207 : {
208 : /* see notes in TransactionIdDidCommit */
209 0 : elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
210 : transactionId);
211 0 : return true;
212 : }
213 0 : return TransactionIdDidAbort(parentXid);
214 : }
215 :
216 : /*
217 : * It's not aborted.
218 : */
219 0 : 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 tqual.c checks for the same XID. If this isn't extremely fast
232 : * then it will be counterproductive.
233 : *
234 : * Note:
235 : * Assumes transaction identifier is valid.
236 : */
237 : bool
238 47150 : TransactionIdIsKnownCompleted(TransactionId transactionId)
239 : {
240 47150 : if (TransactionIdEquals(transactionId, cachedFetchXid))
241 : {
242 : /* If it's in the cache at all, it must be completed. */
243 45185 : return true;
244 : }
245 :
246 1965 : 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 9502 : TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
261 : {
262 9502 : TransactionIdSetTreeStatus(xid, nxids, xids,
263 : TRANSACTION_STATUS_COMMITTED,
264 : InvalidXLogRecPtr);
265 9502 : }
266 :
267 : /*
268 : * TransactionIdAsyncCommitTree
269 : * Same as above, but for async commits. The commit record LSN is needed.
270 : */
271 : void
272 407 : TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
273 : XLogRecPtr lsn)
274 : {
275 407 : TransactionIdSetTreeStatus(xid, nxids, xids,
276 : TRANSACTION_STATUS_COMMITTED, lsn);
277 407 : }
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 725 : TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
291 : {
292 725 : TransactionIdSetTreeStatus(xid, nxids, xids,
293 : TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
294 725 : }
295 :
296 : /*
297 : * TransactionIdPrecedes --- is id1 logically < id2?
298 : */
299 : bool
300 4815091 : 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 4815091 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
309 569716 : return (id1 < id2);
310 :
311 4245375 : diff = (int32) (id1 - id2);
312 4245375 : return (diff < 0);
313 : }
314 :
315 : /*
316 : * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
317 : */
318 : bool
319 505 : TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
320 : {
321 : int32 diff;
322 :
323 505 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
324 0 : return (id1 <= id2);
325 :
326 505 : diff = (int32) (id1 - id2);
327 505 : return (diff <= 0);
328 : }
329 :
330 : /*
331 : * TransactionIdFollows --- is id1 logically > id2?
332 : */
333 : bool
334 752680 : TransactionIdFollows(TransactionId id1, TransactionId id2)
335 : {
336 : int32 diff;
337 :
338 752680 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
339 32673 : return (id1 > id2);
340 :
341 720007 : diff = (int32) (id1 - id2);
342 720007 : return (diff > 0);
343 : }
344 :
345 : /*
346 : * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
347 : */
348 : bool
349 169315 : TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
350 : {
351 : int32 diff;
352 :
353 169315 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
354 0 : return (id1 >= id2);
355 :
356 169315 : diff = (int32) (id1 - id2);
357 169315 : return (diff >= 0);
358 : }
359 :
360 :
361 : /*
362 : * TransactionIdLatest --- get latest XID among a main xact and its children
363 : */
364 : TransactionId
365 11115 : 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 11115 : result = mainxid;
378 22254 : while (--nxids >= 0)
379 : {
380 24 : if (TransactionIdPrecedes(result, xids[nxids]))
381 19 : result = xids[nxids];
382 : }
383 11115 : 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 735990 : 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 735990 : if (TransactionIdEquals(xid, cachedFetchXid))
413 728751 : return cachedCommitLSN;
414 :
415 : /* Special XIDs are always known committed */
416 7239 : if (!TransactionIdIsNormal(xid))
417 7239 : return InvalidXLogRecPtr;
418 :
419 : /*
420 : * Get the transaction status.
421 : */
422 0 : (void) TransactionIdGetStatus(xid, &result);
423 :
424 0 : return result;
425 : }
|