PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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-2025, 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/*
149 * HeapTupleSatisfiesSelf
150 * True iff heap tuple is valid "for itself".
151 *
152 * See SNAPSHOT_MVCC's definition for the intended behaviour.
153 *
154 * Note:
155 * Assumes heap tuple is valid.
156 *
157 * The satisfaction of "itself" requires the following:
158 *
159 * ((Xmin == my-transaction && the row was updated by the current transaction, and
160 * (Xmax is null it was not deleted
161 * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
162 * ||
163 *
164 * (Xmin is committed && the row was modified by a committed transaction, and
165 * (Xmax is null || the row has not been deleted, or
166 * (Xmax != my-transaction && the row was deleted by another transaction
167 * Xmax is not committed))) that has not been committed
168 */
169static bool
171{
172 HeapTupleHeader tuple = htup->t_data;
173
175 Assert(htup->t_tableOid != InvalidOid);
176
178 {
180 return false;
181
182 /* Used by pre-9.0 binary upgrades */
183 if (tuple->t_infomask & HEAP_MOVED_OFF)
184 {
186
188 return false;
189 if (!TransactionIdIsInProgress(xvac))
190 {
191 if (TransactionIdDidCommit(xvac))
192 {
193 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
195 return false;
196 }
197 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
199 }
200 }
201 /* Used by pre-9.0 binary upgrades */
202 else if (tuple->t_infomask & HEAP_MOVED_IN)
203 {
205
207 {
209 return false;
210 if (TransactionIdDidCommit(xvac))
211 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
213 else
214 {
215 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
217 return false;
218 }
219 }
220 }
222 {
223 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
224 return true;
225
226 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
227 return true;
228
229 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
230 {
231 TransactionId xmax;
232
233 xmax = HeapTupleGetUpdateXid(tuple);
234
235 /* not LOCKED_ONLY, so it has to have an xmax */
237
238 /* updating subtransaction must have aborted */
240 return true;
241 else
242 return false;
243 }
244
246 {
247 /* deleting subtransaction must have aborted */
248 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
250 return true;
251 }
252
253 return false;
254 }
256 return false;
258 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
260 else
261 {
262 /* it must have aborted or crashed */
263 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
265 return false;
266 }
267 }
268
269 /* by here, the inserting transaction has committed */
270
271 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
272 return true;
273
274 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
275 {
277 return true;
278 return false; /* updated by other */
279 }
280
281 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
282 {
283 TransactionId xmax;
284
286 return true;
287
288 xmax = HeapTupleGetUpdateXid(tuple);
289
290 /* not LOCKED_ONLY, so it has to have an xmax */
292
294 return false;
296 return true;
297 if (TransactionIdDidCommit(xmax))
298 return false;
299 /* it must have aborted or crashed */
300 return true;
301 }
302
304 {
306 return true;
307 return false;
308 }
309
311 return true;
312
314 {
315 /* it must have aborted or crashed */
316 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
318 return true;
319 }
320
321 /* xmax transaction committed */
322
324 {
325 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
327 return true;
328 }
329
330 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
332 return false;
333}
334
335/*
336 * HeapTupleSatisfiesAny
337 * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
338 */
339static bool
341{
342 return true;
343}
344
345/*
346 * HeapTupleSatisfiesToast
347 * True iff heap tuple is valid as a TOAST row.
348 *
349 * See SNAPSHOT_TOAST's definition for the intended behaviour.
350 *
351 * This is a simplified version that only checks for VACUUM moving conditions.
352 * It's appropriate for TOAST usage because TOAST really doesn't want to do
353 * its own time qual checks; if you can see the main table row that contains
354 * a TOAST reference, you should be able to see the TOASTed value. However,
355 * vacuuming a TOAST table is independent of the main table, and in case such
356 * a vacuum fails partway through, we'd better do this much checking.
357 *
358 * Among other things, this means you can't do UPDATEs of rows in a TOAST
359 * table.
360 */
361static bool
363 Buffer buffer)
364{
365 HeapTupleHeader tuple = htup->t_data;
366
368 Assert(htup->t_tableOid != InvalidOid);
369
371 {
373 return false;
374
375 /* Used by pre-9.0 binary upgrades */
376 if (tuple->t_infomask & HEAP_MOVED_OFF)
377 {
379
381 return false;
382 if (!TransactionIdIsInProgress(xvac))
383 {
384 if (TransactionIdDidCommit(xvac))
385 {
386 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
388 return false;
389 }
390 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
392 }
393 }
394 /* Used by pre-9.0 binary upgrades */
395 else if (tuple->t_infomask & HEAP_MOVED_IN)
396 {
398
400 {
402 return false;
403 if (TransactionIdDidCommit(xvac))
404 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
406 else
407 {
408 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
410 return false;
411 }
412 }
413 }
414
415 /*
416 * An invalid Xmin can be left behind by a speculative insertion that
417 * is canceled by super-deleting the tuple. This also applies to
418 * TOAST tuples created during speculative insertion.
419 */
421 return false;
422 }
423
424 /* otherwise assume the tuple is valid for TOAST. */
425 return true;
426}
427
428/*
429 * HeapTupleSatisfiesUpdate
430 *
431 * This function returns a more detailed result code than most of the
432 * functions in this file, since UPDATE needs to know more than "is it
433 * visible?". It also allows for user-supplied CommandId rather than
434 * relying on CurrentCommandId.
435 *
436 * The possible return codes are:
437 *
438 * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
439 * was created by a later CommandId.
440 *
441 * TM_Ok: The tuple is valid and visible, so it may be updated.
442 *
443 * TM_SelfModified: The tuple was updated by the current transaction, after
444 * the current scan started.
445 *
446 * TM_Updated: The tuple was updated by a committed transaction (including
447 * the case where the tuple was moved into a different partition).
448 *
449 * TM_Deleted: The tuple was deleted by a committed transaction.
450 *
451 * TM_BeingModified: The tuple is being updated by an in-progress transaction
452 * other than the current transaction. (Note: this includes the case where
453 * the tuple is share-locked by a MultiXact, even if the MultiXact includes
454 * the current transaction. Callers that want to distinguish that case must
455 * test for it themselves.)
456 */
459 Buffer buffer)
460{
461 HeapTupleHeader tuple = htup->t_data;
462
464 Assert(htup->t_tableOid != InvalidOid);
465
467 {
469 return TM_Invisible;
470
471 /* Used by pre-9.0 binary upgrades */
472 if (tuple->t_infomask & HEAP_MOVED_OFF)
473 {
475
477 return TM_Invisible;
478 if (!TransactionIdIsInProgress(xvac))
479 {
480 if (TransactionIdDidCommit(xvac))
481 {
482 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
484 return TM_Invisible;
485 }
486 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
488 }
489 }
490 /* Used by pre-9.0 binary upgrades */
491 else if (tuple->t_infomask & HEAP_MOVED_IN)
492 {
494
496 {
498 return TM_Invisible;
499 if (TransactionIdDidCommit(xvac))
500 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
502 else
503 {
504 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
506 return TM_Invisible;
507 }
508 }
509 }
511 {
512 if (HeapTupleHeaderGetCmin(tuple) >= curcid)
513 return TM_Invisible; /* inserted after scan started */
514
515 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
516 return TM_Ok;
517
519 {
520 TransactionId xmax;
521
522 xmax = HeapTupleHeaderGetRawXmax(tuple);
523
524 /*
525 * Careful here: even though this tuple was created by our own
526 * transaction, it might be locked by other transactions, if
527 * the original version was key-share locked when we updated
528 * it.
529 */
530
531 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
532 {
533 if (MultiXactIdIsRunning(xmax, true))
534 return TM_BeingModified;
535 else
536 return TM_Ok;
537 }
538
539 /*
540 * If the locker is gone, then there is nothing of interest
541 * left in this Xmax; otherwise, report the tuple as
542 * locked/updated.
543 */
544 if (!TransactionIdIsInProgress(xmax))
545 return TM_Ok;
546 return TM_BeingModified;
547 }
548
549 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
550 {
551 TransactionId xmax;
552
553 xmax = HeapTupleGetUpdateXid(tuple);
554
555 /* not LOCKED_ONLY, so it has to have an xmax */
557
558 /* deleting subtransaction must have aborted */
560 {
562 false))
563 return TM_BeingModified;
564 return TM_Ok;
565 }
566 else
567 {
568 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
569 return TM_SelfModified; /* updated after scan started */
570 else
571 return TM_Invisible; /* updated before scan started */
572 }
573 }
574
576 {
577 /* deleting subtransaction must have aborted */
578 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
580 return TM_Ok;
581 }
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 }
589 return TM_Invisible;
591 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
593 else
594 {
595 /* it must have aborted or crashed */
596 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
598 return TM_Invisible;
599 }
600 }
601
602 /* by here, the inserting transaction has committed */
603
604 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
605 return TM_Ok;
606
607 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
608 {
610 return TM_Ok;
611 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
612 return TM_Updated; /* updated by other */
613 else
614 return TM_Deleted; /* deleted by other */
615 }
616
617 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
618 {
619 TransactionId xmax;
620
622 return TM_Ok;
623
625 {
627 return TM_BeingModified;
628
630 return TM_Ok;
631 }
632
633 xmax = HeapTupleGetUpdateXid(tuple);
634 if (!TransactionIdIsValid(xmax))
635 {
637 return TM_BeingModified;
638 }
639
640 /* not LOCKED_ONLY, so it has to have an xmax */
642
644 {
645 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
646 return TM_SelfModified; /* updated after scan started */
647 else
648 return TM_Invisible; /* updated before scan started */
649 }
650
652 return TM_BeingModified;
653
654 if (TransactionIdDidCommit(xmax))
655 {
656 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
657 return TM_Updated;
658 else
659 return TM_Deleted;
660 }
661
662 /*
663 * By here, the update in the Xmax is either aborted or crashed, but
664 * what about the other members?
665 */
666
668 {
669 /*
670 * There's no member, even just a locker, alive anymore, so we can
671 * mark the Xmax as invalid.
672 */
673 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
675 return TM_Ok;
676 }
677 else
678 {
679 /* There are lockers running */
680 return TM_BeingModified;
681 }
682 }
683
685 {
687 return TM_BeingModified;
688 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
689 return TM_SelfModified; /* updated after scan started */
690 else
691 return TM_Invisible; /* updated before scan started */
692 }
693
695 return TM_BeingModified;
696
698 {
699 /* it must have aborted or crashed */
700 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
702 return TM_Ok;
703 }
704
705 /* xmax transaction committed */
706
708 {
709 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
711 return TM_Ok;
712 }
713
714 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
716 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
717 return TM_Updated; /* updated by other */
718 else
719 return TM_Deleted; /* deleted by other */
720}
721
722/*
723 * HeapTupleSatisfiesDirty
724 * True iff heap tuple is valid including effects of open transactions.
725 *
726 * See SNAPSHOT_DIRTY's definition for the intended behaviour.
727 *
728 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
729 * the current transaction and committed/aborted xacts are concerned.
730 * However, we also include the effects of other xacts still in progress.
731 *
732 * A special hack is that the passed-in snapshot struct is used as an
733 * output argument to return the xids of concurrent xacts that affected the
734 * tuple. snapshot->xmin is set to the tuple's xmin if that is another
735 * transaction that's still in progress; or to InvalidTransactionId if the
736 * tuple's xmin is committed good, committed dead, or my own xact.
737 * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
738 * inserted speculatively, meaning that the inserter might still back down
739 * on the insertion without aborting the whole transaction, the associated
740 * token is also returned in snapshot->speculativeToken.
741 */
742static bool
744 Buffer buffer)
745{
746 HeapTupleHeader tuple = htup->t_data;
747
749 Assert(htup->t_tableOid != InvalidOid);
750
751 snapshot->xmin = snapshot->xmax = InvalidTransactionId;
752 snapshot->speculativeToken = 0;
753
755 {
757 return false;
758
759 /* Used by pre-9.0 binary upgrades */
760 if (tuple->t_infomask & HEAP_MOVED_OFF)
761 {
763
765 return false;
766 if (!TransactionIdIsInProgress(xvac))
767 {
768 if (TransactionIdDidCommit(xvac))
769 {
770 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
772 return false;
773 }
774 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
776 }
777 }
778 /* Used by pre-9.0 binary upgrades */
779 else if (tuple->t_infomask & HEAP_MOVED_IN)
780 {
782
784 {
786 return false;
787 if (TransactionIdDidCommit(xvac))
788 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
790 else
791 {
792 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
794 return false;
795 }
796 }
797 }
799 {
800 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
801 return true;
802
803 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
804 return true;
805
806 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
807 {
808 TransactionId xmax;
809
810 xmax = HeapTupleGetUpdateXid(tuple);
811
812 /* not LOCKED_ONLY, so it has to have an xmax */
814
815 /* updating subtransaction must have aborted */
817 return true;
818 else
819 return false;
820 }
821
823 {
824 /* deleting subtransaction must have aborted */
825 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
827 return true;
828 }
829
830 return false;
831 }
833 {
834 /*
835 * Return the speculative token to caller. Caller can worry about
836 * xmax, since it requires a conclusively locked row version, and
837 * a concurrent update to this tuple is a conflict of its
838 * purposes.
839 */
841 {
842 snapshot->speculativeToken =
844
845 Assert(snapshot->speculativeToken != 0);
846 }
847
848 snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
849 /* XXX shouldn't we fall through to look at xmax? */
850 return true; /* in insertion by other */
851 }
853 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
855 else
856 {
857 /* it must have aborted or crashed */
858 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
860 return false;
861 }
862 }
863
864 /* by here, the inserting transaction has committed */
865
866 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
867 return true;
868
869 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
870 {
872 return true;
873 return false; /* updated by other */
874 }
875
876 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
877 {
878 TransactionId xmax;
879
881 return true;
882
883 xmax = HeapTupleGetUpdateXid(tuple);
884
885 /* not LOCKED_ONLY, so it has to have an xmax */
887
889 return false;
891 {
892 snapshot->xmax = xmax;
893 return true;
894 }
895 if (TransactionIdDidCommit(xmax))
896 return false;
897 /* it must have aborted or crashed */
898 return true;
899 }
900
902 {
904 return true;
905 return false;
906 }
907
909 {
911 snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
912 return true;
913 }
914
916 {
917 /* it must have aborted or crashed */
918 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
920 return true;
921 }
922
923 /* xmax transaction committed */
924
926 {
927 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
929 return true;
930 }
931
932 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
934 return false; /* updated by other */
935}
936
937/*
938 * HeapTupleSatisfiesMVCC
939 * True iff heap tuple is valid for the given MVCC snapshot.
940 *
941 * See SNAPSHOT_MVCC's definition for the intended behaviour.
942 *
943 * Notice that here, we will not update the tuple status hint bits if the
944 * inserting/deleting transaction is still running according to our snapshot,
945 * even if in reality it's committed or aborted by now. This is intentional.
946 * Checking the true transaction state would require access to high-traffic
947 * shared data structures, creating contention we'd rather do without, and it
948 * would not change the result of our visibility check anyway. The hint bits
949 * will be updated by the first visitor that has a snapshot new enough to see
950 * the inserting/deleting transaction as done. In the meantime, the cost of
951 * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
952 * call will need to run TransactionIdIsCurrentTransactionId in addition to
953 * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
954 * coding where we tried to set the hint bits as soon as possible, we instead
955 * did TransactionIdIsInProgress in each call --- to no avail, as long as the
956 * inserting/deleting transaction was still running --- which was more cycles
957 * and more contention on ProcArrayLock.
958 */
959static bool
961 Buffer buffer)
962{
963 HeapTupleHeader tuple = htup->t_data;
964
965 /*
966 * Assert that the caller has registered the snapshot. This function
967 * doesn't care about the registration as such, but in general you
968 * shouldn't try to use a snapshot without registration because it might
969 * get invalidated while it's still in use, and this is a convenient place
970 * to check for that.
971 */
972 Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
973
975 Assert(htup->t_tableOid != InvalidOid);
976
978 {
980 return false;
981
982 /* Used by pre-9.0 binary upgrades */
983 if (tuple->t_infomask & HEAP_MOVED_OFF)
984 {
986
988 return false;
989 if (!XidInMVCCSnapshot(xvac, snapshot))
990 {
991 if (TransactionIdDidCommit(xvac))
992 {
993 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
995 return false;
996 }
997 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
999 }
1000 }
1001 /* Used by pre-9.0 binary upgrades */
1002 else if (tuple->t_infomask & HEAP_MOVED_IN)
1003 {
1005
1007 {
1008 if (XidInMVCCSnapshot(xvac, snapshot))
1009 return false;
1010 if (TransactionIdDidCommit(xvac))
1011 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1013 else
1014 {
1015 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1017 return false;
1018 }
1019 }
1020 }
1022 {
1023 if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
1024 return false; /* inserted after scan started */
1025
1026 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1027 return true;
1028
1029 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
1030 return true;
1031
1032 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1033 {
1034 TransactionId xmax;
1035
1036 xmax = HeapTupleGetUpdateXid(tuple);
1037
1038 /* not LOCKED_ONLY, so it has to have an xmax */
1040
1041 /* updating subtransaction must have aborted */
1043 return true;
1044 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1045 return true; /* updated after scan started */
1046 else
1047 return false; /* updated before scan started */
1048 }
1049
1051 {
1052 /* deleting subtransaction must have aborted */
1053 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1055 return true;
1056 }
1057
1058 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1059 return true; /* deleted after scan started */
1060 else
1061 return false; /* deleted before scan started */
1062 }
1063 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1064 return false;
1066 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1068 else
1069 {
1070 /* it must have aborted or crashed */
1071 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1073 return false;
1074 }
1075 }
1076 else
1077 {
1078 /* xmin is committed, but maybe not according to our snapshot */
1079 if (!HeapTupleHeaderXminFrozen(tuple) &&
1081 return false; /* treat as still in progress */
1082 }
1083
1084 /* by here, the inserting transaction has committed */
1085
1086 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1087 return true;
1088
1090 return true;
1091
1092 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1093 {
1094 TransactionId xmax;
1095
1096 /* already checked above */
1098
1099 xmax = HeapTupleGetUpdateXid(tuple);
1100
1101 /* not LOCKED_ONLY, so it has to have an xmax */
1103
1105 {
1106 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1107 return true; /* deleted after scan started */
1108 else
1109 return false; /* deleted before scan started */
1110 }
1111 if (XidInMVCCSnapshot(xmax, snapshot))
1112 return true;
1113 if (TransactionIdDidCommit(xmax))
1114 return false; /* updating transaction committed */
1115 /* it must have aborted or crashed */
1116 return true;
1117 }
1118
1119 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1120 {
1122 {
1123 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1124 return true; /* deleted after scan started */
1125 else
1126 return false; /* deleted before scan started */
1127 }
1128
1129 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1130 return true;
1131
1133 {
1134 /* it must have aborted or crashed */
1135 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1137 return true;
1138 }
1139
1140 /* xmax transaction committed */
1141 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1143 }
1144 else
1145 {
1146 /* xmax is committed, but maybe not according to our snapshot */
1147 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1148 return true; /* treat as still in progress */
1149 }
1150
1151 /* xmax transaction committed */
1152
1153 return false;
1154}
1155
1156
1157/*
1158 * HeapTupleSatisfiesVacuum
1159 *
1160 * Determine the status of tuples for VACUUM purposes. Here, what
1161 * we mainly want to know is if a tuple is potentially visible to *any*
1162 * running transaction. If so, it can't be removed yet by VACUUM.
1163 *
1164 * OldestXmin is a cutoff XID (obtained from
1165 * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1166 * OldestXmin are deemed "recently dead"; they might still be visible to some
1167 * open transaction, so we can't remove them, even if we see that the deleting
1168 * transaction has committed.
1169 */
1172 Buffer buffer)
1173{
1175 HTSV_Result res;
1176
1177 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1178
1179 if (res == HEAPTUPLE_RECENTLY_DEAD)
1180 {
1181 Assert(TransactionIdIsValid(dead_after));
1182
1183 if (TransactionIdPrecedes(dead_after, OldestXmin))
1184 res = HEAPTUPLE_DEAD;
1185 }
1186 else
1187 Assert(!TransactionIdIsValid(dead_after));
1188
1189 return res;
1190}
1191
1192/*
1193 * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1194 *
1195 * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1196 * tuple that could still be visible to some backend, stores the xid that
1197 * needs to be compared with the horizon in *dead_after, and returns
1198 * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1199 * the horizon. This is e.g. useful when comparing with different horizons.
1200 *
1201 * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1202 * transaction aborted.
1203 */
1206{
1207 HeapTupleHeader tuple = htup->t_data;
1208
1210 Assert(htup->t_tableOid != InvalidOid);
1211 Assert(dead_after != NULL);
1212
1213 *dead_after = InvalidTransactionId;
1214
1215 /*
1216 * Has inserting transaction committed?
1217 *
1218 * If the inserting transaction aborted, then the tuple was never visible
1219 * to any other transaction, so we can delete it immediately.
1220 */
1221 if (!HeapTupleHeaderXminCommitted(tuple))
1222 {
1223 if (HeapTupleHeaderXminInvalid(tuple))
1224 return HEAPTUPLE_DEAD;
1225 /* Used by pre-9.0 binary upgrades */
1226 else if (tuple->t_infomask & HEAP_MOVED_OFF)
1227 {
1229
1232 if (TransactionIdIsInProgress(xvac))
1234 if (TransactionIdDidCommit(xvac))
1235 {
1236 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1238 return HEAPTUPLE_DEAD;
1239 }
1240 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1242 }
1243 /* Used by pre-9.0 binary upgrades */
1244 else if (tuple->t_infomask & HEAP_MOVED_IN)
1245 {
1247
1250 if (TransactionIdIsInProgress(xvac))
1252 if (TransactionIdDidCommit(xvac))
1253 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1255 else
1256 {
1257 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1259 return HEAPTUPLE_DEAD;
1260 }
1261 }
1263 {
1264 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1266 /* only locked? run infomask-only check first, for performance */
1270 /* inserted and then deleted by same xact */
1273 /* deleting subtransaction must have aborted */
1275 }
1277 {
1278 /*
1279 * It'd be possible to discern between INSERT/DELETE in progress
1280 * here by looking at xmax - but that doesn't seem beneficial for
1281 * the majority of callers and even detrimental for some. We'd
1282 * rather have callers look at/wait for xmin than xmax. It's
1283 * always correct to return INSERT_IN_PROGRESS because that's
1284 * what's happening from the view of other backends.
1285 */
1287 }
1289 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1291 else
1292 {
1293 /*
1294 * Not in Progress, Not Committed, so either Aborted or crashed
1295 */
1296 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1298 return HEAPTUPLE_DEAD;
1299 }
1300
1301 /*
1302 * At this point the xmin is known committed, but we might not have
1303 * been able to set the hint bit yet; so we can no longer Assert that
1304 * it's set.
1305 */
1306 }
1307
1308 /*
1309 * Okay, the inserter committed, so it was good at some point. Now what
1310 * about the deleting transaction?
1311 */
1312 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1313 return HEAPTUPLE_LIVE;
1314
1316 {
1317 /*
1318 * "Deleting" xact really only locked it, so the tuple is live in any
1319 * case. However, we should make sure that either XMAX_COMMITTED or
1320 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1321 * examining the tuple for future xacts.
1322 */
1323 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1324 {
1325 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1326 {
1327 /*
1328 * If it's a pre-pg_upgrade tuple, the multixact cannot
1329 * possibly be running; otherwise have to check.
1330 */
1331 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1333 true))
1334 return HEAPTUPLE_LIVE;
1336 }
1337 else
1338 {
1340 return HEAPTUPLE_LIVE;
1341 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1343 }
1344 }
1345
1346 /*
1347 * We don't really care whether xmax did commit, abort or crash. We
1348 * know that xmax did lock the tuple, but it did not and will never
1349 * actually update it.
1350 */
1351
1352 return HEAPTUPLE_LIVE;
1353 }
1354
1355 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1356 {
1358
1359 /* already checked above */
1361
1362 /* not LOCKED_ONLY, so it has to have an xmax */
1364
1365 if (TransactionIdIsInProgress(xmax))
1367 else if (TransactionIdDidCommit(xmax))
1368 {
1369 /*
1370 * The multixact might still be running due to lockers. Need to
1371 * allow for pruning if below the xid horizon regardless --
1372 * otherwise we could end up with a tuple where the updater has to
1373 * be removed due to the horizon, but is not pruned away. It's
1374 * not a problem to prune that tuple, because any remaining
1375 * lockers will also be present in newer tuple versions.
1376 */
1377 *dead_after = xmax;
1379 }
1380 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1381 {
1382 /*
1383 * Not in Progress, Not Committed, so either Aborted or crashed.
1384 * Mark the Xmax as invalid.
1385 */
1387 }
1388
1389 return HEAPTUPLE_LIVE;
1390 }
1391
1392 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1393 {
1397 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1399 else
1400 {
1401 /*
1402 * Not in Progress, Not Committed, so either Aborted or crashed
1403 */
1404 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1406 return HEAPTUPLE_LIVE;
1407 }
1408
1409 /*
1410 * At this point the xmax is known committed, but we might not have
1411 * been able to set the hint bit yet; so we can no longer Assert that
1412 * it's set.
1413 */
1414 }
1415
1416 /*
1417 * Deleter committed, allow caller to check if it was recent enough that
1418 * some open transactions could still see the tuple.
1419 */
1420 *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1422}
1423
1424
1425/*
1426 * HeapTupleSatisfiesNonVacuumable
1427 *
1428 * True if tuple might be visible to some transaction; false if it's
1429 * surely dead to everyone, ie, vacuumable.
1430 *
1431 * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1432 *
1433 * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1434 * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1435 * snapshot->vistest must have been set up with the horizon to use.
1436 */
1437static bool
1439 Buffer buffer)
1440{
1442 HTSV_Result res;
1443
1444 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1445
1446 if (res == HEAPTUPLE_RECENTLY_DEAD)
1447 {
1448 Assert(TransactionIdIsValid(dead_after));
1449
1450 if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
1451 res = HEAPTUPLE_DEAD;
1452 }
1453 else
1454 Assert(!TransactionIdIsValid(dead_after));
1455
1456 return res != HEAPTUPLE_DEAD;
1457}
1458
1459
1460/*
1461 * HeapTupleIsSurelyDead
1462 *
1463 * Cheaply determine whether a tuple is surely dead to all onlookers.
1464 * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1465 * tuple has just been tested by another visibility routine (usually
1466 * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1467 * should already be set. We assume that if no hint bits are set, the xmin
1468 * or xmax transaction is still running. This is therefore faster than
1469 * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1470 * It's okay to return false when in doubt, but we must return true only
1471 * if the tuple is removable.
1472 */
1473bool
1475{
1476 HeapTupleHeader tuple = htup->t_data;
1477
1479 Assert(htup->t_tableOid != InvalidOid);
1480
1481 /*
1482 * If the inserting transaction is marked invalid, then it aborted, and
1483 * the tuple is definitely dead. If it's marked neither committed nor
1484 * invalid, then we assume it's still alive (since the presumption is that
1485 * all relevant hint bits were just set moments ago).
1486 */
1487 if (!HeapTupleHeaderXminCommitted(tuple))
1488 return HeapTupleHeaderXminInvalid(tuple);
1489
1490 /*
1491 * If the inserting transaction committed, but any deleting transaction
1492 * aborted, the tuple is still alive.
1493 */
1494 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1495 return false;
1496
1497 /*
1498 * If the XMAX is just a lock, the tuple is still alive.
1499 */
1501 return false;
1502
1503 /*
1504 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1505 * know without checking pg_multixact.
1506 */
1507 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1508 return false;
1509
1510 /* If deleter isn't known to have committed, assume it's still running. */
1511 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1512 return false;
1513
1514 /* Deleter committed, so tuple is dead if the XID is old enough. */
1515 return GlobalVisTestIsRemovableXid(vistest,
1517}
1518
1519/*
1520 * Is the tuple really only locked? That is, is it not updated?
1521 *
1522 * It's easy to check just infomask bits if the locker is not a multi; but
1523 * otherwise we need to verify that the updating transaction has not aborted.
1524 *
1525 * This function is here because it follows the same visibility rules laid out
1526 * at the top of this file.
1527 */
1528bool
1530{
1531 TransactionId xmax;
1532
1533 /* if there's no valid Xmax, then there's obviously no update either */
1534 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1535 return true;
1536
1537 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1538 return true;
1539
1540 /* invalid xmax means no update */
1542 return true;
1543
1544 /*
1545 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1546 * necessarily have been updated
1547 */
1548 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1549 return false;
1550
1551 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1552 xmax = HeapTupleGetUpdateXid(tuple);
1553
1554 /* not LOCKED_ONLY, so it has to have an xmax */
1556
1558 return false;
1559 if (TransactionIdIsInProgress(xmax))
1560 return false;
1561 if (TransactionIdDidCommit(xmax))
1562 return false;
1563
1564 /*
1565 * not current, not in progress, not committed -- must have aborted or
1566 * crashed
1567 */
1568 return true;
1569}
1570
1571/*
1572 * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1573 */
1574static bool
1576{
1577 return num > 0 &&
1578 bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1579}
1580
1581/*
1582 * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1583 * obeys.
1584 *
1585 * Only usable on tuples from catalog tables!
1586 *
1587 * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1588 * reading catalog pages which couldn't have been created in an older version.
1589 *
1590 * We don't set any hint bits in here as it seems unlikely to be beneficial as
1591 * those should already be set by normal access and it seems to be too
1592 * dangerous to do so as the semantics of doing so during timetravel are more
1593 * complicated than when dealing "only" with the present.
1594 */
1595static bool
1597 Buffer buffer)
1598{
1599 HeapTupleHeader tuple = htup->t_data;
1602
1604 Assert(htup->t_tableOid != InvalidOid);
1605
1606 /* inserting transaction aborted */
1607 if (HeapTupleHeaderXminInvalid(tuple))
1608 {
1610 return false;
1611 }
1612 /* check if it's one of our txids, toplevel is also in there */
1613 else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1614 {
1615 bool resolved;
1618
1619 /*
1620 * another transaction might have (tried to) delete this tuple or
1621 * cmin/cmax was stored in a combo CID. So we need to lookup the
1622 * actual values externally.
1623 */
1625 htup, buffer,
1626 &cmin, &cmax);
1627
1628 /*
1629 * If we haven't resolved the combo CID to cmin/cmax, that means we
1630 * have not decoded the combo CID yet. That means the cmin is
1631 * definitely in the future, and we're not supposed to see the tuple
1632 * yet.
1633 *
1634 * XXX This only applies to decoding of in-progress transactions. In
1635 * regular logical decoding we only execute this code at commit time,
1636 * at which point we should have seen all relevant combo CIDs. So
1637 * ideally, we should error out in this case but in practice, this
1638 * won't happen. If we are too worried about this then we can add an
1639 * elog inside ResolveCminCmaxDuringDecoding.
1640 *
1641 * XXX For the streaming case, we can track the largest combo CID
1642 * assigned, and error out based on this (when unable to resolve combo
1643 * CID below that observed maximum value).
1644 */
1645 if (!resolved)
1646 return false;
1647
1648 Assert(cmin != InvalidCommandId);
1649
1650 if (cmin >= snapshot->curcid)
1651 return false; /* inserted after scan started */
1652 /* fall through */
1653 }
1654 /* committed before our xmin horizon. Do a normal visibility check. */
1655 else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1656 {
1658 !TransactionIdDidCommit(xmin)));
1659
1660 /* check for hint bit first, consult clog afterwards */
1661 if (!HeapTupleHeaderXminCommitted(tuple) &&
1663 return false;
1664 /* fall through */
1665 }
1666 /* beyond our xmax horizon, i.e. invisible */
1667 else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1668 {
1669 return false;
1670 }
1671 /* check if it's a committed transaction in [xmin, xmax) */
1672 else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1673 {
1674 /* fall through */
1675 }
1676
1677 /*
1678 * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1679 * invisible.
1680 */
1681 else
1682 {
1683 return false;
1684 }
1685
1686 /* at this point we know xmin is visible, go on to check xmax */
1687
1688 /* xid invalid or aborted */
1689 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1690 return true;
1691 /* locked tuples are always visible */
1692 else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1693 return true;
1694
1695 /*
1696 * We can see multis here if we're looking at user tables or if somebody
1697 * SELECT ... FOR SHARE/UPDATE a system table.
1698 */
1699 else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1700 {
1701 xmax = HeapTupleGetUpdateXid(tuple);
1702 }
1703
1704 /* check if it's one of our txids, toplevel is also in there */
1705 if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1706 {
1707 bool resolved;
1708 CommandId cmin;
1710
1711 /* Lookup actual cmin/cmax values */
1713 htup, buffer,
1714 &cmin, &cmax);
1715
1716 /*
1717 * If we haven't resolved the combo CID to cmin/cmax, that means we
1718 * have not decoded the combo CID yet. That means the cmax is
1719 * definitely in the future, and we're still supposed to see the
1720 * tuple.
1721 *
1722 * XXX This only applies to decoding of in-progress transactions. In
1723 * regular logical decoding we only execute this code at commit time,
1724 * at which point we should have seen all relevant combo CIDs. So
1725 * ideally, we should error out in this case but in practice, this
1726 * won't happen. If we are too worried about this then we can add an
1727 * elog inside ResolveCminCmaxDuringDecoding.
1728 *
1729 * XXX For the streaming case, we can track the largest combo CID
1730 * assigned, and error out based on this (when unable to resolve combo
1731 * CID below that observed maximum value).
1732 */
1733 if (!resolved || cmax == InvalidCommandId)
1734 return true;
1735
1736 if (cmax >= snapshot->curcid)
1737 return true; /* deleted after scan started */
1738 else
1739 return false; /* deleted before scan started */
1740 }
1741 /* below xmin horizon, normal transaction state is valid */
1742 else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1743 {
1745 !TransactionIdDidCommit(xmax)));
1746
1747 /* check hint bit first */
1748 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1749 return false;
1750
1751 /* check clog */
1752 return !TransactionIdDidCommit(xmax);
1753 }
1754 /* above xmax horizon, we cannot possibly see the deleting transaction */
1755 else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1756 return true;
1757 /* xmax is between [xmin, xmax), check known committed array */
1758 else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1759 return false;
1760 /* xmax is between [xmin, xmax), but known not to have committed yet */
1761 else
1762 return true;
1763}
1764
1765/*
1766 * HeapTupleSatisfiesVisibility
1767 * True iff heap tuple satisfies a time qual.
1768 *
1769 * Notes:
1770 * Assumes heap tuple is valid, and buffer at least share locked.
1771 *
1772 * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1773 * if so, the indicated buffer is marked dirty.
1774 */
1775bool
1777{
1778 switch (snapshot->snapshot_type)
1779 {
1780 case SNAPSHOT_MVCC:
1781 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1782 case SNAPSHOT_SELF:
1783 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1784 case SNAPSHOT_ANY:
1785 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1786 case SNAPSHOT_TOAST:
1787 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1788 case SNAPSHOT_DIRTY:
1789 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1791 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1793 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1794 }
1795
1796 return false; /* keep compiler quiet */
1797}
int Buffer
Definition: buf.h:23
bool BufferIsPermanent(Buffer buffer)
Definition: bufmgr.c:4393
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4423
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:5367
#define InvalidCommandId
Definition: c.h:640
uint16_t uint16
Definition: c.h:501
uint32 CommandId
Definition: c.h:637
uint32 TransactionId
Definition: c.h:623
size_t Size
Definition: c.h:576
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition: combocid.c:104
CommandId HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup)
Definition: combocid.c:118
Assert(PointerIsAligned(start, uint64))
TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
Definition: heapam.c:7535
HTSV_Result
Definition: heapam.h:123
@ HEAPTUPLE_RECENTLY_DEAD
Definition: heapam.h:126
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition: heapam.h:127
@ HEAPTUPLE_LIVE
Definition: heapam.h:125
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition: heapam.h:128
@ HEAPTUPLE_DEAD
Definition: heapam.h:124
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 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(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
Definition: multixact.c:599
#define InvalidOid
Definition: postgres_ext.h:35
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition: procarray.c:4264
bool TransactionIdIsInProgress(TransactionId xid)
Definition: procarray.c:1402
bool ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
Definition: snapmgr.c:1859
HTAB * HistoricSnapshotGetTupleCids(void)
Definition: snapmgr.c:1685
@ 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:72
@ TM_Ok
Definition: tableam.h:77
@ TM_BeingModified
Definition: tableam.h:99
@ TM_Deleted
Definition: tableam.h:92
@ TM_Updated
Definition: tableam.h:89
@ TM_SelfModified
Definition: tableam.h:83
@ TM_Invisible
Definition: tableam.h:80
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:126
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition: transam.c:382
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:329
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:941
int xidComparator(const void *arg1, const void *arg2)
Definition: xid.c:152
bool XLogNeedsFlush(XLogRecPtr record)
Definition: xlog.c:3254
uint64 XLogRecPtr
Definition: xlogdefs.h:21