1 : /*-------------------------------------------------------------------------
2 : *
3 : * transam.c
4 : * postgres transaction log interface routines
5 : *
6 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.72 2007/11/15 21:14:32 momjian Exp $
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/tqual.h"
26 :
27 :
28 : static XidStatus TransactionLogFetch(TransactionId transactionId);
29 : static void TransactionLogUpdate(TransactionId transactionId,
30 : XidStatus status, XLogRecPtr lsn);
31 :
32 : /*
33 : * Single-item cache for results of TransactionLogFetch.
34 : */
35 : static TransactionId cachedFetchXid = InvalidTransactionId;
36 : static XidStatus cachedFetchXidStatus;
37 : static XLogRecPtr cachedCommitLSN;
38 :
39 : /* Handy constant for an invalid xlog recptr */
40 : static const XLogRecPtr InvalidXLogRecPtr = {0, 0};
41 :
42 :
43 : /* ----------------------------------------------------------------
44 : * postgres log access method interface
45 : *
46 : * TransactionLogFetch
47 : * TransactionLogUpdate
48 : * ----------------------------------------------------------------
49 : */
50 :
51 : /*
52 : * TransactionLogFetch --- fetch commit status of specified transaction id
53 : */
54 : static XidStatus
55 : TransactionLogFetch(TransactionId transactionId)
56 227611 : {
57 : XidStatus xidstatus;
58 : XLogRecPtr xidlsn;
59 :
60 : /*
61 : * Before going to the commit log manager, check our single item cache to
62 : * see if we didn't just check the transaction status a moment ago.
63 : */
64 227611 : if (TransactionIdEquals(transactionId, cachedFetchXid))
65 212195 : return cachedFetchXidStatus;
66 :
67 : /*
68 : * Also, check to see if the transaction ID is a permanent one.
69 : */
70 15416 : if (!TransactionIdIsNormal(transactionId))
71 : {
72 5110 : if (TransactionIdEquals(transactionId, BootstrapTransactionId))
73 5110 : return TRANSACTION_STATUS_COMMITTED;
74 0 : if (TransactionIdEquals(transactionId, FrozenTransactionId))
75 0 : return TRANSACTION_STATUS_COMMITTED;
76 0 : return TRANSACTION_STATUS_ABORTED;
77 : }
78 :
79 : /*
80 : * Get the transaction status.
81 : */
82 10306 : xidstatus = TransactionIdGetStatus(transactionId, &xidlsn);
83 :
84 : /*
85 : * DO NOT cache status for unfinished or sub-committed transactions! We
86 : * only cache status that is guaranteed not to change.
87 : */
88 10306 : if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
89 : xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
90 : {
91 10115 : cachedFetchXid = transactionId;
92 10115 : cachedFetchXidStatus = xidstatus;
93 10115 : cachedCommitLSN = xidlsn;
94 : }
95 :
96 10306 : return xidstatus;
97 : }
98 :
99 : /* --------------------------------
100 : * TransactionLogUpdate
101 : *
102 : * Store the new status of a transaction. The commit record LSN must be
103 : * passed when recording an async commit; else it should be InvalidXLogRecPtr.
104 : * --------------------------------
105 : */
106 : static inline void
107 : TransactionLogUpdate(TransactionId transactionId,
108 : XidStatus status, XLogRecPtr lsn)
109 8679 : {
110 : /*
111 : * update the commit log
112 : */
113 8679 : TransactionIdSetStatus(transactionId, status, lsn);
114 8679 : }
115 :
116 : /*
117 : * TransactionLogMultiUpdate
118 : *
119 : * Update multiple transaction identifiers to a given status.
120 : * Don't depend on this being atomic; it's not.
121 : */
122 : static inline void
123 : TransactionLogMultiUpdate(int nxids, TransactionId *xids,
124 : XidStatus status, XLogRecPtr lsn)
125 14 : {
126 : int i;
127 :
128 : Assert(nxids != 0);
129 :
130 33 : for (i = 0; i < nxids; i++)
131 19 : TransactionIdSetStatus(xids[i], status, lsn);
132 14 : }
133 :
134 : /* ----------------------------------------------------------------
135 : * Interface functions
136 : *
137 : * TransactionId DidCommit
138 : * TransactionId DidAbort
139 : * TransactionId IsInProgress
140 : * ========
141 : * these functions test the transaction status of
142 : * a specified transaction id.
143 : *
144 : * TransactionId Commit
145 : * TransactionId Abort
146 : * ========
147 : * these functions set the transaction status
148 : * of the specified xid.
149 : *
150 : * ----------------------------------------------------------------
151 : */
152 :
153 : /* --------------------------------
154 : * TransactionId DidCommit
155 : * TransactionId DidAbort
156 : * TransactionId IsInProgress
157 : * --------------------------------
158 : */
159 :
160 : /*
161 : * TransactionIdDidCommit
162 : * True iff transaction associated with the identifier did commit.
163 : *
164 : * Note:
165 : * Assumes transaction identifier is valid.
166 : */
167 : bool /* true if given transaction committed */
168 : TransactionIdDidCommit(TransactionId transactionId)
169 227611 : {
170 : XidStatus xidstatus;
171 :
172 227611 : xidstatus = TransactionLogFetch(transactionId);
173 :
174 : /*
175 : * If it's marked committed, it's committed.
176 : */
177 227611 : if (xidstatus == TRANSACTION_STATUS_COMMITTED)
178 226220 : return true;
179 :
180 : /*
181 : * If it's marked subcommitted, we have to check the parent recursively.
182 : * However, if it's older than TransactionXmin, we can't look at
183 : * pg_subtrans; instead assume that the parent crashed without cleaning up
184 : * its children.
185 : *
186 : * Originally we Assert'ed that the result of SubTransGetParent was not
187 : * zero. However with the introduction of prepared transactions, there can
188 : * be a window just after database startup where we do not have complete
189 : * knowledge in pg_subtrans of the transactions after TransactionXmin.
190 : * StartupSUBTRANS() has ensured that any missing information will be
191 : * zeroed. Since this case should not happen under normal conditions, it
192 : * seems reasonable to emit a WARNING for it.
193 : */
194 1391 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
195 : {
196 : TransactionId parentXid;
197 :
198 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
199 0 : return false;
200 0 : parentXid = SubTransGetParent(transactionId);
201 0 : if (!TransactionIdIsValid(parentXid))
202 : {
203 0 : elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
204 : transactionId);
205 0 : return false;
206 : }
207 0 : return TransactionIdDidCommit(parentXid);
208 : }
209 :
210 : /*
211 : * It's not committed.
212 : */
213 1391 : return false;
214 : }
215 :
216 : /*
217 : * TransactionIdDidAbort
218 : * True iff transaction associated with the identifier did abort.
219 : *
220 : * Note:
221 : * Assumes transaction identifier is valid.
222 : */
223 : bool /* true if given transaction aborted */
224 : TransactionIdDidAbort(TransactionId transactionId)
225 0 : {
226 : XidStatus xidstatus;
227 :
228 0 : xidstatus = TransactionLogFetch(transactionId);
229 :
230 : /*
231 : * If it's marked aborted, it's aborted.
232 : */
233 0 : if (xidstatus == TRANSACTION_STATUS_ABORTED)
234 0 : return true;
235 :
236 : /*
237 : * If it's marked subcommitted, we have to check the parent recursively.
238 : * However, if it's older than TransactionXmin, we can't look at
239 : * pg_subtrans; instead assume that the parent crashed without cleaning up
240 : * its children.
241 : */
242 0 : if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
243 : {
244 : TransactionId parentXid;
245 :
246 0 : if (TransactionIdPrecedes(transactionId, TransactionXmin))
247 0 : return true;
248 0 : parentXid = SubTransGetParent(transactionId);
249 0 : if (!TransactionIdIsValid(parentXid))
250 : {
251 : /* see notes in TransactionIdDidCommit */
252 0 : elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
253 : transactionId);
254 0 : return true;
255 : }
256 0 : return TransactionIdDidAbort(parentXid);
257 : }
258 :
259 : /*
260 : * It's not aborted.
261 : */
262 0 : return false;
263 : }
264 :
265 : /* --------------------------------
266 : * TransactionId Commit
267 : * TransactionId Abort
268 : * --------------------------------
269 : */
270 :
271 : /*
272 : * TransactionIdCommit
273 : * Commits the transaction associated with the identifier.
274 : *
275 : * Note:
276 : * Assumes transaction identifier is valid.
277 : */
278 : void
279 : TransactionIdCommit(TransactionId transactionId)
280 8469 : {
281 8469 : TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED,
282 : InvalidXLogRecPtr);
283 8469 : }
284 :
285 : /*
286 : * TransactionIdAsyncCommit
287 : * Same as above, but for async commits. The commit record LSN is needed.
288 : */
289 : void
290 : TransactionIdAsyncCommit(TransactionId transactionId, XLogRecPtr lsn)
291 0 : {
292 0 : TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED, lsn);
293 0 : }
294 :
295 :
296 : /*
297 : * TransactionIdAbort
298 : * Aborts the transaction associated with the identifier.
299 : *
300 : * Note:
301 : * Assumes transaction identifier is valid.
302 : * No async version of this is needed.
303 : */
304 : void
305 : TransactionIdAbort(TransactionId transactionId)
306 191 : {
307 191 : TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED,
308 : InvalidXLogRecPtr);
309 191 : }
310 :
311 : /*
312 : * TransactionIdSubCommit
313 : * Marks the subtransaction associated with the identifier as
314 : * sub-committed.
315 : *
316 : * Note:
317 : * No async version of this is needed.
318 : */
319 : void
320 : TransactionIdSubCommit(TransactionId transactionId)
321 19 : {
322 19 : TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED,
323 : InvalidXLogRecPtr);
324 19 : }
325 :
326 : /*
327 : * TransactionIdCommitTree
328 : * Marks all the given transaction ids as committed.
329 : *
330 : * The caller has to be sure that this is used only to mark subcommitted
331 : * subtransactions as committed, and only *after* marking the toplevel
332 : * parent as committed. Otherwise there is a race condition against
333 : * TransactionIdDidCommit.
334 : */
335 : void
336 : TransactionIdCommitTree(int nxids, TransactionId *xids)
337 8469 : {
338 8469 : if (nxids > 0)
339 9 : TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
340 : InvalidXLogRecPtr);
341 8469 : }
342 :
343 : /*
344 : * TransactionIdAsyncCommitTree
345 : * Same as above, but for async commits. The commit record LSN is needed.
346 : */
347 : void
348 : TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn)
349 0 : {
350 0 : if (nxids > 0)
351 0 : TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
352 : lsn);
353 0 : }
354 :
355 :
356 : /*
357 : * TransactionIdAbortTree
358 : * Marks all the given transaction ids as aborted.
359 : *
360 : * We don't need to worry about the non-atomic behavior, since any onlookers
361 : * will consider all the xacts as not-yet-committed anyway.
362 : */
363 : void
364 : TransactionIdAbortTree(int nxids, TransactionId *xids)
365 191 : {
366 191 : if (nxids > 0)
367 5 : TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_ABORTED,
368 : InvalidXLogRecPtr);
369 191 : }
370 :
371 : /*
372 : * TransactionIdPrecedes --- is id1 logically < id2?
373 : */
374 : bool
375 : TransactionIdPrecedes(TransactionId id1, TransactionId id2)
376 1557197 : {
377 : /*
378 : * If either ID is a permanent XID then we can just do unsigned
379 : * comparison. If both are normal, do a modulo-2^31 comparison.
380 : */
381 : int32 diff;
382 :
383 1557197 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
384 491940 : return (id1 < id2);
385 :
386 1065257 : diff = (int32) (id1 - id2);
387 1065257 : return (diff < 0);
388 : }
389 :
390 : /*
391 : * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
392 : */
393 : bool
394 : TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
395 0 : {
396 : int32 diff;
397 :
398 0 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
399 0 : return (id1 <= id2);
400 :
401 0 : diff = (int32) (id1 - id2);
402 0 : return (diff <= 0);
403 : }
404 :
405 : /*
406 : * TransactionIdFollows --- is id1 logically > id2?
407 : */
408 : bool
409 : TransactionIdFollows(TransactionId id1, TransactionId id2)
410 6 : {
411 : int32 diff;
412 :
413 6 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
414 0 : return (id1 > id2);
415 :
416 6 : diff = (int32) (id1 - id2);
417 6 : return (diff > 0);
418 : }
419 :
420 : /*
421 : * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
422 : */
423 : bool
424 : TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
425 32206 : {
426 : int32 diff;
427 :
428 32206 : if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
429 6 : return (id1 >= id2);
430 :
431 32200 : diff = (int32) (id1 - id2);
432 32200 : return (diff >= 0);
433 : }
434 :
435 :
436 : /*
437 : * TransactionIdLatest --- get latest XID among a main xact and its children
438 : */
439 : TransactionId
440 : TransactionIdLatest(TransactionId mainxid,
441 : int nxids, const TransactionId *xids)
442 8865 : {
443 : TransactionId result;
444 :
445 : /*
446 : * In practice it is highly likely that the xids[] array is sorted, and so
447 : * we could save some cycles by just taking the last child XID, but this
448 : * probably isn't so performance-critical that it's worth depending on
449 : * that assumption. But just to show we're not totally stupid, scan the
450 : * array back-to-front to avoid useless assignments.
451 : */
452 8865 : result = mainxid;
453 17749 : while (--nxids >= 0)
454 : {
455 19 : if (TransactionIdPrecedes(result, xids[nxids]))
456 14 : result = xids[nxids];
457 : }
458 8865 : return result;
459 : }
460 :
461 :
462 : /*
463 : * TransactionIdGetCommitLSN
464 : *
465 : * This function returns an LSN that is late enough to be able
466 : * to guarantee that if we flush up to the LSN returned then we
467 : * will have flushed the transaction's commit record to disk.
468 : *
469 : * The result is not necessarily the exact LSN of the transaction's
470 : * commit record! For example, for long-past transactions (those whose
471 : * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
472 : * Also, because we group transactions on the same clog page to conserve
473 : * storage, we might return the LSN of a later transaction that falls into
474 : * the same group.
475 : */
476 : XLogRecPtr
477 : TransactionIdGetCommitLSN(TransactionId xid)
478 226176 : {
479 : XLogRecPtr result;
480 :
481 : /*
482 : * Currently, all uses of this function are for xids that were just
483 : * reported to be committed by TransactionLogFetch, so we expect that
484 : * checking TransactionLogFetch's cache will usually succeed and avoid an
485 : * extra trip to shared memory.
486 : */
487 226176 : if (TransactionIdEquals(xid, cachedFetchXid))
488 221066 : return cachedCommitLSN;
489 :
490 : /* Special XIDs are always known committed */
491 5110 : if (!TransactionIdIsNormal(xid))
492 5110 : return InvalidXLogRecPtr;
493 :
494 : /*
495 : * Get the transaction status.
496 : */
497 0 : (void) TransactionIdGetStatus(xid, &result);
498 :
499 0 : return result;
500 : }
|