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-2024, 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, as well as comments at the top of
114  * heapam_visibility.c that explain how everything fits together.
115  * ----------------------------------------------------------------
116  */
117 
118 /*
119  * TransactionIdDidCommit
120  * True iff transaction associated with the identifier did commit.
121  *
122  * Note:
123  * Assumes transaction identifier is valid and exists in clog.
124  */
125 bool /* true if given transaction committed */
127 {
128  XidStatus xidstatus;
129 
130  xidstatus = TransactionLogFetch(transactionId);
131 
132  /*
133  * If it's marked committed, it's committed.
134  */
135  if (xidstatus == TRANSACTION_STATUS_COMMITTED)
136  return true;
137 
138  /*
139  * If it's marked subcommitted, we have to check the parent recursively.
140  * However, if it's older than TransactionXmin, we can't look at
141  * pg_subtrans; instead assume that the parent crashed without cleaning up
142  * its children.
143  *
144  * Originally we Assert'ed that the result of SubTransGetParent was not
145  * zero. However with the introduction of prepared transactions, there can
146  * be a window just after database startup where we do not have complete
147  * knowledge in pg_subtrans of the transactions after TransactionXmin.
148  * StartupSUBTRANS() has ensured that any missing information will be
149  * zeroed. Since this case should not happen under normal conditions, it
150  * seems reasonable to emit a WARNING for it.
151  */
152  if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
153  {
154  TransactionId parentXid;
155 
156  if (TransactionIdPrecedes(transactionId, TransactionXmin))
157  return false;
158  parentXid = SubTransGetParent(transactionId);
159  if (!TransactionIdIsValid(parentXid))
160  {
161  elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
162  transactionId);
163  return false;
164  }
165  return TransactionIdDidCommit(parentXid);
166  }
167 
168  /*
169  * It's not committed.
170  */
171  return false;
172 }
173 
174 /*
175  * TransactionIdDidAbort
176  * True iff transaction associated with the identifier did abort.
177  *
178  * Note:
179  * Assumes transaction identifier is valid and exists in clog.
180  *
181  * Returns true only for explicitly aborted transactions, as transactions
182  * implicitly aborted due to a crash will commonly still appear to be
183  * in-progress in the clog. Most of the time TransactionIdDidCommit(),
184  * with a preceding TransactionIdIsInProgress() check, should be used
185  * instead of TransactionIdDidAbort().
186  */
187 bool /* true if given transaction aborted */
189 {
190  XidStatus xidstatus;
191 
192  xidstatus = TransactionLogFetch(transactionId);
193 
194  /*
195  * If it's marked aborted, it's aborted.
196  */
197  if (xidstatus == TRANSACTION_STATUS_ABORTED)
198  return true;
199 
200  /*
201  * If it's marked subcommitted, we have to check the parent recursively.
202  * However, if it's older than TransactionXmin, we can't look at
203  * pg_subtrans; instead assume that the parent crashed without cleaning up
204  * its children.
205  */
206  if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
207  {
208  TransactionId parentXid;
209 
210  if (TransactionIdPrecedes(transactionId, TransactionXmin))
211  return true;
212  parentXid = SubTransGetParent(transactionId);
213  if (!TransactionIdIsValid(parentXid))
214  {
215  /* see notes in TransactionIdDidCommit */
216  elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
217  transactionId);
218  return true;
219  }
220  return TransactionIdDidAbort(parentXid);
221  }
222 
223  /*
224  * It's not aborted.
225  */
226  return false;
227 }
228 
229 /*
230  * TransactionIdCommitTree
231  * Marks the given transaction and children as committed
232  *
233  * "xid" is a toplevel transaction commit, and the xids array contains its
234  * committed subtransactions.
235  *
236  * This commit operation is not guaranteed to be atomic, but if not, subxids
237  * are correctly marked subcommit first.
238  */
239 void
241 {
242  TransactionIdSetTreeStatus(xid, nxids, xids,
245 }
246 
247 /*
248  * TransactionIdAsyncCommitTree
249  * Same as above, but for async commits. The commit record LSN is needed.
250  */
251 void
253  XLogRecPtr lsn)
254 {
255  TransactionIdSetTreeStatus(xid, nxids, xids,
257 }
258 
259 /*
260  * TransactionIdAbortTree
261  * Marks the given transaction and children as aborted.
262  *
263  * "xid" is a toplevel transaction commit, and the xids array contains its
264  * committed subtransactions.
265  *
266  * We don't need to worry about the non-atomic behavior, since any onlookers
267  * will consider all the xacts as not-yet-committed anyway.
268  */
269 void
271 {
272  TransactionIdSetTreeStatus(xid, nxids, xids,
274 }
275 
276 /*
277  * TransactionIdPrecedes --- is id1 logically < id2?
278  */
279 bool
281 {
282  /*
283  * If either ID is a permanent XID then we can just do unsigned
284  * comparison. If both are normal, do a modulo-2^32 comparison.
285  */
286  int32 diff;
287 
288  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
289  return (id1 < id2);
290 
291  diff = (int32) (id1 - id2);
292  return (diff < 0);
293 }
294 
295 /*
296  * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
297  */
298 bool
300 {
301  int32 diff;
302 
303  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
304  return (id1 <= id2);
305 
306  diff = (int32) (id1 - id2);
307  return (diff <= 0);
308 }
309 
310 /*
311  * TransactionIdFollows --- is id1 logically > id2?
312  */
313 bool
315 {
316  int32 diff;
317 
318  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
319  return (id1 > id2);
320 
321  diff = (int32) (id1 - id2);
322  return (diff > 0);
323 }
324 
325 /*
326  * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
327  */
328 bool
330 {
331  int32 diff;
332 
333  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
334  return (id1 >= id2);
335 
336  diff = (int32) (id1 - id2);
337  return (diff >= 0);
338 }
339 
340 
341 /*
342  * TransactionIdLatest --- get latest XID among a main xact and its children
343  */
346  int nxids, const TransactionId *xids)
347 {
348  TransactionId result;
349 
350  /*
351  * In practice it is highly likely that the xids[] array is sorted, and so
352  * we could save some cycles by just taking the last child XID, but this
353  * probably isn't so performance-critical that it's worth depending on
354  * that assumption. But just to show we're not totally stupid, scan the
355  * array back-to-front to avoid useless assignments.
356  */
357  result = mainxid;
358  while (--nxids >= 0)
359  {
360  if (TransactionIdPrecedes(result, xids[nxids]))
361  result = xids[nxids];
362  }
363  return result;
364 }
365 
366 
367 /*
368  * TransactionIdGetCommitLSN
369  *
370  * This function returns an LSN that is late enough to be able
371  * to guarantee that if we flush up to the LSN returned then we
372  * will have flushed the transaction's commit record to disk.
373  *
374  * The result is not necessarily the exact LSN of the transaction's
375  * commit record! For example, for long-past transactions (those whose
376  * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
377  * Also, because we group transactions on the same clog page to conserve
378  * storage, we might return the LSN of a later transaction that falls into
379  * the same group.
380  */
383 {
384  XLogRecPtr result;
385 
386  /*
387  * Currently, all uses of this function are for xids that were just
388  * reported to be committed by TransactionLogFetch, so we expect that
389  * checking TransactionLogFetch's cache will usually succeed and avoid an
390  * extra trip to shared memory.
391  */
393  return cachedCommitLSN;
394 
395  /* Special XIDs are always known committed */
396  if (!TransactionIdIsNormal(xid))
397  return InvalidXLogRecPtr;
398 
399  /*
400  * Get the transaction status.
401  */
402  (void) TransactionIdGetStatus(xid, &result);
403 
404  return result;
405 }
signed int int32
Definition: c.h:494
uint32 TransactionId
Definition: c.h:652
XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
Definition: clog.c:735
void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids, TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
Definition: clog.c:183
#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:36
#define elog(elevel,...)
Definition: elog.h:224
TransactionId TransactionXmin
Definition: snapmgr.c:98
TransactionId SubTransGetParent(TransactionId xid)
Definition: subtrans.c:122
void TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, XLogRecPtr lsn)
Definition: transam.c:252
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:345
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126
void TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:240
void TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
Definition: transam.c:270
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:299
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:188
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:314
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition: transam.c:382
static XLogRecPtr cachedCommitLSN
Definition: transam.c:35
static XidStatus cachedFetchXidStatus
Definition: transam.c:34
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:329
#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