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-2024, 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
966 Assert(htup->t_tableOid != InvalidOid);
967
969 {
971 return false;
972
973 /* Used by pre-9.0 binary upgrades */
974 if (tuple->t_infomask & HEAP_MOVED_OFF)
975 {
977
979 return false;
980 if (!XidInMVCCSnapshot(xvac, snapshot))
981 {
982 if (TransactionIdDidCommit(xvac))
983 {
984 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
986 return false;
987 }
988 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
990 }
991 }
992 /* Used by pre-9.0 binary upgrades */
993 else if (tuple->t_infomask & HEAP_MOVED_IN)
994 {
996
998 {
999 if (XidInMVCCSnapshot(xvac, snapshot))
1000 return false;
1001 if (TransactionIdDidCommit(xvac))
1002 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1004 else
1005 {
1006 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1008 return false;
1009 }
1010 }
1011 }
1013 {
1014 if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
1015 return false; /* inserted after scan started */
1016
1017 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1018 return true;
1019
1020 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
1021 return true;
1022
1023 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1024 {
1025 TransactionId xmax;
1026
1027 xmax = HeapTupleGetUpdateXid(tuple);
1028
1029 /* not LOCKED_ONLY, so it has to have an xmax */
1031
1032 /* updating subtransaction must have aborted */
1034 return true;
1035 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1036 return true; /* updated after scan started */
1037 else
1038 return false; /* updated before scan started */
1039 }
1040
1042 {
1043 /* deleting subtransaction must have aborted */
1044 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1046 return true;
1047 }
1048
1049 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1050 return true; /* deleted after scan started */
1051 else
1052 return false; /* deleted before scan started */
1053 }
1054 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1055 return false;
1057 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1059 else
1060 {
1061 /* it must have aborted or crashed */
1062 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1064 return false;
1065 }
1066 }
1067 else
1068 {
1069 /* xmin is committed, but maybe not according to our snapshot */
1070 if (!HeapTupleHeaderXminFrozen(tuple) &&
1072 return false; /* treat as still in progress */
1073 }
1074
1075 /* by here, the inserting transaction has committed */
1076
1077 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1078 return true;
1079
1081 return true;
1082
1083 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1084 {
1085 TransactionId xmax;
1086
1087 /* already checked above */
1089
1090 xmax = HeapTupleGetUpdateXid(tuple);
1091
1092 /* not LOCKED_ONLY, so it has to have an xmax */
1094
1096 {
1097 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1098 return true; /* deleted after scan started */
1099 else
1100 return false; /* deleted before scan started */
1101 }
1102 if (XidInMVCCSnapshot(xmax, snapshot))
1103 return true;
1104 if (TransactionIdDidCommit(xmax))
1105 return false; /* updating transaction committed */
1106 /* it must have aborted or crashed */
1107 return true;
1108 }
1109
1110 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1111 {
1113 {
1114 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1115 return true; /* deleted after scan started */
1116 else
1117 return false; /* deleted before scan started */
1118 }
1119
1120 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1121 return true;
1122
1124 {
1125 /* it must have aborted or crashed */
1126 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1128 return true;
1129 }
1130
1131 /* xmax transaction committed */
1132 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1134 }
1135 else
1136 {
1137 /* xmax is committed, but maybe not according to our snapshot */
1138 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1139 return true; /* treat as still in progress */
1140 }
1141
1142 /* xmax transaction committed */
1143
1144 return false;
1145}
1146
1147
1148/*
1149 * HeapTupleSatisfiesVacuum
1150 *
1151 * Determine the status of tuples for VACUUM purposes. Here, what
1152 * we mainly want to know is if a tuple is potentially visible to *any*
1153 * running transaction. If so, it can't be removed yet by VACUUM.
1154 *
1155 * OldestXmin is a cutoff XID (obtained from
1156 * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1157 * OldestXmin are deemed "recently dead"; they might still be visible to some
1158 * open transaction, so we can't remove them, even if we see that the deleting
1159 * transaction has committed.
1160 */
1163 Buffer buffer)
1164{
1167
1168 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1169
1171 {
1172 Assert(TransactionIdIsValid(dead_after));
1173
1174 if (TransactionIdPrecedes(dead_after, OldestXmin))
1176 }
1177 else
1178 Assert(!TransactionIdIsValid(dead_after));
1179
1180 return res;
1181}
1182
1183/*
1184 * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1185 *
1186 * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1187 * tuple that could still be visible to some backend, stores the xid that
1188 * needs to be compared with the horizon in *dead_after, and returns
1189 * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1190 * the horizon. This is e.g. useful when comparing with different horizons.
1191 *
1192 * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1193 * transaction aborted.
1194 */
1197{
1198 HeapTupleHeader tuple = htup->t_data;
1199
1201 Assert(htup->t_tableOid != InvalidOid);
1202 Assert(dead_after != NULL);
1203
1204 *dead_after = InvalidTransactionId;
1205
1206 /*
1207 * Has inserting transaction committed?
1208 *
1209 * If the inserting transaction aborted, then the tuple was never visible
1210 * to any other transaction, so we can delete it immediately.
1211 */
1212 if (!HeapTupleHeaderXminCommitted(tuple))
1213 {
1214 if (HeapTupleHeaderXminInvalid(tuple))
1215 return HEAPTUPLE_DEAD;
1216 /* Used by pre-9.0 binary upgrades */
1217 else if (tuple->t_infomask & HEAP_MOVED_OFF)
1218 {
1220
1223 if (TransactionIdIsInProgress(xvac))
1225 if (TransactionIdDidCommit(xvac))
1226 {
1227 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1229 return HEAPTUPLE_DEAD;
1230 }
1231 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1233 }
1234 /* Used by pre-9.0 binary upgrades */
1235 else if (tuple->t_infomask & HEAP_MOVED_IN)
1236 {
1238
1241 if (TransactionIdIsInProgress(xvac))
1243 if (TransactionIdDidCommit(xvac))
1244 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1246 else
1247 {
1248 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1250 return HEAPTUPLE_DEAD;
1251 }
1252 }
1254 {
1255 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1257 /* only locked? run infomask-only check first, for performance */
1261 /* inserted and then deleted by same xact */
1264 /* deleting subtransaction must have aborted */
1266 }
1268 {
1269 /*
1270 * It'd be possible to discern between INSERT/DELETE in progress
1271 * here by looking at xmax - but that doesn't seem beneficial for
1272 * the majority of callers and even detrimental for some. We'd
1273 * rather have callers look at/wait for xmin than xmax. It's
1274 * always correct to return INSERT_IN_PROGRESS because that's
1275 * what's happening from the view of other backends.
1276 */
1278 }
1280 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1282 else
1283 {
1284 /*
1285 * Not in Progress, Not Committed, so either Aborted or crashed
1286 */
1287 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1289 return HEAPTUPLE_DEAD;
1290 }
1291
1292 /*
1293 * At this point the xmin is known committed, but we might not have
1294 * been able to set the hint bit yet; so we can no longer Assert that
1295 * it's set.
1296 */
1297 }
1298
1299 /*
1300 * Okay, the inserter committed, so it was good at some point. Now what
1301 * about the deleting transaction?
1302 */
1303 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1304 return HEAPTUPLE_LIVE;
1305
1307 {
1308 /*
1309 * "Deleting" xact really only locked it, so the tuple is live in any
1310 * case. However, we should make sure that either XMAX_COMMITTED or
1311 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1312 * examining the tuple for future xacts.
1313 */
1314 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1315 {
1316 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1317 {
1318 /*
1319 * If it's a pre-pg_upgrade tuple, the multixact cannot
1320 * possibly be running; otherwise have to check.
1321 */
1322 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1324 true))
1325 return HEAPTUPLE_LIVE;
1327 }
1328 else
1329 {
1331 return HEAPTUPLE_LIVE;
1332 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1334 }
1335 }
1336
1337 /*
1338 * We don't really care whether xmax did commit, abort or crash. We
1339 * know that xmax did lock the tuple, but it did not and will never
1340 * actually update it.
1341 */
1342
1343 return HEAPTUPLE_LIVE;
1344 }
1345
1346 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1347 {
1349
1350 /* already checked above */
1352
1353 /* not LOCKED_ONLY, so it has to have an xmax */
1355
1356 if (TransactionIdIsInProgress(xmax))
1358 else if (TransactionIdDidCommit(xmax))
1359 {
1360 /*
1361 * The multixact might still be running due to lockers. Need to
1362 * allow for pruning if below the xid horizon regardless --
1363 * otherwise we could end up with a tuple where the updater has to
1364 * be removed due to the horizon, but is not pruned away. It's
1365 * not a problem to prune that tuple, because any remaining
1366 * lockers will also be present in newer tuple versions.
1367 */
1368 *dead_after = xmax;
1370 }
1371 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1372 {
1373 /*
1374 * Not in Progress, Not Committed, so either Aborted or crashed.
1375 * Mark the Xmax as invalid.
1376 */
1378 }
1379
1380 return HEAPTUPLE_LIVE;
1381 }
1382
1383 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1384 {
1388 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1390 else
1391 {
1392 /*
1393 * Not in Progress, Not Committed, so either Aborted or crashed
1394 */
1395 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1397 return HEAPTUPLE_LIVE;
1398 }
1399
1400 /*
1401 * At this point the xmax is known committed, but we might not have
1402 * been able to set the hint bit yet; so we can no longer Assert that
1403 * it's set.
1404 */
1405 }
1406
1407 /*
1408 * Deleter committed, allow caller to check if it was recent enough that
1409 * some open transactions could still see the tuple.
1410 */
1411 *dead_after = HeapTupleHeaderGetRawXmax(tuple);
1413}
1414
1415
1416/*
1417 * HeapTupleSatisfiesNonVacuumable
1418 *
1419 * True if tuple might be visible to some transaction; false if it's
1420 * surely dead to everyone, ie, vacuumable.
1421 *
1422 * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1423 *
1424 * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1425 * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1426 * snapshot->vistest must have been set up with the horizon to use.
1427 */
1428static bool
1430 Buffer buffer)
1431{
1434
1435 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1436
1438 {
1439 Assert(TransactionIdIsValid(dead_after));
1440
1441 if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
1443 }
1444 else
1445 Assert(!TransactionIdIsValid(dead_after));
1446
1447 return res != HEAPTUPLE_DEAD;
1448}
1449
1450
1451/*
1452 * HeapTupleIsSurelyDead
1453 *
1454 * Cheaply determine whether a tuple is surely dead to all onlookers.
1455 * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1456 * tuple has just been tested by another visibility routine (usually
1457 * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1458 * should already be set. We assume that if no hint bits are set, the xmin
1459 * or xmax transaction is still running. This is therefore faster than
1460 * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1461 * It's okay to return false when in doubt, but we must return true only
1462 * if the tuple is removable.
1463 */
1464bool
1466{
1467 HeapTupleHeader tuple = htup->t_data;
1468
1470 Assert(htup->t_tableOid != InvalidOid);
1471
1472 /*
1473 * If the inserting transaction is marked invalid, then it aborted, and
1474 * the tuple is definitely dead. If it's marked neither committed nor
1475 * invalid, then we assume it's still alive (since the presumption is that
1476 * all relevant hint bits were just set moments ago).
1477 */
1478 if (!HeapTupleHeaderXminCommitted(tuple))
1479 return HeapTupleHeaderXminInvalid(tuple);
1480
1481 /*
1482 * If the inserting transaction committed, but any deleting transaction
1483 * aborted, the tuple is still alive.
1484 */
1485 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1486 return false;
1487
1488 /*
1489 * If the XMAX is just a lock, the tuple is still alive.
1490 */
1492 return false;
1493
1494 /*
1495 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1496 * know without checking pg_multixact.
1497 */
1498 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1499 return false;
1500
1501 /* If deleter isn't known to have committed, assume it's still running. */
1502 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1503 return false;
1504
1505 /* Deleter committed, so tuple is dead if the XID is old enough. */
1506 return GlobalVisTestIsRemovableXid(vistest,
1508}
1509
1510/*
1511 * Is the tuple really only locked? That is, is it not updated?
1512 *
1513 * It's easy to check just infomask bits if the locker is not a multi; but
1514 * otherwise we need to verify that the updating transaction has not aborted.
1515 *
1516 * This function is here because it follows the same visibility rules laid out
1517 * at the top of this file.
1518 */
1519bool
1521{
1522 TransactionId xmax;
1523
1524 /* if there's no valid Xmax, then there's obviously no update either */
1525 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1526 return true;
1527
1528 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1529 return true;
1530
1531 /* invalid xmax means no update */
1533 return true;
1534
1535 /*
1536 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1537 * necessarily have been updated
1538 */
1539 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1540 return false;
1541
1542 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1543 xmax = HeapTupleGetUpdateXid(tuple);
1544
1545 /* not LOCKED_ONLY, so it has to have an xmax */
1547
1549 return false;
1550 if (TransactionIdIsInProgress(xmax))
1551 return false;
1552 if (TransactionIdDidCommit(xmax))
1553 return false;
1554
1555 /*
1556 * not current, not in progress, not committed -- must have aborted or
1557 * crashed
1558 */
1559 return true;
1560}
1561
1562/*
1563 * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1564 */
1565static bool
1567{
1568 return num > 0 &&
1569 bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1570}
1571
1572/*
1573 * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1574 * obeys.
1575 *
1576 * Only usable on tuples from catalog tables!
1577 *
1578 * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1579 * reading catalog pages which couldn't have been created in an older version.
1580 *
1581 * We don't set any hint bits in here as it seems unlikely to be beneficial as
1582 * those should already be set by normal access and it seems to be too
1583 * dangerous to do so as the semantics of doing so during timetravel are more
1584 * complicated than when dealing "only" with the present.
1585 */
1586static bool
1588 Buffer buffer)
1589{
1590 HeapTupleHeader tuple = htup->t_data;
1593
1595 Assert(htup->t_tableOid != InvalidOid);
1596
1597 /* inserting transaction aborted */
1598 if (HeapTupleHeaderXminInvalid(tuple))
1599 {
1601 return false;
1602 }
1603 /* check if it's one of our txids, toplevel is also in there */
1604 else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1605 {
1606 bool resolved;
1609
1610 /*
1611 * another transaction might have (tried to) delete this tuple or
1612 * cmin/cmax was stored in a combo CID. So we need to lookup the
1613 * actual values externally.
1614 */
1616 htup, buffer,
1617 &cmin, &cmax);
1618
1619 /*
1620 * If we haven't resolved the combo CID to cmin/cmax, that means we
1621 * have not decoded the combo CID yet. That means the cmin is
1622 * definitely in the future, and we're not supposed to see the tuple
1623 * yet.
1624 *
1625 * XXX This only applies to decoding of in-progress transactions. In
1626 * regular logical decoding we only execute this code at commit time,
1627 * at which point we should have seen all relevant combo CIDs. So
1628 * ideally, we should error out in this case but in practice, this
1629 * won't happen. If we are too worried about this then we can add an
1630 * elog inside ResolveCminCmaxDuringDecoding.
1631 *
1632 * XXX For the streaming case, we can track the largest combo CID
1633 * assigned, and error out based on this (when unable to resolve combo
1634 * CID below that observed maximum value).
1635 */
1636 if (!resolved)
1637 return false;
1638
1639 Assert(cmin != InvalidCommandId);
1640
1641 if (cmin >= snapshot->curcid)
1642 return false; /* inserted after scan started */
1643 /* fall through */
1644 }
1645 /* committed before our xmin horizon. Do a normal visibility check. */
1646 else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1647 {
1649 !TransactionIdDidCommit(xmin)));
1650
1651 /* check for hint bit first, consult clog afterwards */
1652 if (!HeapTupleHeaderXminCommitted(tuple) &&
1654 return false;
1655 /* fall through */
1656 }
1657 /* beyond our xmax horizon, i.e. invisible */
1658 else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1659 {
1660 return false;
1661 }
1662 /* check if it's a committed transaction in [xmin, xmax) */
1663 else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1664 {
1665 /* fall through */
1666 }
1667
1668 /*
1669 * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1670 * invisible.
1671 */
1672 else
1673 {
1674 return false;
1675 }
1676
1677 /* at this point we know xmin is visible, go on to check xmax */
1678
1679 /* xid invalid or aborted */
1680 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1681 return true;
1682 /* locked tuples are always visible */
1683 else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1684 return true;
1685
1686 /*
1687 * We can see multis here if we're looking at user tables or if somebody
1688 * SELECT ... FOR SHARE/UPDATE a system table.
1689 */
1690 else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1691 {
1692 xmax = HeapTupleGetUpdateXid(tuple);
1693 }
1694
1695 /* check if it's one of our txids, toplevel is also in there */
1696 if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1697 {
1698 bool resolved;
1699 CommandId cmin;
1701
1702 /* Lookup actual cmin/cmax values */
1704 htup, buffer,
1705 &cmin, &cmax);
1706
1707 /*
1708 * If we haven't resolved the combo CID to cmin/cmax, that means we
1709 * have not decoded the combo CID yet. That means the cmax is
1710 * definitely in the future, and we're still supposed to see the
1711 * tuple.
1712 *
1713 * XXX This only applies to decoding of in-progress transactions. In
1714 * regular logical decoding we only execute this code at commit time,
1715 * at which point we should have seen all relevant combo CIDs. So
1716 * ideally, we should error out in this case but in practice, this
1717 * won't happen. If we are too worried about this then we can add an
1718 * elog inside ResolveCminCmaxDuringDecoding.
1719 *
1720 * XXX For the streaming case, we can track the largest combo CID
1721 * assigned, and error out based on this (when unable to resolve combo
1722 * CID below that observed maximum value).
1723 */
1724 if (!resolved || cmax == InvalidCommandId)
1725 return true;
1726
1727 if (cmax >= snapshot->curcid)
1728 return true; /* deleted after scan started */
1729 else
1730 return false; /* deleted before scan started */
1731 }
1732 /* below xmin horizon, normal transaction state is valid */
1733 else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1734 {
1736 !TransactionIdDidCommit(xmax)));
1737
1738 /* check hint bit first */
1739 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1740 return false;
1741
1742 /* check clog */
1743 return !TransactionIdDidCommit(xmax);
1744 }
1745 /* above xmax horizon, we cannot possibly see the deleting transaction */
1746 else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1747 return true;
1748 /* xmax is between [xmin, xmax), check known committed array */
1749 else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1750 return false;
1751 /* xmax is between [xmin, xmax), but known not to have committed yet */
1752 else
1753 return true;
1754}
1755
1756/*
1757 * HeapTupleSatisfiesVisibility
1758 * True iff heap tuple satisfies a time qual.
1759 *
1760 * Notes:
1761 * Assumes heap tuple is valid, and buffer at least share locked.
1762 *
1763 * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1764 * if so, the indicated buffer is marked dirty.
1765 */
1766bool
1768{
1769 switch (snapshot->snapshot_type)
1770 {
1771 case SNAPSHOT_MVCC:
1772 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1773 case SNAPSHOT_SELF:
1774 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1775 case SNAPSHOT_ANY:
1776 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1777 case SNAPSHOT_TOAST:
1778 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1779 case SNAPSHOT_DIRTY:
1780 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1782 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1784 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1785 }
1786
1787 return false; /* keep compiler quiet */
1788}
int Buffer
Definition: buf.h:23
bool BufferIsPermanent(Buffer buffer)
Definition: bufmgr.c:3955
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:3985
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:4988
#define InvalidCommandId
Definition: c.h:623
#define Assert(condition)
Definition: c.h:812
uint16_t uint16
Definition: c.h:484
uint32 CommandId
Definition: c.h:620
uint32 TransactionId
Definition: c.h:606
size_t Size
Definition: c.h:559
CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup)
Definition: combocid.c:104
CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup)
Definition: combocid.c:118
TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple)
Definition: heapam.c:7422
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 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
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask)
Definition: htup_details.h:227
#define HEAP_XMIN_COMMITTED
Definition: htup_details.h:204
#define HeapTupleHeaderGetXvac(tup)
Definition: htup_details.h:411
#define HEAP_XMAX_LOCK_ONLY
Definition: htup_details.h:197
#define HEAP_MOVED_IN
Definition: htup_details.h:212
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:309
#define HEAP_XMAX_IS_MULTI
Definition: htup_details.h:209
#define HEAP_XMAX_COMMITTED
Definition: htup_details.h:207
#define HeapTupleHeaderGetSpeculativeToken(tup)
Definition: htup_details.h:433
#define HEAP_XMIN_INVALID
Definition: htup_details.h:205
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:304
#define HeapTupleHeaderXminFrozen(tup)
Definition: htup_details.h:331
#define HEAP_XMAX_INVALID
Definition: htup_details.h:208
#define HeapTupleHeaderXminCommitted(tup)
Definition: htup_details.h:320
#define HeapTupleHeaderGetRawXmax(tup)
Definition: htup_details.h:371
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:361
#define HeapTupleHeaderGetRawCommandId(tup)
Definition: htup_details.h:387
#define HEAP_LOCKED_UPGRADED(infomask)
Definition: htup_details.h:249
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:428
#define HeapTupleHeaderXminInvalid(tup)
Definition: htup_details.h:325
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:36
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:1800
HTAB * HistoricSnapshotGetTupleCids(void)
Definition: snapmgr.c:1626
@ 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
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:79
@ TM_Ok
Definition: tableam.h:84
@ TM_BeingModified
Definition: tableam.h:106
@ TM_Deleted
Definition: tableam.h:99
@ TM_Updated
Definition: tableam.h:96
@ TM_SelfModified
Definition: tableam.h:90
@ TM_Invisible
Definition: tableam.h:87
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:940
int xidComparator(const void *arg1, const void *arg2)
Definition: xid.c:152
bool XLogNeedsFlush(XLogRecPtr record)
Definition: xlog.c:3133
uint64 XLogRecPtr
Definition: xlogdefs.h:21