PostgreSQL Source Code  git master
transam.c
Go to the documentation of this file.
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  */
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
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))
71  if (TransactionIdEquals(transactionId, FrozenTransactionId))
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 &&
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  */
124 bool /* true if given transaction committed */
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  */
180 bool /* true if given transaction aborted */
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  */
237 bool
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
261 {
262  TransactionIdSetTreeStatus(xid, nxids, xids,
265 }
266 
267 /*
268  * TransactionIdAsyncCommitTree
269  * Same as above, but for async commits. The commit record LSN is needed.
270  */
271 void
273  XLogRecPtr lsn)
274 {
275  TransactionIdSetTreeStatus(xid, nxids, xids,
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
291 {
292  TransactionIdSetTreeStatus(xid, nxids, xids,
294 }
295 
296 /*
297  * TransactionIdPrecedes --- is id1 logically < id2?
298  */
299 bool
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
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
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
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  */
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  */
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  */
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 }
void TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:290
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:334
void TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, XLogRecPtr lsn)
Definition: transam.c:272
#define TRANSACTION_STATUS_COMMITTED
Definition: clog.h:27
uint32 TransactionId
Definition: c.h:508
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:109
TransactionId TransactionIdLatest(TransactionId mainxid, int nxids, const TransactionId *xids)
Definition: transam.c:365
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:349
int XidStatus
Definition: clog.h:24
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:125
bool TransactionIdIsKnownCompleted(TransactionId transactionId)
Definition: transam.c:238
signed int int32
Definition: c.h:347
TransactionId TransactionXmin
Definition: snapmgr.c:166
void TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:260
void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
Definition: clog.c:163
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:319
#define TRANSACTION_STATUS_ABORTED
Definition: clog.h:28
#define BootstrapTransactionId
Definition: transam.h:32
#define TRANSACTION_STATUS_SUB_COMMITTED
Definition: clog.h:29
#define InvalidTransactionId
Definition: transam.h:31
static XLogRecPtr cachedCommitLSN
Definition: transam.c:35
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:181
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define WARNING
Definition: elog.h:40
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition: transam.c:402
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define FrozenTransactionId
Definition: transam.h:33
XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
Definition: clog.c:641
#define elog(elevel,...)
Definition: elog.h:228
static XidStatus cachedFetchXidStatus
Definition: transam.c:34
static TransactionId cachedFetchXid
Definition: transam.c:33
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
static XidStatus TransactionLogFetch(TransactionId transactionId)
Definition: transam.c:52
#define TRANSACTION_STATUS_IN_PROGRESS
Definition: clog.h:26