1 /*-------------------------------------------------------------------------
4 * POSTGRES "time qualification" code, ie, tuple visibility rules.
6 * NOTE: all the HeapTupleSatisfies routines will update the tuple's
7 * "hint" status bits if we see that the inserting or deleting transaction
8 * has now committed or aborted (and it is safe to set the hint bits).
9 * If the hint bits are changed, SetBufferCommitInfoNeedsSave is called on
10 * the passed-in buffer. The caller must hold not only a pin, but at least
11 * shared buffer content lock on the buffer containing the tuple.
13 * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array)
14 * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
15 * pg_clog). Otherwise we have a race condition: we might decide that a
16 * just-committed transaction crashed, because none of the tests succeed.
17 * xact.c is careful to record commit/abort in pg_clog before it unsets
18 * MyProc->xid in PGPROC array. That fixes that problem, but it also
19 * means there is a window where TransactionIdIsInProgress and
20 * TransactionIdDidCommit will both return true. If we check only
21 * TransactionIdDidCommit, we could consider a tuple committed when a
22 * later GetSnapshotData call will still think the originating transaction
23 * is in progress, which leads to application-level inconsistency. The
24 * upshot is that we gotta check TransactionIdIsInProgress first in all
25 * code paths, except for a few cases where we are looking at
26 * subtransactions of our own main transaction and so there can't be any
30 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
31 * Portions Copyright (c) 1994, Regents of the University of California
36 *-------------------------------------------------------------------------
41 #include "access/multixact.h"
42 #include "access/subtrans.h"
43 #include "access/transam.h"
44 #include "access/xact.h"
45 #include "storage/bufmgr.h"
46 #include "storage/procarray.h"
47 #include "utils/tqual.h"
50 /* Static variables representing various special snapshot semantics */
51 SnapshotData SnapshotNowData
= {HeapTupleSatisfiesNow
};
52 SnapshotData SnapshotSelfData
= {HeapTupleSatisfiesSelf
};
53 SnapshotData SnapshotAnyData
= {HeapTupleSatisfiesAny
};
54 SnapshotData SnapshotToastData
= {HeapTupleSatisfiesToast
};
57 static bool XidInMVCCSnapshot(TransactionId xid
, Snapshot snapshot
);
63 * Set commit/abort hint bits on a tuple, if appropriate at this time.
65 * It is only safe to set a transaction-committed hint bit if we know the
66 * transaction's commit record has been flushed to disk. We cannot change
67 * the LSN of the page here because we may hold only a share lock on the
68 * buffer, so we can't use the LSN to interlock this; we have to just refrain
69 * from setting the hint bit until some future re-examination of the tuple.
71 * We can always set hint bits when marking a transaction aborted. (Some
72 * code in heapam.c relies on that!)
74 * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
75 * we can always set the hint bits, since VACUUM FULL always uses synchronous
76 * commits and doesn't move tuples that weren't previously hinted. (This is
77 * not known by this subroutine, but is applied by its callers.)
79 * Normal commits may be asynchronous, so for those we need to get the LSN
80 * of the transaction and then check whether this is flushed.
82 * The caller should pass xid as the XID of the transaction to check, or
83 * InvalidTransactionId if no check is needed.
86 SetHintBits(HeapTupleHeader tuple
, Buffer buffer
,
87 uint16 infomask
, TransactionId xid
)
89 if (TransactionIdIsValid(xid
))
91 /* NB: xid must be known committed here! */
92 XLogRecPtr commitLSN
= TransactionIdGetCommitLSN(xid
);
94 if (XLogNeedsFlush(commitLSN
))
95 return; /* not flushed yet, so don't set hint */
98 tuple
->t_infomask
|= infomask
;
99 SetBufferCommitInfoNeedsSave(buffer
);
103 * HeapTupleSetHintBits --- exported version of SetHintBits()
105 * This must be separate because of C99's brain-dead notions about how to
106 * implement inline functions.
109 HeapTupleSetHintBits(HeapTupleHeader tuple
, Buffer buffer
,
110 uint16 infomask
, TransactionId xid
)
112 SetHintBits(tuple
, buffer
, infomask
, xid
);
117 * HeapTupleSatisfiesSelf
118 * True iff heap tuple is valid "for itself".
120 * Here, we consider the effects of:
121 * all committed transactions (as of the current instant)
122 * previous commands of this transaction
123 * changes made by the current command
126 * Assumes heap tuple is valid.
128 * The satisfaction of "itself" requires the following:
130 * ((Xmin == my-transaction && the row was updated by the current transaction, and
131 * (Xmax is null it was not deleted
132 * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
135 * (Xmin is committed && the row was modified by a committed transaction, and
136 * (Xmax is null || the row has not been deleted, or
137 * (Xmax != my-transaction && the row was deleted by another transaction
138 * Xmax is not committed))) that has not been committed
141 HeapTupleSatisfiesSelf(HeapTupleHeader tuple
, Snapshot snapshot
, Buffer buffer
)
143 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
145 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
148 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
150 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
152 if (TransactionIdIsCurrentTransactionId(xvac
))
154 if (!TransactionIdIsInProgress(xvac
))
156 if (TransactionIdDidCommit(xvac
))
158 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
159 InvalidTransactionId
);
162 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
163 InvalidTransactionId
);
166 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
168 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
170 if (!TransactionIdIsCurrentTransactionId(xvac
))
172 if (TransactionIdIsInProgress(xvac
))
174 if (TransactionIdDidCommit(xvac
))
175 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
176 InvalidTransactionId
);
179 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
180 InvalidTransactionId
);
185 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple
)))
187 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
190 if (tuple
->t_infomask
& HEAP_IS_LOCKED
) /* not deleter */
193 Assert(!(tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
));
195 if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
197 /* deleting subtransaction must have aborted */
198 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
199 InvalidTransactionId
);
205 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
207 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
208 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
209 HeapTupleHeaderGetXmin(tuple
));
212 /* it must have aborted or crashed */
213 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
214 InvalidTransactionId
);
219 /* by here, the inserting transaction has committed */
221 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid or aborted */
224 if (tuple
->t_infomask
& HEAP_XMAX_COMMITTED
)
226 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
228 return false; /* updated by other */
231 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
233 /* MultiXacts are currently only allowed to lock tuples */
234 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
238 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
240 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
245 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
248 if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
250 /* it must have aborted or crashed */
251 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
252 InvalidTransactionId
);
256 /* xmax transaction committed */
258 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
260 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
261 InvalidTransactionId
);
265 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
266 HeapTupleHeaderGetXmax(tuple
));
271 * HeapTupleSatisfiesNow
272 * True iff heap tuple is valid "now".
274 * Here, we consider the effects of:
275 * all committed transactions (as of the current instant)
276 * previous commands of this transaction
278 * Note we do _not_ include changes made by the current command. This
279 * solves the "Halloween problem" wherein an UPDATE might try to re-update
280 * its own output tuples.
283 * Assumes heap tuple is valid.
285 * The satisfaction of "now" requires the following:
287 * ((Xmin == my-transaction && inserted by the current transaction
288 * Cmin < my-command && before this command, and
289 * (Xmax is null || the row has not been deleted, or
290 * (Xmax == my-transaction && it was deleted by the current transaction
291 * Cmax >= my-command))) but not before this command,
293 * (Xmin is committed && the row was inserted by a committed transaction, and
294 * (Xmax is null || the row has not been deleted, or
295 * (Xmax == my-transaction && the row is being deleted by this transaction
296 * Cmax >= my-command) || but it's not deleted "yet", or
297 * (Xmax != my-transaction && the row was deleted by another transaction
298 * Xmax is not committed)))) that has not been committed
300 * mao says 17 march 1993: the tests in this routine are correct;
301 * if you think they're not, you're wrong, and you should think
302 * about it again. i know, it happened to me. we don't need to
303 * check commit time against the start time of this transaction
304 * because 2ph locking protects us from doing the wrong thing.
305 * if you mess around here, you'll break serializability. the only
306 * problem with this code is that it does the wrong thing for system
307 * catalog updates, because the catalogs aren't subject to 2ph, so
308 * the serializability guarantees we provide don't extend to xacts
309 * that do catalog accesses. this is unfortunate, but not critical.
312 HeapTupleSatisfiesNow(HeapTupleHeader tuple
, Snapshot snapshot
, Buffer buffer
)
314 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
316 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
319 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
321 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
323 if (TransactionIdIsCurrentTransactionId(xvac
))
325 if (!TransactionIdIsInProgress(xvac
))
327 if (TransactionIdDidCommit(xvac
))
329 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
330 InvalidTransactionId
);
333 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
334 InvalidTransactionId
);
337 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
339 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
341 if (!TransactionIdIsCurrentTransactionId(xvac
))
343 if (TransactionIdIsInProgress(xvac
))
345 if (TransactionIdDidCommit(xvac
))
346 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
347 InvalidTransactionId
);
350 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
351 InvalidTransactionId
);
356 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple
)))
358 if (HeapTupleHeaderGetCmin(tuple
) >= GetCurrentCommandId(false))
359 return false; /* inserted after scan started */
361 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
364 if (tuple
->t_infomask
& HEAP_IS_LOCKED
) /* not deleter */
367 Assert(!(tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
));
369 if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
371 /* deleting subtransaction must have aborted */
372 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
373 InvalidTransactionId
);
377 if (HeapTupleHeaderGetCmax(tuple
) >= GetCurrentCommandId(false))
378 return true; /* deleted after scan started */
380 return false; /* deleted before scan started */
382 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
384 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
385 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
386 HeapTupleHeaderGetXmin(tuple
));
389 /* it must have aborted or crashed */
390 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
391 InvalidTransactionId
);
396 /* by here, the inserting transaction has committed */
398 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid or aborted */
401 if (tuple
->t_infomask
& HEAP_XMAX_COMMITTED
)
403 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
408 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
410 /* MultiXacts are currently only allowed to lock tuples */
411 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
415 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
417 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
419 if (HeapTupleHeaderGetCmax(tuple
) >= GetCurrentCommandId(false))
420 return true; /* deleted after scan started */
422 return false; /* deleted before scan started */
425 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
428 if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
430 /* it must have aborted or crashed */
431 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
432 InvalidTransactionId
);
436 /* xmax transaction committed */
438 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
440 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
441 InvalidTransactionId
);
445 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
446 HeapTupleHeaderGetXmax(tuple
));
451 * HeapTupleSatisfiesAny
452 * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
455 HeapTupleSatisfiesAny(HeapTupleHeader tuple
, Snapshot snapshot
, Buffer buffer
)
461 * HeapTupleSatisfiesToast
462 * True iff heap tuple is valid as a TOAST row.
464 * This is a simplified version that only checks for VACUUM moving conditions.
465 * It's appropriate for TOAST usage because TOAST really doesn't want to do
466 * its own time qual checks; if you can see the main table row that contains
467 * a TOAST reference, you should be able to see the TOASTed value. However,
468 * vacuuming a TOAST table is independent of the main table, and in case such
469 * a vacuum fails partway through, we'd better do this much checking.
471 * Among other things, this means you can't do UPDATEs of rows in a TOAST
475 HeapTupleSatisfiesToast(HeapTupleHeader tuple
, Snapshot snapshot
,
478 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
480 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
483 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
485 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
487 if (TransactionIdIsCurrentTransactionId(xvac
))
489 if (!TransactionIdIsInProgress(xvac
))
491 if (TransactionIdDidCommit(xvac
))
493 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
494 InvalidTransactionId
);
497 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
498 InvalidTransactionId
);
501 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
503 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
505 if (!TransactionIdIsCurrentTransactionId(xvac
))
507 if (TransactionIdIsInProgress(xvac
))
509 if (TransactionIdDidCommit(xvac
))
510 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
511 InvalidTransactionId
);
514 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
515 InvalidTransactionId
);
522 /* otherwise assume the tuple is valid for TOAST. */
527 * HeapTupleSatisfiesUpdate
529 * Same logic as HeapTupleSatisfiesNow, but returns a more detailed result
530 * code, since UPDATE needs to know more than "is it visible?". Also,
531 * tuples of my own xact are tested against the passed CommandId not
534 * The possible return codes are:
536 * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
537 * e.g. it was created by a later CommandId.
539 * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
542 * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
543 * after the current scan started.
545 * HeapTupleUpdated: The tuple was updated by a committed transaction.
547 * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
548 * transaction other than the current transaction. (Note: this includes
549 * the case where the tuple is share-locked by a MultiXact, even if the
550 * MultiXact includes the current transaction. Callers that want to
551 * distinguish that case must test for it themselves.)
554 HeapTupleSatisfiesUpdate(HeapTupleHeader tuple
, CommandId curcid
,
557 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
559 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
560 return HeapTupleInvisible
;
562 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
564 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
566 if (TransactionIdIsCurrentTransactionId(xvac
))
567 return HeapTupleInvisible
;
568 if (!TransactionIdIsInProgress(xvac
))
570 if (TransactionIdDidCommit(xvac
))
572 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
573 InvalidTransactionId
);
574 return HeapTupleInvisible
;
576 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
577 InvalidTransactionId
);
580 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
582 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
584 if (!TransactionIdIsCurrentTransactionId(xvac
))
586 if (TransactionIdIsInProgress(xvac
))
587 return HeapTupleInvisible
;
588 if (TransactionIdDidCommit(xvac
))
589 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
590 InvalidTransactionId
);
593 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
594 InvalidTransactionId
);
595 return HeapTupleInvisible
;
599 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple
)))
601 if (HeapTupleHeaderGetCmin(tuple
) >= curcid
)
602 return HeapTupleInvisible
; /* inserted after scan started */
604 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
605 return HeapTupleMayBeUpdated
;
607 if (tuple
->t_infomask
& HEAP_IS_LOCKED
) /* not deleter */
608 return HeapTupleMayBeUpdated
;
610 Assert(!(tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
));
612 if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
614 /* deleting subtransaction must have aborted */
615 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
616 InvalidTransactionId
);
617 return HeapTupleMayBeUpdated
;
620 if (HeapTupleHeaderGetCmax(tuple
) >= curcid
)
621 return HeapTupleSelfUpdated
; /* updated after scan started */
623 return HeapTupleInvisible
; /* updated before scan started */
625 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
626 return HeapTupleInvisible
;
627 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
628 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
629 HeapTupleHeaderGetXmin(tuple
));
632 /* it must have aborted or crashed */
633 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
634 InvalidTransactionId
);
635 return HeapTupleInvisible
;
639 /* by here, the inserting transaction has committed */
641 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid or aborted */
642 return HeapTupleMayBeUpdated
;
644 if (tuple
->t_infomask
& HEAP_XMAX_COMMITTED
)
646 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
647 return HeapTupleMayBeUpdated
;
648 return HeapTupleUpdated
; /* updated by other */
651 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
653 /* MultiXacts are currently only allowed to lock tuples */
654 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
656 if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple
)))
657 return HeapTupleBeingUpdated
;
658 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
659 InvalidTransactionId
);
660 return HeapTupleMayBeUpdated
;
663 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
665 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
666 return HeapTupleMayBeUpdated
;
667 if (HeapTupleHeaderGetCmax(tuple
) >= curcid
)
668 return HeapTupleSelfUpdated
; /* updated after scan started */
670 return HeapTupleInvisible
; /* updated before scan started */
673 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
674 return HeapTupleBeingUpdated
;
676 if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
678 /* it must have aborted or crashed */
679 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
680 InvalidTransactionId
);
681 return HeapTupleMayBeUpdated
;
684 /* xmax transaction committed */
686 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
688 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
689 InvalidTransactionId
);
690 return HeapTupleMayBeUpdated
;
693 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
694 HeapTupleHeaderGetXmax(tuple
));
695 return HeapTupleUpdated
; /* updated by other */
699 * HeapTupleSatisfiesDirty
700 * True iff heap tuple is valid including effects of open transactions.
702 * Here, we consider the effects of:
703 * all committed and in-progress transactions (as of the current instant)
704 * previous commands of this transaction
705 * changes made by the current command
707 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
708 * the current transaction and committed/aborted xacts are concerned.
709 * However, we also include the effects of other xacts still in progress.
711 * A special hack is that the passed-in snapshot struct is used as an
712 * output argument to return the xids of concurrent xacts that affected the
713 * tuple. snapshot->xmin is set to the tuple's xmin if that is another
714 * transaction that's still in progress; or to InvalidTransactionId if the
715 * tuple's xmin is committed good, committed dead, or my own xact. Similarly
716 * for snapshot->xmax and the tuple's xmax.
719 HeapTupleSatisfiesDirty(HeapTupleHeader tuple
, Snapshot snapshot
,
722 snapshot
->xmin
= snapshot
->xmax
= InvalidTransactionId
;
724 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
726 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
729 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
731 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
733 if (TransactionIdIsCurrentTransactionId(xvac
))
735 if (!TransactionIdIsInProgress(xvac
))
737 if (TransactionIdDidCommit(xvac
))
739 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
740 InvalidTransactionId
);
743 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
744 InvalidTransactionId
);
747 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
749 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
751 if (!TransactionIdIsCurrentTransactionId(xvac
))
753 if (TransactionIdIsInProgress(xvac
))
755 if (TransactionIdDidCommit(xvac
))
756 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
757 InvalidTransactionId
);
760 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
761 InvalidTransactionId
);
766 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple
)))
768 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
771 if (tuple
->t_infomask
& HEAP_IS_LOCKED
) /* not deleter */
774 Assert(!(tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
));
776 if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
778 /* deleting subtransaction must have aborted */
779 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
780 InvalidTransactionId
);
786 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
788 snapshot
->xmin
= HeapTupleHeaderGetXmin(tuple
);
789 /* XXX shouldn't we fall through to look at xmax? */
790 return true; /* in insertion by other */
792 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
793 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
794 HeapTupleHeaderGetXmin(tuple
));
797 /* it must have aborted or crashed */
798 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
799 InvalidTransactionId
);
804 /* by here, the inserting transaction has committed */
806 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid or aborted */
809 if (tuple
->t_infomask
& HEAP_XMAX_COMMITTED
)
811 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
813 return false; /* updated by other */
816 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
818 /* MultiXacts are currently only allowed to lock tuples */
819 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
823 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
825 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
830 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
832 snapshot
->xmax
= HeapTupleHeaderGetXmax(tuple
);
836 if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
838 /* it must have aborted or crashed */
839 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
840 InvalidTransactionId
);
844 /* xmax transaction committed */
846 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
848 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
849 InvalidTransactionId
);
853 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
854 HeapTupleHeaderGetXmax(tuple
));
855 return false; /* updated by other */
859 * HeapTupleSatisfiesMVCC
860 * True iff heap tuple is valid for the given MVCC snapshot.
862 * Here, we consider the effects of:
863 * all transactions committed as of the time of the given snapshot
864 * previous commands of this transaction
866 * Does _not_ include:
867 * transactions shown as in-progress by the snapshot
868 * transactions started after the snapshot was taken
869 * changes made by the current command
871 * This is the same as HeapTupleSatisfiesNow, except that transactions that
872 * were in progress or as yet unstarted when the snapshot was taken will
873 * be treated as uncommitted, even if they have committed by now.
875 * (Notice, however, that the tuple status hint bits will be updated on the
876 * basis of the true state of the transaction, even if we then pretend we
880 HeapTupleSatisfiesMVCC(HeapTupleHeader tuple
, Snapshot snapshot
,
883 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
885 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
888 if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
890 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
892 if (TransactionIdIsCurrentTransactionId(xvac
))
894 if (!TransactionIdIsInProgress(xvac
))
896 if (TransactionIdDidCommit(xvac
))
898 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
899 InvalidTransactionId
);
902 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
903 InvalidTransactionId
);
906 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
908 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
910 if (!TransactionIdIsCurrentTransactionId(xvac
))
912 if (TransactionIdIsInProgress(xvac
))
914 if (TransactionIdDidCommit(xvac
))
915 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
916 InvalidTransactionId
);
919 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
920 InvalidTransactionId
);
925 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple
)))
927 if (HeapTupleHeaderGetCmin(tuple
) >= snapshot
->curcid
)
928 return false; /* inserted after scan started */
930 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
933 if (tuple
->t_infomask
& HEAP_IS_LOCKED
) /* not deleter */
936 Assert(!(tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
));
938 if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
940 /* deleting subtransaction must have aborted */
941 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
942 InvalidTransactionId
);
946 if (HeapTupleHeaderGetCmax(tuple
) >= snapshot
->curcid
)
947 return true; /* deleted after scan started */
949 return false; /* deleted before scan started */
951 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
953 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
954 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
955 HeapTupleHeaderGetXmin(tuple
));
958 /* it must have aborted or crashed */
959 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
960 InvalidTransactionId
);
966 * By here, the inserting transaction has committed - have to check
969 if (XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple
), snapshot
))
970 return false; /* treat as still in progress */
972 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid or aborted */
975 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
978 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
980 /* MultiXacts are currently only allowed to lock tuples */
981 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
985 if (!(tuple
->t_infomask
& HEAP_XMAX_COMMITTED
))
987 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple
)))
989 if (HeapTupleHeaderGetCmax(tuple
) >= snapshot
->curcid
)
990 return true; /* deleted after scan started */
992 return false; /* deleted before scan started */
995 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
998 if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
1000 /* it must have aborted or crashed */
1001 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
1002 InvalidTransactionId
);
1006 /* xmax transaction committed */
1007 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
1008 HeapTupleHeaderGetXmax(tuple
));
1012 * OK, the deleting transaction committed too ... but when?
1014 if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple
), snapshot
))
1015 return true; /* treat as still in progress */
1022 * HeapTupleSatisfiesVacuum
1024 * Determine the status of tuples for VACUUM purposes. Here, what
1025 * we mainly want to know is if a tuple is potentially visible to *any*
1026 * running transaction. If so, it can't be removed yet by VACUUM.
1028 * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
1029 * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
1030 * still be visible to some open transaction, so we can't remove them,
1031 * even if we see that the deleting transaction has committed.
1034 HeapTupleSatisfiesVacuum(HeapTupleHeader tuple
, TransactionId OldestXmin
,
1038 * Has inserting transaction committed?
1040 * If the inserting transaction aborted, then the tuple was never visible
1041 * to any other transaction, so we can delete it immediately.
1043 if (!(tuple
->t_infomask
& HEAP_XMIN_COMMITTED
))
1045 if (tuple
->t_infomask
& HEAP_XMIN_INVALID
)
1046 return HEAPTUPLE_DEAD
;
1047 else if (tuple
->t_infomask
& HEAP_MOVED_OFF
)
1049 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
1051 if (TransactionIdIsCurrentTransactionId(xvac
))
1052 return HEAPTUPLE_DELETE_IN_PROGRESS
;
1053 if (TransactionIdIsInProgress(xvac
))
1054 return HEAPTUPLE_DELETE_IN_PROGRESS
;
1055 if (TransactionIdDidCommit(xvac
))
1057 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
1058 InvalidTransactionId
);
1059 return HEAPTUPLE_DEAD
;
1061 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
1062 InvalidTransactionId
);
1064 else if (tuple
->t_infomask
& HEAP_MOVED_IN
)
1066 TransactionId xvac
= HeapTupleHeaderGetXvac(tuple
);
1068 if (TransactionIdIsCurrentTransactionId(xvac
))
1069 return HEAPTUPLE_INSERT_IN_PROGRESS
;
1070 if (TransactionIdIsInProgress(xvac
))
1071 return HEAPTUPLE_INSERT_IN_PROGRESS
;
1072 if (TransactionIdDidCommit(xvac
))
1073 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
1074 InvalidTransactionId
);
1077 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
1078 InvalidTransactionId
);
1079 return HEAPTUPLE_DEAD
;
1082 else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple
)))
1084 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
) /* xid invalid */
1085 return HEAPTUPLE_INSERT_IN_PROGRESS
;
1086 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
1087 return HEAPTUPLE_INSERT_IN_PROGRESS
;
1088 /* inserted and then deleted by same xact */
1089 return HEAPTUPLE_DELETE_IN_PROGRESS
;
1091 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple
)))
1092 SetHintBits(tuple
, buffer
, HEAP_XMIN_COMMITTED
,
1093 HeapTupleHeaderGetXmin(tuple
));
1097 * Not in Progress, Not Committed, so either Aborted or crashed
1099 SetHintBits(tuple
, buffer
, HEAP_XMIN_INVALID
,
1100 InvalidTransactionId
);
1101 return HEAPTUPLE_DEAD
;
1105 * At this point the xmin is known committed, but we might not have
1106 * been able to set the hint bit yet; so we can no longer Assert that
1112 * Okay, the inserter committed, so it was good at some point. Now what
1113 * about the deleting transaction?
1115 if (tuple
->t_infomask
& HEAP_XMAX_INVALID
)
1116 return HEAPTUPLE_LIVE
;
1118 if (tuple
->t_infomask
& HEAP_IS_LOCKED
)
1121 * "Deleting" xact really only locked it, so the tuple is live in any
1122 * case. However, we should make sure that either XMAX_COMMITTED or
1123 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1124 * examining the tuple for future xacts. Also, marking dead
1125 * MultiXacts as invalid here provides defense against MultiXactId
1126 * wraparound (see also comments in heap_freeze_tuple()).
1128 if (!(tuple
->t_infomask
& HEAP_XMAX_COMMITTED
))
1130 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
1132 if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple
)))
1133 return HEAPTUPLE_LIVE
;
1137 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
1138 return HEAPTUPLE_LIVE
;
1142 * We don't really care whether xmax did commit, abort or crash.
1143 * We know that xmax did lock the tuple, but it did not and will
1144 * never actually update it.
1146 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
1147 InvalidTransactionId
);
1149 return HEAPTUPLE_LIVE
;
1152 if (tuple
->t_infomask
& HEAP_XMAX_IS_MULTI
)
1154 /* MultiXacts are currently only allowed to lock tuples */
1155 Assert(tuple
->t_infomask
& HEAP_IS_LOCKED
);
1156 return HEAPTUPLE_LIVE
;
1159 if (!(tuple
->t_infomask
& HEAP_XMAX_COMMITTED
))
1161 if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple
)))
1162 return HEAPTUPLE_DELETE_IN_PROGRESS
;
1163 else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple
)))
1164 SetHintBits(tuple
, buffer
, HEAP_XMAX_COMMITTED
,
1165 HeapTupleHeaderGetXmax(tuple
));
1169 * Not in Progress, Not Committed, so either Aborted or crashed
1171 SetHintBits(tuple
, buffer
, HEAP_XMAX_INVALID
,
1172 InvalidTransactionId
);
1173 return HEAPTUPLE_LIVE
;
1177 * At this point the xmax is known committed, but we might not have
1178 * been able to set the hint bit yet; so we can no longer Assert that
1184 * Deleter committed, but check special cases.
1187 if (TransactionIdEquals(HeapTupleHeaderGetXmin(tuple
),
1188 HeapTupleHeaderGetXmax(tuple
)))
1191 * Inserter also deleted it, so it was never visible to anyone else.
1192 * However, we can only remove it early if it's not an updated tuple;
1193 * else its parent tuple is linking to it via t_ctid, and this tuple
1194 * mustn't go away before the parent does.
1196 if (!(tuple
->t_infomask
& HEAP_UPDATED
))
1197 return HEAPTUPLE_DEAD
;
1200 if (!TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple
), OldestXmin
))
1202 /* deleting xact is too recent, tuple could still be visible */
1203 return HEAPTUPLE_RECENTLY_DEAD
;
1206 /* Otherwise, it's dead and removable */
1207 return HEAPTUPLE_DEAD
;
1213 * Is the given XID still-in-progress according to the snapshot?
1215 * Note: GetSnapshotData never stores either top xid or subxids of our own
1216 * backend into a snapshot, so these xids will not be reported as "running"
1217 * by this function. This is OK for current uses, because we actually only
1218 * apply this for known-committed XIDs.
1221 XidInMVCCSnapshot(TransactionId xid
, Snapshot snapshot
)
1226 * Make a quick range check to eliminate most XIDs without looking at the
1227 * xip arrays. Note that this is OK even if we convert a subxact XID to
1228 * its parent below, because a subxact with XID < xmin has surely also got
1229 * a parent with XID < xmin, while one with XID >= xmax must belong to a
1230 * parent that was not yet committed at the time of this snapshot.
1233 /* Any xid < xmin is not in-progress */
1234 if (TransactionIdPrecedes(xid
, snapshot
->xmin
))
1236 /* Any xid >= xmax is in-progress */
1237 if (TransactionIdFollowsOrEquals(xid
, snapshot
->xmax
))
1241 * If the snapshot contains full subxact data, the fastest way to check
1242 * things is just to compare the given XID against both subxact XIDs and
1243 * top-level XIDs. If the snapshot overflowed, we have to use pg_subtrans
1244 * to convert a subxact XID to its parent XID, but then we need only look
1245 * at top-level XIDs not subxacts.
1247 if (snapshot
->subxcnt
>= 0)
1249 /* full data, so search subxip */
1252 for (j
= 0; j
< snapshot
->subxcnt
; j
++)
1254 if (TransactionIdEquals(xid
, snapshot
->subxip
[j
]))
1258 /* not there, fall through to search xip[] */
1262 /* overflowed, so convert xid to top-level */
1263 xid
= SubTransGetTopmostTransaction(xid
);
1266 * If xid was indeed a subxact, we might now have an xid < xmin, so
1267 * recheck to avoid an array scan. No point in rechecking xmax.
1269 if (TransactionIdPrecedes(xid
, snapshot
->xmin
))
1273 for (i
= 0; i
< snapshot
->xcnt
; i
++)
1275 if (TransactionIdEquals(xid
, snapshot
->xip
[i
]))