PostgreSQL Source Code git master
heapam_visibility.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * heapam_visibility.c
4 * Tuple visibility rules for tuples stored in heap.
5 *
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, MarkBufferDirtyHint 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.
12 *
13 * NOTE: When using a non-MVCC snapshot, we must check
14 * TransactionIdIsInProgress (which looks in the PGPROC array) before
15 * TransactionIdDidCommit (which look in pg_xact). Otherwise we have a race
16 * condition: we might decide that a just-committed transaction crashed,
17 * because none of the tests succeed. xact.c is careful to record
18 * commit/abort in pg_xact before it unsets MyProc->xid in the PGPROC array.
19 * That fixes that problem, but it also means there is a window where
20 * TransactionIdIsInProgress and TransactionIdDidCommit will both return true.
21 * If we check only TransactionIdDidCommit, we could consider a tuple
22 * committed when a later GetSnapshotData call will still think the
23 * originating transaction is in progress, which leads to application-level
24 * inconsistency. The upshot is that we gotta check TransactionIdIsInProgress
25 * first in all 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 race
27 * condition.
28 *
29 * We can't use TransactionIdDidAbort here because it won't treat transactions
30 * that were in progress during a crash as aborted. We determine that
31 * transactions aborted/crashed through process of elimination instead.
32 *
33 * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
34 * TransactionIdIsInProgress, but the logic is otherwise the same: do not
35 * check pg_xact until after deciding that the xact is no longer in progress.
36 *
37 *
38 * Summary of visibility functions:
39 *
40 * HeapTupleSatisfiesMVCC()
41 * visible to supplied snapshot, excludes current command
42 * HeapTupleSatisfiesUpdate()
43 * visible to instant snapshot, with user-supplied command
44 * counter and more complex result
45 * HeapTupleSatisfiesSelf()
46 * visible to instant snapshot and current command
47 * HeapTupleSatisfiesDirty()
48 * like HeapTupleSatisfiesSelf(), but includes open transactions
49 * HeapTupleSatisfiesVacuum()
50 * visible to any running transaction, used by VACUUM
51 * HeapTupleSatisfiesNonVacuumable()
52 * Snapshot-style API for HeapTupleSatisfiesVacuum
53 * HeapTupleSatisfiesToast()
54 * visible unless part of interrupted vacuum, used for TOAST
55 * HeapTupleSatisfiesAny()
56 * all tuples are visible
57 *
58 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
59 * Portions Copyright (c) 1994, Regents of the University of California
60 *
61 * IDENTIFICATION
62 * src/backend/access/heap/heapam_visibility.c
63 *
64 *-------------------------------------------------------------------------
65 */
66
67#include "postgres.h"
68
69#include "access/heapam.h"
70#include "access/htup_details.h"
71#include "access/multixact.h"
72#include "access/tableam.h"
73#include "access/transam.h"
74#include "access/xact.h"
75#include "access/xlog.h"
76#include "storage/bufmgr.h"
77#include "storage/procarray.h"
78#include "utils/builtins.h"
79#include "utils/snapmgr.h"
80
81
82/*
83 * SetHintBits()
84 *
85 * Set commit/abort hint bits on a tuple, if appropriate at this time.
86 *
87 * It is only safe to set a transaction-committed hint bit if we know the
88 * transaction's commit record is guaranteed to be flushed to disk before the
89 * buffer, or if the table is temporary or unlogged and will be obliterated by
90 * a crash anyway. We cannot change the LSN of the page here, because we may
91 * hold only a share lock on the buffer, so we can only use the LSN to
92 * interlock this if the buffer's LSN already is newer than the commit LSN;
93 * otherwise we have to just refrain from setting the hint bit until some
94 * future re-examination of the tuple.
95 *
96 * We can always set hint bits when marking a transaction aborted. (Some
97 * code in heapam.c relies on that!)
98 *
99 * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
100 * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
101 * synchronous commits and didn't move tuples that weren't previously
102 * hinted. (This is not known by this subroutine, but is applied by its
103 * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
104 * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
105 * support in-place update from pre-9.0 databases.
106 *
107 * Normal commits may be asynchronous, so for those we need to get the LSN
108 * of the transaction and then check whether this is flushed.
109 *
110 * The caller should pass xid as the XID of the transaction to check, or
111 * InvalidTransactionId if no check is needed.
112 */
113static inline void
115 uint16 infomask, TransactionId xid)
116{
117 if (TransactionIdIsValid(xid))
118 {
119 /* NB: xid must be known committed here! */
120 XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
121
122 if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
123 BufferGetLSNAtomic(buffer) < commitLSN)
124 {
125 /* not flushed and no LSN interlock, so don't set hint */
126 return;
127 }
128 }
129
130 tuple->t_infomask |= infomask;
131 MarkBufferDirtyHint(buffer, true);
132}
133
134/*
135 * HeapTupleSetHintBits --- exported version of SetHintBits()
136 *
137 * This must be separate because of C99's brain-dead notions about how to
138 * implement inline functions.
139 */
140void
142 uint16 infomask, TransactionId xid)
143{
144 SetHintBits(tuple, buffer, infomask, xid);
145}
146
147/*
148 * If HEAP_MOVED_OFF or HEAP_MOVED_IN are set on the tuple, remove them and
149 * adjust hint bits. See the comment for SetHintBits() for more background.
150 *
151 * This helper returns false if the row ought to be invisible, true otherwise.
152 */
153static inline bool
155{
156 TransactionId xvac;
157
158 /* only used by pre-9.0 binary upgrades */
159 if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
160 return true;
161
162 xvac = HeapTupleHeaderGetXvac(tuple);
163
165 elog(ERROR, "encountered tuple with HEAP_MOVED considered current");
166
168 elog(ERROR, "encountered tuple with HEAP_MOVED considered in-progress");
169
170 if (tuple->t_infomask & HEAP_MOVED_OFF)
171 {
172 if (TransactionIdDidCommit(xvac))
173 {
174 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
176 return false;
177 }
178 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
180 }
181 else if (tuple->t_infomask & HEAP_MOVED_IN)
182 {
183 if (TransactionIdDidCommit(xvac))
184 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
186 else
187 {
188 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
190 return false;
191 }
192 }
193
194 return true;
195}
196
197/*
198 * HeapTupleSatisfiesSelf
199 * True iff heap tuple is valid "for itself".
200 *
201 * See SNAPSHOT_MVCC's definition for the intended behaviour.
202 *
203 * Note:
204 * Assumes heap tuple is valid.
205 *
206 * The satisfaction of "itself" requires the following:
207 *
208 * ((Xmin == my-transaction && the row was updated by the current transaction, and
209 * (Xmax is null it was not deleted
210 * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
211 * ||
212 *
213 * (Xmin is committed && the row was modified by a committed transaction, and
214 * (Xmax is null || the row has not been deleted, or
215 * (Xmax != my-transaction && the row was deleted by another transaction
216 * Xmax is not committed))) that has not been committed
217 */
218static bool
220{
221 HeapTupleHeader tuple = htup->t_data;
222
224 Assert(htup->t_tableOid != InvalidOid);
225
227 {
229 return false;
230
231 if (!HeapTupleCleanMoved(tuple, buffer))
232 return false;
234 {
235 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
236 return true;
237
238 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
239 return true;
240
241 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
242 {
243 TransactionId xmax;
244
245 xmax = HeapTupleGetUpdateXid(tuple);
246
247 /* not LOCKED_ONLY, so it has to have an xmax */
249
250 /* updating subtransaction must have aborted */
252 return true;
253 else
254 return false;
255 }
256
258 {
259 /* deleting subtransaction must have aborted */
260 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
262 return true;
263 }
264
265 return false;
266 }
268 return false;
270 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
272 else
273 {
274 /* it must have aborted or crashed */
275 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
277 return false;
278 }
279 }
280
281 /* by here, the inserting transaction has committed */
282
283 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
284 return true;
285
286 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
287 {
289 return true;
290 return false; /* updated by other */
291 }
292
293 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
294 {
295 TransactionId xmax;
296
298 return true;
299
300 xmax = HeapTupleGetUpdateXid(tuple);
301
302 /* not LOCKED_ONLY, so it has to have an xmax */
304
306 return false;
308 return true;
309 if (TransactionIdDidCommit(xmax))
310 return false;
311 /* it must have aborted or crashed */
312 return true;
313 }
314
316 {
318 return true;
319 return false;
320 }
321
323 return true;
324
326 {
327 /* it must have aborted or crashed */
328 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
330 return true;
331 }
332
333 /* xmax transaction committed */
334
336 {
337 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
339 return true;
340 }
341
342 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
344 return false;
345}
346
347/*
348 * HeapTupleSatisfiesAny
349 * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
350 */
351static bool
353{
354 return true;
355}
356
357/*
358 * HeapTupleSatisfiesToast
359 * True iff heap tuple is valid as a TOAST row.
360 *
361 * See SNAPSHOT_TOAST's definition for the intended behaviour.
362 *
363 * This is a simplified version that only checks for VACUUM moving conditions.
364 * It's appropriate for TOAST usage because TOAST really doesn't want to do
365 * its own time qual checks; if you can see the main table row that contains
366 * a TOAST reference, you should be able to see the TOASTed value. However,
367 * vacuuming a TOAST table is independent of the main table, and in case such
368 * a vacuum fails partway through, we'd better do this much checking.
369 *
370 * Among other things, this means you can't do UPDATEs of rows in a TOAST
371 * table.
372 */
373static bool
375 Buffer buffer)
376{
377 HeapTupleHeader tuple = htup->t_data;
378
380 Assert(htup->t_tableOid != InvalidOid);
381
383 {
385 return false;
386
387 if (!HeapTupleCleanMoved(tuple, buffer))
388 return false;
389
390 /*
391 * An invalid Xmin can be left behind by a speculative insertion that
392 * is canceled by super-deleting the tuple. This also applies to
393 * TOAST tuples created during speculative insertion.
394 */
396 return false;
397 }
398
399 /* otherwise assume the tuple is valid for TOAST. */
400 return true;
401}
402
403/*
404 * HeapTupleSatisfiesUpdate
405 *
406 * This function returns a more detailed result code than most of the
407 * functions in this file, since UPDATE needs to know more than "is it
408 * visible?". It also allows for user-supplied CommandId rather than
409 * relying on CurrentCommandId.
410 *
411 * The possible return codes are:
412 *
413 * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
414 * was created by a later CommandId.
415 *
416 * TM_Ok: The tuple is valid and visible, so it may be updated.
417 *
418 * TM_SelfModified: The tuple was updated by the current transaction, after
419 * the current scan started.
420 *
421 * TM_Updated: The tuple was updated by a committed transaction (including
422 * the case where the tuple was moved into a different partition).
423 *
424 * TM_Deleted: The tuple was deleted by a committed transaction.
425 *
426 * TM_BeingModified: The tuple is being updated by an in-progress transaction
427 * other than the current transaction. (Note: this includes the case where
428 * the tuple is share-locked by a MultiXact, even if the MultiXact includes
429 * the current transaction. Callers that want to distinguish that case must
430 * test for it themselves.)
431 */
434 Buffer buffer)
435{
436 HeapTupleHeader tuple = htup->t_data;
437
439 Assert(htup->t_tableOid != InvalidOid);
440
442 {
444 return TM_Invisible;
445
446 else if (!HeapTupleCleanMoved(tuple, buffer))
447 return TM_Invisible;
449 {
450 if (HeapTupleHeaderGetCmin(tuple) >= curcid)
451 return TM_Invisible; /* inserted after scan started */
452
453 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
454 return TM_Ok;
455
457 {
458 TransactionId xmax;
459
460 xmax = HeapTupleHeaderGetRawXmax(tuple);
461
462 /*
463 * Careful here: even though this tuple was created by our own
464 * transaction, it might be locked by other transactions, if
465 * the original version was key-share locked when we updated
466 * it.
467 */
468
469 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
470 {
471 if (MultiXactIdIsRunning(xmax, true))
472 return TM_BeingModified;
473 else
474 return TM_Ok;
475 }
476
477 /*
478 * If the locker is gone, then there is nothing of interest
479 * left in this Xmax; otherwise, report the tuple as
480 * locked/updated.
481 */
482 if (!TransactionIdIsInProgress(xmax))
483 return TM_Ok;
484 return TM_BeingModified;
485 }
486
487 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
488 {
489 TransactionId xmax;
490
491 xmax = HeapTupleGetUpdateXid(tuple);
492
493 /* not LOCKED_ONLY, so it has to have an xmax */
495
496 /* deleting subtransaction must have aborted */
498 {
500 false))
501 return TM_BeingModified;
502 return TM_Ok;
503 }
504 else
505 {
506 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
507 return TM_SelfModified; /* updated after scan started */
508 else
509 return TM_Invisible; /* updated before scan started */
510 }
511 }
512
514 {
515 /* deleting subtransaction must have aborted */
516 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
518 return TM_Ok;
519 }
520
521 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
522 return TM_SelfModified; /* updated after scan started */
523 else
524 return TM_Invisible; /* updated before scan started */
525 }
527 return TM_Invisible;
529 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
531 else
532 {
533 /* it must have aborted or crashed */
534 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
536 return TM_Invisible;
537 }
538 }
539
540 /* by here, the inserting transaction has committed */
541
542 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
543 return TM_Ok;
544
545 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
546 {
548 return TM_Ok;
549 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
550 return TM_Updated; /* updated by other */
551 else
552 return TM_Deleted; /* deleted by other */
553 }
554
555 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
556 {
557 TransactionId xmax;
558
560 return TM_Ok;
561
563 {
565 return TM_BeingModified;
566
568 return TM_Ok;
569 }
570
571 xmax = HeapTupleGetUpdateXid(tuple);
572 if (!TransactionIdIsValid(xmax))
573 {
575 return TM_BeingModified;
576 }
577
578 /* not LOCKED_ONLY, so it has to have an xmax */
580
582 {
583 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
584 return TM_SelfModified; /* updated after scan started */
585 else
586 return TM_Invisible; /* updated before scan started */
587 }
588
590 return TM_BeingModified;
591
592 if (TransactionIdDidCommit(xmax))
593 {
594 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
595 return TM_Updated;
596 else
597 return TM_Deleted;
598 }
599
600 /*
601 * By here, the update in the Xmax is either aborted or crashed, but
602 * what about the other members?
603 */
604
606 {
607 /*
608 * There's no member, even just a locker, alive anymore, so we can
609 * mark the Xmax as invalid.
610 */
611 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
613 return TM_Ok;
614 }
615 else
616 {
617 /* There are lockers running */
618 return TM_BeingModified;
619 }
620 }
621
623 {
625 return TM_BeingModified;
626 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
627 return TM_SelfModified; /* updated after scan started */
628 else
629 return TM_Invisible; /* updated before scan started */
630 }
631
633 return TM_BeingModified;
634
636 {
637 /* it must have aborted or crashed */
638 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
640 return TM_Ok;
641 }
642
643 /* xmax transaction committed */
644
646 {
647 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
649 return TM_Ok;
650 }
651
652 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
654 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
655 return TM_Updated; /* updated by other */
656 else
657 return TM_Deleted; /* deleted by other */
658}
659
660/*
661 * HeapTupleSatisfiesDirty
662 * True iff heap tuple is valid including effects of open transactions.
663 *
664 * See SNAPSHOT_DIRTY's definition for the intended behaviour.
665 *
666 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
667 * the current transaction and committed/aborted xacts are concerned.
668 * However, we also include the effects of other xacts still in progress.
669 *
670 * A special hack is that the passed-in snapshot struct is used as an
671 * output argument to return the xids of concurrent xacts that affected the
672 * tuple. snapshot->xmin is set to the tuple's xmin if that is another
673 * transaction that's still in progress; or to InvalidTransactionId if the
674 * tuple's xmin is committed good, committed dead, or my own xact.
675 * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
676 * inserted speculatively, meaning that the inserter might still back down
677 * on the insertion without aborting the whole transaction, the associated
678 * token is also returned in snapshot->speculativeToken.
679 */
680static bool
682 Buffer buffer)
683{
684 HeapTupleHeader tuple = htup->t_data;
685
687 Assert(htup->t_tableOid != InvalidOid);
688
689 snapshot->xmin = snapshot->xmax = InvalidTransactionId;
690 snapshot->speculativeToken = 0;
691
693 {
695 return false;
696
697 if (!HeapTupleCleanMoved(tuple, buffer))
698 return false;
700 {
701 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
702 return true;
703
704 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
705 return true;
706
707 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
708 {
709 TransactionId xmax;
710
711 xmax = HeapTupleGetUpdateXid(tuple);
712
713 /* not LOCKED_ONLY, so it has to have an xmax */
715
716 /* updating subtransaction must have aborted */
718 return true;
719 else
720 return false;
721 }
722
724 {
725 /* deleting subtransaction must have aborted */
726 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
728 return true;
729 }
730
731 return false;
732 }
734 {
735 /*
736 * Return the speculative token to caller. Caller can worry about
737 * xmax, since it requires a conclusively locked row version, and
738 * a concurrent update to this tuple is a conflict of its
739 * purposes.
740 */
742 {
743 snapshot->speculativeToken =
745
746 Assert(snapshot->speculativeToken != 0);
747 }
748
749 snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
750 /* XXX shouldn't we fall through to look at xmax? */
751 return true; /* in insertion by other */
752 }
754 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
756 else
757 {
758 /* it must have aborted or crashed */
759 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
761 return false;
762 }
763 }
764
765 /* by here, the inserting transaction has committed */
766
767 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
768 return true;
769
770 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
771 {
773 return true;
774 return false; /* updated by other */
775 }
776
777 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
778 {
779 TransactionId xmax;
780
782 return true;
783
784 xmax = HeapTupleGetUpdateXid(tuple);
785
786 /* not LOCKED_ONLY, so it has to have an xmax */
788
790 return false;
792 {
793 snapshot->xmax = xmax;
794 return true;
795 }
796 if (TransactionIdDidCommit(xmax))
797 return false;
798 /* it must have aborted or crashed */
799 return true;
800 }
801
803 {
805 return true;
806 return false;
807 }
808
810 {
812 snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
813 return true;
814 }
815
817 {
818 /* it must have aborted or crashed */
819 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
821 return true;
822 }
823
824 /* xmax transaction committed */
825
827 {
828 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
830 return true;
831 }
832
833 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
835 return false; /* updated by other */
836}
837
838/*
839 * HeapTupleSatisfiesMVCC
840 * True iff heap tuple is valid for the given MVCC snapshot.
841 *
842 * See SNAPSHOT_MVCC's definition for the intended behaviour.
843 *
844 * Notice that here, we will not update the tuple status hint bits if the
845 * inserting/deleting transaction is still running according to our snapshot,
846 * even if in reality it's committed or aborted by now. This is intentional.
847 * Checking the true transaction state would require access to high-traffic
848 * shared data structures, creating contention we'd rather do without, and it
849 * would not change the result of our visibility check anyway. The hint bits
850 * will be updated by the first visitor that has a snapshot new enough to see
851 * the inserting/deleting transaction as done. In the meantime, the cost of
852 * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
853 * call will need to run TransactionIdIsCurrentTransactionId in addition to
854 * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
855 * coding where we tried to set the hint bits as soon as possible, we instead
856 * did TransactionIdIsInProgress in each call --- to no avail, as long as the
857 * inserting/deleting transaction was still running --- which was more cycles
858 * and more contention on ProcArrayLock.
859 */
860static bool
862 Buffer buffer)
863{
864 HeapTupleHeader tuple = htup->t_data;
865
866 /*
867 * Assert that the caller has registered the snapshot. This function
868 * doesn't care about the registration as such, but in general you
869 * shouldn't try to use a snapshot without registration because it might
870 * get invalidated while it's still in use, and this is a convenient place
871 * to check for that.
872 */
873 Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
874
876 Assert(htup->t_tableOid != InvalidOid);
877
879 {
881 return false;
882
883 if (!HeapTupleCleanMoved(tuple, buffer))
884 return false;
886 {
887 if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
888 return false; /* inserted after scan started */
889
890 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
891 return true;
892
893 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
894 return true;
895
896 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
897 {
898 TransactionId xmax;
899
900 xmax = HeapTupleGetUpdateXid(tuple);
901
902 /* not LOCKED_ONLY, so it has to have an xmax */
904
905 /* updating subtransaction must have aborted */
907 return true;
908 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
909 return true; /* updated after scan started */
910 else
911 return false; /* updated before scan started */
912 }
913
915 {
916 /* deleting subtransaction must have aborted */
917 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
919 return true;
920 }
921
922 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
923 return true; /* deleted after scan started */
924 else
925 return false; /* deleted before scan started */
926 }
927 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
928 return false;
930 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
932 else
933 {
934 /* it must have aborted or crashed */
935 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
937 return false;
938 }
939 }
940 else
941 {
942 /* xmin is committed, but maybe not according to our snapshot */
943 if (!HeapTupleHeaderXminFrozen(tuple) &&
945 return false; /* treat as still in progress */
946 }
947
948 /* by here, the inserting transaction has committed */
949
950 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
951 return true;
952
954 return true;
955
956 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
957 {
958 TransactionId xmax;
959
960 /* already checked above */
962
963 xmax = HeapTupleGetUpdateXid(tuple);
964
965 /* not LOCKED_ONLY, so it has to have an xmax */
967
969 {
970 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
971 return true; /* deleted after scan started */
972 else
973 return false; /* deleted before scan started */
974 }
975 if (XidInMVCCSnapshot(xmax, snapshot))
976 return true;
977 if (TransactionIdDidCommit(xmax))
978 return false; /* updating transaction committed */
979 /* it must have aborted or crashed */
980 return true;
981 }
982
983 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
984 {
986 {
987 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
988 return true; /* deleted after scan started */
989 else
990 return false; /* deleted before scan started */
991 }
992
993 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
994 return true;
995
997 {
998 /* it must have aborted or crashed */
999 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1001 return true;
1002 }
1003
1004 /* xmax transaction committed */
1005 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1007 }
1008 else
1009 {
1010 /* xmax is committed, but maybe not according to our snapshot */
1011 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1012 return true; /* treat as still in progress */
1013 }
1014
1015 /* xmax transaction committed */
1016
1017 return false;
1018}
1019
1020
1021/*
1022 * HeapTupleSatisfiesVacuum
1023 *
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.
1027 *
1028 * OldestXmin is a cutoff XID (obtained from
1029 * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1030 * OldestXmin are deemed "recently dead"; they might still be visible to some
1031 * open transaction, so we can't remove them, even if we see that the deleting
1032 * transaction has committed.
1033 */
1036 Buffer buffer)
1037{
1039 HTSV_Result res;
1040
1041 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1042
1043 if (res == HEAPTUPLE_RECENTLY_DEAD)
1044 {
1045 Assert(TransactionIdIsValid(dead_after));
1046
1047 if (TransactionIdPrecedes(dead_after, OldestXmin))
1048 res = HEAPTUPLE_DEAD;
1049 }
1050 else
1051 Assert(!TransactionIdIsValid(dead_after));
1052
1053 return res;
1054}
1055
1056/*
1057 * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1058 *
1059 * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1060 * tuple that could still be visible to some backend, stores the xid that
1061 * needs to be compared with the horizon in *dead_after, and returns
1062 * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1063 * the horizon. This is e.g. useful when comparing with different horizons.
1064 *
1065 * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1066 * transaction aborted.
1067 */
1070{
1071 HeapTupleHeader tuple = htup->t_data;
1072
1074 Assert(htup->t_tableOid != InvalidOid);
1075 Assert(dead_after != NULL);
1076
1077 *dead_after = InvalidTransactionId;
1078
1079 /*
1080 * Has inserting transaction committed?
1081 *
1082 * If the inserting transaction aborted, then the tuple was never visible
1083 * to any other transaction, so we can delete it immediately.
1084 */
1085 if (!HeapTupleHeaderXminCommitted(tuple))
1086 {
1087 if (HeapTupleHeaderXminInvalid(tuple))
1088 return HEAPTUPLE_DEAD;
1089 else if (!HeapTupleCleanMoved(tuple, buffer))
1090 return HEAPTUPLE_DEAD;
1092 {
1093 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1095 /* only locked? run infomask-only check first, for performance */
1099 /* inserted and then deleted by same xact */
1102 /* deleting subtransaction must have aborted */
1104 }
1106 {
1107 /*
1108 * It'd be possible to discern between INSERT/DELETE in progress
1109 * here by looking at xmax - but that doesn't seem beneficial for
1110 * the majority of callers and even detrimental for some. We'd
1111 * rather have callers look at/wait for xmin than xmax. It's
1112 * always correct to return INSERT_IN_PROGRESS because that's
1113 * what's happening from the view of other backends.
1114 */
1116 }
1118 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1120 else
1121 {
1122 /*
1123 * Not in Progress, Not Committed, so either Aborted or crashed
1124 */
1125 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1127 return HEAPTUPLE_DEAD;
1128 }
1129
1130 /*
1131 * At this point the xmin is known committed, but we might not have
1132 * been able to set the hint bit yet; so we can no longer Assert that
1133 * it's set.
1134 */
1135 }
1136
1137 /*
1138 * Okay, the inserter committed, so it was good at some point. Now what
1139 * about the deleting transaction?
1140 */
1141 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1142 return HEAPTUPLE_LIVE;
1143
1145 {
1146 /*
1147 * "Deleting" xact really only locked it, so the tuple is live in any
1148 * case. However, we should make sure that either XMAX_COMMITTED or
1149 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1150 * examining the tuple for future xacts.
1151 */
1152 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1153 {
1154 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1155 {
1156 /*
1157 * If it's a pre-pg_upgrade tuple, the multixact cannot
1158 * possibly be running; otherwise have to check.
1159 */
1160 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1162 true))
1163 return HEAPTUPLE_LIVE;
1165 }
1166 else
1167 {
1169 return HEAPTUPLE_LIVE;
1170 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1172 }
1173 }
1174
1175 /*
1176 * We don't really care whether xmax did commit, abort or crash. We
1177 * know that xmax did lock the tuple, but it did not and will never
1178 * actually update it.
1179 */
1180
1181 return HEAPTUPLE_LIVE;
1182 }
1183
1184 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1185 {
1187
1188 /* already checked above */
1190
1191 /* not LOCKED_ONLY, so it has to have an xmax */
1193
1194 if (TransactionIdIsInProgress(xmax))
1196 else if (TransactionIdDidCommit(xmax))
1197 {
1198 /*
1199 * The multixact might still be running due to lockers. Need to
1200 * allow for pruning if below the xid horizon regardless --
1201 * otherwise we could end up with a tuple where the updater has to
1202 * be removed due to the horizon, but is not pruned away. It's
1203 * not a problem to prune that tuple, because any remaining
1204 * lockers will also be present in newer tuple versions.
1205 */
1206 *dead_after = xmax;
1208 }
1209 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1210 {
1211 /*
1212 * Not in Progress, Not Committed, so either Aborted or crashed.
1213 * Mark the Xmax as invalid.
1214 */
1216 }
1217
1218 return HEAPTUPLE_LIVE;
1219 }
1220
1221 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1222 {
1226 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1228 else
1229 {
1230 /*
1231 * Not in Progress, Not Committed, so either Aborted or crashed
1232 */
1233 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1235 return HEAPTUPLE_LIVE;
1236 }
1237
1238 /*
1239 * At this point the xmax is known committed, but we might not have
1240 * been able to set the hint bit yet; so we can no longer Assert that
1241 * it's set.
1242 */
1243 }
1244
1245 /*
1246 * Deleter committed, allow caller to check if it was recent enough that
1247 * some open transactions could still see the tuple.
1248 */
1249 *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1251}
1252
1253
1254/*
1255 * HeapTupleSatisfiesNonVacuumable
1256 *
1257 * True if tuple might be visible to some transaction; false if it's
1258 * surely dead to everyone, ie, vacuumable.
1259 *
1260 * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1261 *
1262 * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1263 * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1264 * snapshot->vistest must have been set up with the horizon to use.
1265 */
1266static bool
1268 Buffer buffer)
1269{
1271 HTSV_Result res;
1272
1273 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1274
1275 if (res == HEAPTUPLE_RECENTLY_DEAD)
1276 {
1277 Assert(TransactionIdIsValid(dead_after));
1278
1279 if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
1280 res = HEAPTUPLE_DEAD;
1281 }
1282 else
1283 Assert(!TransactionIdIsValid(dead_after));
1284
1285 return res != HEAPTUPLE_DEAD;
1286}
1287
1288
1289/*
1290 * HeapTupleIsSurelyDead
1291 *
1292 * Cheaply determine whether a tuple is surely dead to all onlookers.
1293 * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1294 * tuple has just been tested by another visibility routine (usually
1295 * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1296 * should already be set. We assume that if no hint bits are set, the xmin
1297 * or xmax transaction is still running. This is therefore faster than
1298 * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1299 * It's okay to return false when in doubt, but we must return true only
1300 * if the tuple is removable.
1301 */
1302bool
1304{
1305 HeapTupleHeader tuple = htup->t_data;
1306
1308 Assert(htup->t_tableOid != InvalidOid);
1309
1310 /*
1311 * If the inserting transaction is marked invalid, then it aborted, and
1312 * the tuple is definitely dead. If it's marked neither committed nor
1313 * invalid, then we assume it's still alive (since the presumption is that
1314 * all relevant hint bits were just set moments ago).
1315 */
1316 if (!HeapTupleHeaderXminCommitted(tuple))
1317 return HeapTupleHeaderXminInvalid(tuple);
1318
1319 /*
1320 * If the inserting transaction committed, but any deleting transaction
1321 * aborted, the tuple is still alive.
1322 */
1323 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1324 return false;
1325
1326 /*
1327 * If the XMAX is just a lock, the tuple is still alive.
1328 */
1330 return false;
1331
1332 /*
1333 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1334 * know without checking pg_multixact.
1335 */
1336 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1337 return false;
1338
1339 /* If deleter isn't known to have committed, assume it's still running. */
1340 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1341 return false;
1342
1343 /* Deleter committed, so tuple is dead if the XID is old enough. */
1344 return GlobalVisTestIsRemovableXid(vistest,
1346}
1347
1348/*
1349 * Is the tuple really only locked? That is, is it not updated?
1350 *
1351 * It's easy to check just infomask bits if the locker is not a multi; but
1352 * otherwise we need to verify that the updating transaction has not aborted.
1353 *
1354 * This function is here because it follows the same visibility rules laid out
1355 * at the top of this file.
1356 */
1357bool
1359{
1360 TransactionId xmax;
1361
1362 /* if there's no valid Xmax, then there's obviously no update either */
1363 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1364 return true;
1365
1366 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1367 return true;
1368
1369 /* invalid xmax means no update */
1371 return true;
1372
1373 /*
1374 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1375 * necessarily have been updated
1376 */
1377 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1378 return false;
1379
1380 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1381 xmax = HeapTupleGetUpdateXid(tuple);
1382
1383 /* not LOCKED_ONLY, so it has to have an xmax */
1385
1387 return false;
1388 if (TransactionIdIsInProgress(xmax))
1389 return false;
1390 if (TransactionIdDidCommit(xmax))
1391 return false;
1392
1393 /*
1394 * not current, not in progress, not committed -- must have aborted or
1395 * crashed
1396 */
1397 return true;
1398}
1399
1400/*
1401 * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1402 */
1403static bool
1405{
1406 return num > 0 &&
1407 bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1408}
1409
1410/*
1411 * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1412 * obeys.
1413 *
1414 * Only usable on tuples from catalog tables!
1415 *
1416 * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1417 * reading catalog pages which couldn't have been created in an older version.
1418 *
1419 * We don't set any hint bits in here as it seems unlikely to be beneficial as
1420 * those should already be set by normal access and it seems to be too
1421 * dangerous to do so as the semantics of doing so during timetravel are more
1422 * complicated than when dealing "only" with the present.
1423 */
1424static bool
1426 Buffer buffer)
1427{
1428 HeapTupleHeader tuple = htup->t_data;
1431
1433 Assert(htup->t_tableOid != InvalidOid);
1434
1435 /* inserting transaction aborted */
1436 if (HeapTupleHeaderXminInvalid(tuple))
1437 {
1439 return false;
1440 }
1441 /* check if it's one of our txids, toplevel is also in there */
1442 else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1443 {
1444 bool resolved;
1447
1448 /*
1449 * another transaction might have (tried to) delete this tuple or
1450 * cmin/cmax was stored in a combo CID. So we need to lookup the
1451 * actual values externally.
1452 */
1454 htup, buffer,
1455 &cmin, &cmax);
1456
1457 /*
1458 * If we haven't resolved the combo CID to cmin/cmax, that means we
1459 * have not decoded the combo CID yet. That means the cmin is
1460 * definitely in the future, and we're not supposed to see the tuple
1461 * yet.
1462 *
1463 * XXX This only applies to decoding of in-progress transactions. In
1464 * regular logical decoding we only execute this code at commit time,
1465 * at which point we should have seen all relevant combo CIDs. So
1466 * ideally, we should error out in this case but in practice, this
1467 * won't happen. If we are too worried about this then we can add an
1468 * elog inside ResolveCminCmaxDuringDecoding.
1469 *
1470 * XXX For the streaming case, we can track the largest combo CID
1471 * assigned, and error out based on this (when unable to resolve combo
1472 * CID below that observed maximum value).
1473 */
1474 if (!resolved)
1475 return false;
1476
1477 Assert(cmin != InvalidCommandId);
1478
1479 if (cmin >= snapshot->curcid)
1480 return false; /* inserted after scan started */
1481 /* fall through */
1482 }
1483 /* committed before our xmin horizon. Do a normal visibility check. */
1484 else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1485 {
1487 !TransactionIdDidCommit(xmin)));
1488
1489 /* check for hint bit first, consult clog afterwards */
1490 if (!HeapTupleHeaderXminCommitted(tuple) &&
1492 return false;
1493 /* fall through */
1494 }
1495 /* beyond our xmax horizon, i.e. invisible */
1496 else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1497 {
1498 return false;
1499 }
1500 /* check if it's a committed transaction in [xmin, xmax) */
1501 else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1502 {
1503 /* fall through */
1504 }
1505
1506 /*
1507 * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1508 * invisible.
1509 */
1510 else
1511 {
1512 return false;
1513 }
1514
1515 /* at this point we know xmin is visible, go on to check xmax */
1516
1517 /* xid invalid or aborted */
1518 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1519 return true;
1520 /* locked tuples are always visible */
1521 else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1522 return true;
1523
1524 /*
1525 * We can see multis here if we're looking at user tables or if somebody
1526 * SELECT ... FOR SHARE/UPDATE a system table.
1527 */
1528 else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1529 {
1530 xmax = HeapTupleGetUpdateXid(tuple);
1531 }
1532
1533 /* check if it's one of our txids, toplevel is also in there */
1534 if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1535 {
1536 bool resolved;
1537 CommandId cmin;
1539
1540 /* Lookup actual cmin/cmax values */
1542 htup, buffer,
1543 &cmin, &cmax);
1544
1545 /*
1546 * If we haven't resolved the combo CID to cmin/cmax, that means we
1547 * have not decoded the combo CID yet. That means the cmax is
1548 * definitely in the future, and we're still supposed to see the
1549 * tuple.
1550 *
1551 * XXX This only applies to decoding of in-progress transactions. In
1552 * regular logical decoding we only execute this code at commit time,
1553 * at which point we should have seen all relevant combo CIDs. So
1554 * ideally, we should error out in this case but in practice, this
1555 * won't happen. If we are too worried about this then we can add an
1556 * elog inside ResolveCminCmaxDuringDecoding.
1557 *
1558 * XXX For the streaming case, we can track the largest combo CID
1559 * assigned, and error out based on this (when unable to resolve combo
1560 * CID below that observed maximum value).
1561 */
1562 if (!resolved || cmax == InvalidCommandId)
1563 return true;
1564
1565 if (cmax >= snapshot->curcid)
1566 return true; /* deleted after scan started */
1567 else
1568 return false; /* deleted before scan started */
1569 }
1570 /* below xmin horizon, normal transaction state is valid */
1571 else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1572 {
1574 !TransactionIdDidCommit(xmax)));
1575
1576 /* check hint bit first */
1577 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1578 return false;
1579
1580 /* check clog */
1581 return !TransactionIdDidCommit(xmax);
1582 }
1583 /* above xmax horizon, we cannot possibly see the deleting transaction */
1584 else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1585 return true;
1586 /* xmax is between [xmin, xmax), check known committed array */
1587 else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1588 return false;
1589 /* xmax is between [xmin, xmax), but known not to have committed yet */
1590 else
1591 return true;
1592}
1593
1594/*
1595 * HeapTupleSatisfiesVisibility
1596 * True iff heap tuple satisfies a time qual.
1597 *
1598 * Notes:
1599 * Assumes heap tuple is valid, and buffer at least share locked.
1600 *
1601 * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1602 * if so, the indicated buffer is marked dirty.
1603 */
1604bool
1606{
1607 switch (snapshot->snapshot_type)
1608 {
1609 case SNAPSHOT_MVCC:
1610 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1611 case SNAPSHOT_SELF:
1612 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1613 case SNAPSHOT_ANY:
1614 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1615 case SNAPSHOT_TOAST:
1616 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1617 case SNAPSHOT_DIRTY:
1618 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1620 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1622 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1623 }
1624
1625 return false; /* keep compiler quiet */
1626}
int Buffer
Definition: buf.h:23
bool BufferIsPermanent(Buffer buffer)
Definition: bufmgr.c:4564
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4594
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:5525
#define InvalidCommandId
Definition: c.h:689
#define likely(x)
Definition: c.h:417
uint16_t uint16
Definition: c.h:551
uint32 CommandId
Definition: c.h:686
uint32 TransactionId
Definition: c.h:672
size_t Size
Definition: c.h:625
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition: combocid.c:104
CommandId HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup)
Definition: combocid.c:118
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
Assert(PointerIsAligned(start, uint64))
TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
Definition: heapam.c:7624
HTSV_Result
Definition: heapam.h:125
@ HEAPTUPLE_RECENTLY_DEAD
Definition: heapam.h:128
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition: heapam.h:129
@ HEAPTUPLE_LIVE
Definition: heapam.h:127
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition: heapam.h:130
@ HEAPTUPLE_DEAD
Definition: heapam.h:126
static void SetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
static bool HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
static bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
TM_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer)
static bool HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
#define HEAP_MOVED_OFF
Definition: htup_details.h:211
static bool HeapTupleHeaderXminFrozen(const HeapTupleHeaderData *tup)
Definition: htup_details.h:350
#define HEAP_XMIN_COMMITTED
Definition: htup_details.h:204
static bool HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
Definition: htup_details.h:226
static bool HeapTupleHeaderXminInvalid(const HeapTupleHeaderData *tup)
Definition: htup_details.h:343
static TransactionId HeapTupleHeaderGetXvac(const HeapTupleHeaderData *tup)
Definition: htup_details.h:442
#define HEAP_XMAX_LOCK_ONLY
Definition: htup_details.h:197
#define HEAP_MOVED_IN
Definition: htup_details.h:212
static CommandId HeapTupleHeaderGetRawCommandId(const HeapTupleHeaderData *tup)
Definition: htup_details.h:415
static TransactionId HeapTupleHeaderGetRawXmax(const HeapTupleHeaderData *tup)
Definition: htup_details.h:377
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:209
#define HEAP_XMAX_COMMITTED
Definition: htup_details.h:207
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
Definition: htup_details.h:324
#define HEAP_XMIN_INVALID
Definition: htup_details.h:205
static BlockNumber HeapTupleHeaderGetSpeculativeToken(const HeapTupleHeaderData *tup)
Definition: htup_details.h:467
#define HEAP_XMAX_INVALID
Definition: htup_details.h:208
static TransactionId HeapTupleHeaderGetRawXmin(const HeapTupleHeaderData *tup)
Definition: htup_details.h:318
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
Definition: htup_details.h:461
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
Definition: htup_details.h:397
static bool HEAP_LOCKED_UPGRADED(uint16 infomask)
Definition: htup_details.h:251
static bool HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
Definition: htup_details.h:337
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition: itemptr.c:35
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
Definition: multixact.c:465
#define InvalidOid
Definition: postgres_ext.h:37
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition: procarray.c:4243
bool TransactionIdIsInProgress(TransactionId xid)
Definition: procarray.c:1404
bool ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
Definition: snapmgr.c:1869
HTAB * HistoricSnapshotGetTupleCids(void)
Definition: snapmgr.c:1698
@ SNAPSHOT_TOAST
Definition: snapshot.h:70
@ SNAPSHOT_SELF
Definition: snapshot.h:60
@ SNAPSHOT_NON_VACUUMABLE
Definition: snapshot.h:114
@ SNAPSHOT_MVCC
Definition: snapshot.h:46
@ SNAPSHOT_ANY
Definition: snapshot.h:65
@ SNAPSHOT_HISTORIC_MVCC
Definition: snapshot.h:105
@ SNAPSHOT_DIRTY
Definition: snapshot.h:98
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
ItemPointerData t_ctid
Definition: htup_details.h:161
TransactionId xmin
Definition: snapshot.h:153
int32 subxcnt
Definition: snapshot.h:177
uint32 regd_count
Definition: snapshot.h:201
uint32 active_count
Definition: snapshot.h:200
CommandId curcid
Definition: snapshot.h:183
struct GlobalVisState * vistest
Definition: snapshot.h:195
uint32 xcnt
Definition: snapshot.h:165
TransactionId * subxip
Definition: snapshot.h:176
TransactionId xmax
Definition: snapshot.h:154
uint32 speculativeToken
Definition: snapshot.h:189
SnapshotType snapshot_type
Definition: snapshot.h:140
TransactionId * xip
Definition: snapshot.h:164
TM_Result
Definition: tableam.h:73
@ TM_Ok
Definition: tableam.h:78
@ TM_BeingModified
Definition: tableam.h:100
@ TM_Deleted
Definition: tableam.h:93
@ TM_Updated
Definition: tableam.h:90
@ TM_SelfModified
Definition: tableam.h:84
@ TM_Invisible
Definition: tableam.h:81
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition: transam.c:318
#define InvalidTransactionId
Definition: transam.h:31
static bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.h:312
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.h:263
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:942
int xidComparator(const void *arg1, const void *arg2)
Definition: xid.c:152
bool XLogNeedsFlush(XLogRecPtr record)
Definition: xlog.c:3146
uint64 XLogRecPtr
Definition: xlogdefs.h:21