]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/transam.c
Reduce pinning and buffer content locking for btree scans.
[postgresql] / src / backend / access / transam / transam.c
1 /*-------------------------------------------------------------------------
2  *
3  * transam.c
4  *        postgres transaction log interface routines
5  *
6  * Portions Copyright (c) 1996-2015, 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 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         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.
123  */
124 bool                                                    /* true if given transaction committed */
125 TransactionIdDidCommit(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.
179  */
180 bool                                                    /* true if given transaction aborted */
181 TransactionIdDidAbort(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_clog 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 TransactionIdIsKnownCompleted(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  */
259 void
260 TransactionIdCommitTree(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  */
271 void
272 TransactionIdAsyncCommitTree(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  */
289 void
290 TransactionIdAbortTree(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  */
299 bool
300 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         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  */
318 bool
319 TransactionIdPrecedesOrEquals(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  */
333 bool
334 TransactionIdFollows(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  */
348 bool
349 TransactionIdFollowsOrEquals(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  */
364 TransactionId
365 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         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  */
401 XLogRecPtr
402 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         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 }