Query in SQL function still not schema-safe; add a couple
[PostgreSQL.git] / src / backend / access / transam / transam.c
blob2a1eab4d16c0a9b3a899340aa456cf80e9a9531b
1 /*-------------------------------------------------------------------------
3 * transam.c
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
10 * IDENTIFICATION
11 * $PostgreSQL$
13 * NOTES
14 * This file contains the high level access-method interface to the
15 * transaction system.
17 *-------------------------------------------------------------------------
20 #include "postgres.h"
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,
32 * update, or delete.
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};
41 /* Local functions */
42 static XidStatus TransactionLogFetch(TransactionId transactionId);
45 /* ----------------------------------------------------------------
46 * Postgres log access method interface
48 * TransactionLogFetch
49 * ----------------------------------------------------------------
53 * TransactionLogFetch --- fetch commit status of specified transaction id
55 static XidStatus
56 TransactionLogFetch(TransactionId transactionId)
58 XidStatus xidstatus;
59 XLogRecPtr xidlsn;
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;
97 return xidstatus;
100 /* ----------------------------------------------------------------
101 * Interface functions
103 * TransactionIdDidCommit
104 * TransactionIdDidAbort
105 * ========
106 * these functions test the transaction status of
107 * a specified transaction id.
109 * TransactionIdCommitTree
110 * TransactionIdAsyncCommitTree
111 * TransactionIdAbortTree
112 * ========
113 * these functions set the transaction status of the specified
114 * transaction tree.
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.
125 * Note:
126 * Assumes transaction identifier is valid.
128 bool /* true if given transaction committed */
129 TransactionIdDidCommit(TransactionId transactionId)
131 XidStatus xidstatus;
133 xidstatus = TransactionLogFetch(transactionId);
136 * If it's marked committed, it's committed.
138 if (xidstatus == TRANSACTION_STATUS_COMMITTED)
139 return true;
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
145 * its children.
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))
160 return false;
161 parentXid = SubTransGetParent(transactionId);
162 if (!TransactionIdIsValid(parentXid))
164 elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
165 transactionId);
166 return false;
168 return TransactionIdDidCommit(parentXid);
172 * It's not committed.
174 return false;
178 * TransactionIdDidAbort
179 * True iff transaction associated with the identifier did abort.
181 * Note:
182 * Assumes transaction identifier is valid.
184 bool /* true if given transaction aborted */
185 TransactionIdDidAbort(TransactionId transactionId)
187 XidStatus xidstatus;
189 xidstatus = TransactionLogFetch(transactionId);
192 * If it's marked aborted, it's aborted.
194 if (xidstatus == TRANSACTION_STATUS_ABORTED)
195 return true;
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
201 * its children.
203 if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
205 TransactionId parentXid;
207 if (TransactionIdPrecedes(transactionId, TransactionXmin))
208 return true;
209 parentXid = SubTransGetParent(transactionId);
210 if (!TransactionIdIsValid(parentXid))
212 /* see notes in TransactionIdDidCommit */
213 elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
214 transactionId);
215 return true;
217 return TransactionIdDidAbort(parentXid);
221 * It's not aborted.
223 return false;
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.
238 * Note:
239 * Assumes transaction identifier is valid.
241 bool
242 TransactionIdIsKnownCompleted(TransactionId transactionId)
244 if (TransactionIdEquals(transactionId, cachedFetchXid))
246 /* If it's in the cache at all, it must be completed. */
247 return true;
250 return false;
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.
263 void
264 TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
266 TransactionIdSetTreeStatus(xid, nxids, xids,
267 TRANSACTION_STATUS_COMMITTED,
268 InvalidXLogRecPtr);
272 * TransactionIdAsyncCommitTree
273 * Same as above, but for async commits. The commit record LSN is needed.
275 void
276 TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
277 XLogRecPtr lsn)
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.
293 void
294 TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
296 TransactionIdSetTreeStatus(xid, nxids, xids,
297 TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
301 * TransactionIdPrecedes --- is id1 logically < id2?
303 bool
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.
310 int32 diff;
312 if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
313 return (id1 < id2);
315 diff = (int32) (id1 - id2);
316 return (diff < 0);
320 * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
322 bool
323 TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
325 int32 diff;
327 if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
328 return (id1 <= id2);
330 diff = (int32) (id1 - id2);
331 return (diff <= 0);
335 * TransactionIdFollows --- is id1 logically > id2?
337 bool
338 TransactionIdFollows(TransactionId id1, TransactionId id2)
340 int32 diff;
342 if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
343 return (id1 > id2);
345 diff = (int32) (id1 - id2);
346 return (diff > 0);
350 * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
352 bool
353 TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
355 int32 diff;
357 if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
358 return (id1 >= id2);
360 diff = (int32) (id1 - id2);
361 return (diff >= 0);
366 * TransactionIdLatest --- get latest XID among a main xact and its children
368 TransactionId
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.
381 result = mainxid;
382 while (--nxids >= 0)
384 if (TransactionIdPrecedes(result, xids[nxids]))
385 result = xids[nxids];
387 return result;
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
403 * the same group.
405 XLogRecPtr
406 TransactionIdGetCommitLSN(TransactionId xid)
408 XLogRecPtr result;
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);
428 return result;