1 /*-------------------------------------------------------------------------
4 * postgres transaction log interface routines
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
14 * This file contains the high level access-method interface to the
17 *-------------------------------------------------------------------------
22 #include "access/clog.h"
23 #include "access/subtrans.h"
24 #include "access/transam.h"
25 #include "utils/snapmgr.h"
29 * Single-item cache for results of TransactionLogFetch. It's worth having
30 * such a cache because we frequently find ourselves repeatedly checking the
31 * same XID, for example when scanning a table just after a bulk insert,
34 static TransactionId cachedFetchXid
= InvalidTransactionId
;
35 static XidStatus cachedFetchXidStatus
;
36 static XLogRecPtr cachedCommitLSN
;
38 /* Handy constant for an invalid xlog recptr */
39 static const XLogRecPtr InvalidXLogRecPtr
= {0, 0};
42 static XidStatus
TransactionLogFetch(TransactionId transactionId
);
45 /* ----------------------------------------------------------------
46 * Postgres log access method interface
49 * ----------------------------------------------------------------
53 * TransactionLogFetch --- fetch commit status of specified transaction id
56 TransactionLogFetch(TransactionId transactionId
)
62 * Before going to the commit log manager, check our single item cache to
63 * see if we didn't just check the transaction status a moment ago.
65 if (TransactionIdEquals(transactionId
, cachedFetchXid
))
66 return cachedFetchXidStatus
;
69 * Also, check to see if the transaction ID is a permanent one.
71 if (!TransactionIdIsNormal(transactionId
))
73 if (TransactionIdEquals(transactionId
, BootstrapTransactionId
))
74 return TRANSACTION_STATUS_COMMITTED
;
75 if (TransactionIdEquals(transactionId
, FrozenTransactionId
))
76 return TRANSACTION_STATUS_COMMITTED
;
77 return TRANSACTION_STATUS_ABORTED
;
81 * Get the transaction status.
83 xidstatus
= TransactionIdGetStatus(transactionId
, &xidlsn
);
86 * Cache it, but DO NOT cache status for unfinished or sub-committed
87 * transactions! We only cache status that is guaranteed not to change.
89 if (xidstatus
!= TRANSACTION_STATUS_IN_PROGRESS
&&
90 xidstatus
!= TRANSACTION_STATUS_SUB_COMMITTED
)
92 cachedFetchXid
= transactionId
;
93 cachedFetchXidStatus
= xidstatus
;
94 cachedCommitLSN
= xidlsn
;
100 /* ----------------------------------------------------------------
101 * Interface functions
103 * TransactionIdDidCommit
104 * TransactionIdDidAbort
106 * these functions test the transaction status of
107 * a specified transaction id.
109 * TransactionIdCommitTree
110 * TransactionIdAsyncCommitTree
111 * TransactionIdAbortTree
113 * these functions set the transaction status of the specified
116 * See also TransactionIdIsInProgress, which once was in this module
117 * but now lives in procarray.c.
118 * ----------------------------------------------------------------
122 * TransactionIdDidCommit
123 * True iff transaction associated with the identifier did commit.
126 * Assumes transaction identifier is valid.
128 bool /* true if given transaction committed */
129 TransactionIdDidCommit(TransactionId transactionId
)
133 xidstatus
= TransactionLogFetch(transactionId
);
136 * If it's marked committed, it's committed.
138 if (xidstatus
== TRANSACTION_STATUS_COMMITTED
)
142 * If it's marked subcommitted, we have to check the parent recursively.
143 * However, if it's older than TransactionXmin, we can't look at
144 * pg_subtrans; instead assume that the parent crashed without cleaning up
147 * Originally we Assert'ed that the result of SubTransGetParent was not
148 * zero. However with the introduction of prepared transactions, there can
149 * be a window just after database startup where we do not have complete
150 * knowledge in pg_subtrans of the transactions after TransactionXmin.
151 * StartupSUBTRANS() has ensured that any missing information will be
152 * zeroed. Since this case should not happen under normal conditions, it
153 * seems reasonable to emit a WARNING for it.
155 if (xidstatus
== TRANSACTION_STATUS_SUB_COMMITTED
)
157 TransactionId parentXid
;
159 if (TransactionIdPrecedes(transactionId
, TransactionXmin
))
161 parentXid
= SubTransGetParent(transactionId
);
162 if (!TransactionIdIsValid(parentXid
))
164 elog(WARNING
, "no pg_subtrans entry for subcommitted XID %u",
168 return TransactionIdDidCommit(parentXid
);
172 * It's not committed.
178 * TransactionIdDidAbort
179 * True iff transaction associated with the identifier did abort.
182 * Assumes transaction identifier is valid.
184 bool /* true if given transaction aborted */
185 TransactionIdDidAbort(TransactionId transactionId
)
189 xidstatus
= TransactionLogFetch(transactionId
);
192 * If it's marked aborted, it's aborted.
194 if (xidstatus
== TRANSACTION_STATUS_ABORTED
)
198 * If it's marked subcommitted, we have to check the parent recursively.
199 * However, if it's older than TransactionXmin, we can't look at
200 * pg_subtrans; instead assume that the parent crashed without cleaning up
203 if (xidstatus
== TRANSACTION_STATUS_SUB_COMMITTED
)
205 TransactionId parentXid
;
207 if (TransactionIdPrecedes(transactionId
, TransactionXmin
))
209 parentXid
= SubTransGetParent(transactionId
);
210 if (!TransactionIdIsValid(parentXid
))
212 /* see notes in TransactionIdDidCommit */
213 elog(WARNING
, "no pg_subtrans entry for subcommitted XID %u",
217 return TransactionIdDidAbort(parentXid
);
227 * TransactionIdIsKnownCompleted
228 * True iff transaction associated with the identifier is currently
229 * known to have either committed or aborted.
231 * This does NOT look into pg_clog but merely probes our local cache
232 * (and so it's not named TransactionIdDidComplete, which would be the
233 * appropriate name for a function that worked that way). The intended
234 * use is just to short-circuit TransactionIdIsInProgress calls when doing
235 * repeated tqual.c checks for the same XID. If this isn't extremely fast
236 * then it will be counterproductive.
239 * Assumes transaction identifier is valid.
242 TransactionIdIsKnownCompleted(TransactionId transactionId
)
244 if (TransactionIdEquals(transactionId
, cachedFetchXid
))
246 /* If it's in the cache at all, it must be completed. */
254 * TransactionIdCommitTree
255 * Marks the given transaction and children as committed
257 * "xid" is a toplevel transaction commit, and the xids array contains its
258 * committed subtransactions.
260 * This commit operation is not guaranteed to be atomic, but if not, subxids
261 * are correctly marked subcommit first.
264 TransactionIdCommitTree(TransactionId xid
, int nxids
, TransactionId
*xids
)
266 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
267 TRANSACTION_STATUS_COMMITTED
,
272 * TransactionIdAsyncCommitTree
273 * Same as above, but for async commits. The commit record LSN is needed.
276 TransactionIdAsyncCommitTree(TransactionId xid
, int nxids
, TransactionId
*xids
,
279 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
280 TRANSACTION_STATUS_COMMITTED
, lsn
);
284 * TransactionIdAbortTree
285 * Marks the given transaction and children as aborted.
287 * "xid" is a toplevel transaction commit, and the xids array contains its
288 * committed subtransactions.
290 * We don't need to worry about the non-atomic behavior, since any onlookers
291 * will consider all the xacts as not-yet-committed anyway.
294 TransactionIdAbortTree(TransactionId xid
, int nxids
, TransactionId
*xids
)
296 TransactionIdSetTreeStatus(xid
, nxids
, xids
,
297 TRANSACTION_STATUS_ABORTED
, InvalidXLogRecPtr
);
301 * TransactionIdPrecedes --- is id1 logically < id2?
304 TransactionIdPrecedes(TransactionId id1
, TransactionId id2
)
307 * If either ID is a permanent XID then we can just do unsigned
308 * comparison. If both are normal, do a modulo-2^31 comparison.
312 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
315 diff
= (int32
) (id1
- id2
);
320 * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
323 TransactionIdPrecedesOrEquals(TransactionId id1
, TransactionId id2
)
327 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
330 diff
= (int32
) (id1
- id2
);
335 * TransactionIdFollows --- is id1 logically > id2?
338 TransactionIdFollows(TransactionId id1
, TransactionId id2
)
342 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
345 diff
= (int32
) (id1
- id2
);
350 * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
353 TransactionIdFollowsOrEquals(TransactionId id1
, TransactionId id2
)
357 if (!TransactionIdIsNormal(id1
) || !TransactionIdIsNormal(id2
))
360 diff
= (int32
) (id1
- id2
);
366 * TransactionIdLatest --- get latest XID among a main xact and its children
369 TransactionIdLatest(TransactionId mainxid
,
370 int nxids
, const TransactionId
*xids
)
372 TransactionId result
;
375 * In practice it is highly likely that the xids[] array is sorted, and so
376 * we could save some cycles by just taking the last child XID, but this
377 * probably isn't so performance-critical that it's worth depending on
378 * that assumption. But just to show we're not totally stupid, scan the
379 * array back-to-front to avoid useless assignments.
384 if (TransactionIdPrecedes(result
, xids
[nxids
]))
385 result
= xids
[nxids
];
392 * TransactionIdGetCommitLSN
394 * This function returns an LSN that is late enough to be able
395 * to guarantee that if we flush up to the LSN returned then we
396 * will have flushed the transaction's commit record to disk.
398 * The result is not necessarily the exact LSN of the transaction's
399 * commit record! For example, for long-past transactions (those whose
400 * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
401 * Also, because we group transactions on the same clog page to conserve
402 * storage, we might return the LSN of a later transaction that falls into
406 TransactionIdGetCommitLSN(TransactionId xid
)
411 * Currently, all uses of this function are for xids that were just
412 * reported to be committed by TransactionLogFetch, so we expect that
413 * checking TransactionLogFetch's cache will usually succeed and avoid an
414 * extra trip to shared memory.
416 if (TransactionIdEquals(xid
, cachedFetchXid
))
417 return cachedCommitLSN
;
419 /* Special XIDs are always known committed */
420 if (!TransactionIdIsNormal(xid
))
421 return InvalidXLogRecPtr
;
424 * Get the transaction status.
426 (void) TransactionIdGetStatus(xid
, &result
);