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-2022, 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  * TransactionIdCommitTree
224  * Marks the given transaction and children as committed
225  *
226  * "xid" is a toplevel transaction commit, and the xids array contains its
227  * committed subtransactions.
228  *
229  * This commit operation is not guaranteed to be atomic, but if not, subxids
230  * are correctly marked subcommit first.
231  */
232 void
234 {
235  TransactionIdSetTreeStatus(xid, nxids, xids,
238 }
239 
240 /*
241  * TransactionIdAsyncCommitTree
242  * Same as above, but for async commits. The commit record LSN is needed.
243  */
244 void
246  XLogRecPtr lsn)
247 {
248  TransactionIdSetTreeStatus(xid, nxids, xids,
250 }
251 
252 /*
253  * TransactionIdAbortTree
254  * Marks the given transaction and children as aborted.
255  *
256  * "xid" is a toplevel transaction commit, and the xids array contains its
257  * committed subtransactions.
258  *
259  * We don't need to worry about the non-atomic behavior, since any onlookers
260  * will consider all the xacts as not-yet-committed anyway.
261  */
262 void
264 {
265  TransactionIdSetTreeStatus(xid, nxids, xids,
267 }
268 
269 /*
270  * TransactionIdPrecedes --- is id1 logically < id2?
271  */
272 bool
274 {
275  /*
276  * If either ID is a permanent XID then we can just do unsigned
277  * comparison. If both are normal, do a modulo-2^32 comparison.
278  */
279  int32 diff;
280 
281  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
282  return (id1 < id2);
283 
284  diff = (int32) (id1 - id2);
285  return (diff < 0);
286 }
287 
288 /*
289  * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
290  */
291 bool
293 {
294  int32 diff;
295 
296  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
297  return (id1 <= id2);
298 
299  diff = (int32) (id1 - id2);
300  return (diff <= 0);
301 }
302 
303 /*
304  * TransactionIdFollows --- is id1 logically > id2?
305  */
306 bool
308 {
309  int32 diff;
310 
311  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
312  return (id1 > id2);
313 
314  diff = (int32) (id1 - id2);
315  return (diff > 0);
316 }
317 
318 /*
319  * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
320  */
321 bool
323 {
324  int32 diff;
325 
326  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
327  return (id1 >= id2);
328 
329  diff = (int32) (id1 - id2);
330  return (diff >= 0);
331 }
332 
333 
334 /*
335  * TransactionIdLatest --- get latest XID among a main xact and its children
336  */
339  int nxids, const TransactionId *xids)
340 {
341  TransactionId result;
342 
343  /*
344  * In practice it is highly likely that the xids[] array is sorted, and so
345  * we could save some cycles by just taking the last child XID, but this
346  * probably isn't so performance-critical that it's worth depending on
347  * that assumption. But just to show we're not totally stupid, scan the
348  * array back-to-front to avoid useless assignments.
349  */
350  result = mainxid;
351  while (--nxids >= 0)
352  {
353  if (TransactionIdPrecedes(result, xids[nxids]))
354  result = xids[nxids];
355  }
356  return result;
357 }
358 
359 
360 /*
361  * TransactionIdGetCommitLSN
362  *
363  * This function returns an LSN that is late enough to be able
364  * to guarantee that if we flush up to the LSN returned then we
365  * will have flushed the transaction's commit record to disk.
366  *
367  * The result is not necessarily the exact LSN of the transaction's
368  * commit record! For example, for long-past transactions (those whose
369  * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
370  * Also, because we group transactions on the same clog page to conserve
371  * storage, we might return the LSN of a later transaction that falls into
372  * the same group.
373  */
376 {
377  XLogRecPtr result;
378 
379  /*
380  * Currently, all uses of this function are for xids that were just
381  * reported to be committed by TransactionLogFetch, so we expect that
382  * checking TransactionLogFetch's cache will usually succeed and avoid an
383  * extra trip to shared memory.
384  */
386  return cachedCommitLSN;
387 
388  /* Special XIDs are always known committed */
389  if (!TransactionIdIsNormal(xid))
390  return InvalidXLogRecPtr;
391 
392  /*
393  * Get the transaction status.
394  */
395  (void) TransactionIdGetStatus(xid, &result);
396 
397  return result;
398 }
signed int int32
Definition: c.h:440
uint32 TransactionId
Definition: c.h:598
XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
Definition: clog.c:640
void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
Definition: clog.c:164
#define TRANSACTION_STATUS_IN_PROGRESS
Definition: clog.h:27
int XidStatus
Definition: clog.h:25
#define TRANSACTION_STATUS_ABORTED
Definition: clog.h:29
#define TRANSACTION_STATUS_SUB_COMMITTED
Definition: clog.h:30
#define TRANSACTION_STATUS_COMMITTED
Definition: clog.h:28
#define WARNING
Definition: elog.h:30
#define elog(elevel,...)
Definition: elog.h:218
TransactionId TransactionXmin
Definition: snapmgr.c:112
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:109
void TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, XLogRecPtr lsn)
Definition: transam.c:245
static TransactionId cachedFetchXid
Definition: transam.c:33
static XidStatus TransactionLogFetch(TransactionId transactionId)
Definition: transam.c:52
TransactionId TransactionIdLatest(TransactionId mainxid, int nxids, const TransactionId *xids)
Definition: transam.c:338
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:125
void TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:233
void TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:263
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:273
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:292
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:181
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:307
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition: transam.c:375
static XLogRecPtr cachedCommitLSN
Definition: transam.c:35
static XidStatus cachedFetchXidStatus
Definition: transam.c:34
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:322
#define FrozenTransactionId
Definition: transam.h:33
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43
#define BootstrapTransactionId
Definition: transam.h:32
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28