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 */
33static TransactionId cachedFetchXid = InvalidTransactionId;
34static XidStatus cachedFetchXidStatus;
35static XLogRecPtr cachedCommitLSN;
36
37/* Local functions */
38static 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 */
51static XidStatus
52TransactionLogFetch(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 */
124bool /* true if given transaction committed */
125TransactionIdDidCommit(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 */
180bool /* true if given transaction aborted */
181TransactionIdDidAbort(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 */
237bool
238TransactionIdIsKnownCompleted(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 */
259void
260TransactionIdCommitTree(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 */
271void
272TransactionIdAsyncCommitTree(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 */
289void
290TransactionIdAbortTree(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 */
299bool
300TransactionIdPrecedes(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 */
318bool
319TransactionIdPrecedesOrEquals(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 */
333bool
334TransactionIdFollows(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 */
348bool
349TransactionIdFollowsOrEquals(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 */
364TransactionId
365TransactionIdLatest(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 */
401XLogRecPtr
402TransactionIdGetCommitLSN(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