PostgreSQL Source Code git master
Loading...
Searching...
No Matches
heapam_visibility.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * heapam_visibility.c
4 * Tuple visibility rules for tuples stored in heap.
5 *
6 * NOTE: all the HeapTupleSatisfies routines will update the tuple's
7 * "hint" status bits if we see that the inserting or deleting transaction
8 * has now committed or aborted (and it is safe to set the hint bits).
9 * If the hint bits are changed, MarkBufferDirtyHint is called on
10 * the passed-in buffer. The caller must hold not only a pin, but at least
11 * shared buffer content lock on the buffer containing the tuple.
12 *
13 * NOTE: When using a non-MVCC snapshot, we must check
14 * TransactionIdIsInProgress (which looks in the PGPROC array) before
15 * TransactionIdDidCommit (which look in pg_xact). Otherwise we have a race
16 * condition: we might decide that a just-committed transaction crashed,
17 * because none of the tests succeed. xact.c is careful to record
18 * commit/abort in pg_xact before it unsets MyProc->xid in the PGPROC array.
19 * That fixes that problem, but it also means there is a window where
20 * TransactionIdIsInProgress and TransactionIdDidCommit will both return true.
21 * If we check only TransactionIdDidCommit, we could consider a tuple
22 * committed when a later GetSnapshotData call will still think the
23 * originating transaction is in progress, which leads to application-level
24 * inconsistency. The upshot is that we gotta check TransactionIdIsInProgress
25 * first in all code paths, except for a few cases where we are looking at
26 * subtransactions of our own main transaction and so there can't be any race
27 * condition.
28 *
29 * We can't use TransactionIdDidAbort here because it won't treat transactions
30 * that were in progress during a crash as aborted. We determine that
31 * transactions aborted/crashed through process of elimination instead.
32 *
33 * When using an MVCC snapshot, we rely on XidInMVCCSnapshot rather than
34 * TransactionIdIsInProgress, but the logic is otherwise the same: do not
35 * check pg_xact until after deciding that the xact is no longer in progress.
36 *
37 *
38 * Summary of visibility functions:
39 *
40 * HeapTupleSatisfiesMVCC()
41 * visible to supplied snapshot, excludes current command
42 * HeapTupleSatisfiesUpdate()
43 * visible to instant snapshot, with user-supplied command
44 * counter and more complex result
45 * HeapTupleSatisfiesSelf()
46 * visible to instant snapshot and current command
47 * HeapTupleSatisfiesDirty()
48 * like HeapTupleSatisfiesSelf(), but includes open transactions
49 * HeapTupleSatisfiesVacuum()
50 * visible to any running transaction, used by VACUUM
51 * HeapTupleSatisfiesNonVacuumable()
52 * Snapshot-style API for HeapTupleSatisfiesVacuum
53 * HeapTupleSatisfiesToast()
54 * visible unless part of interrupted vacuum, used for TOAST
55 * HeapTupleSatisfiesAny()
56 * all tuples are visible
57 *
58 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
59 * Portions Copyright (c) 1994, Regents of the University of California
60 *
61 * IDENTIFICATION
62 * src/backend/access/heap/heapam_visibility.c
63 *
64 *-------------------------------------------------------------------------
65 */
66
67#include "postgres.h"
68
69#include "access/heapam.h"
70#include "access/htup_details.h"
71#include "access/multixact.h"
72#include "access/tableam.h"
73#include "access/transam.h"
74#include "access/xact.h"
75#include "access/xlog.h"
76#include "storage/bufmgr.h"
77#include "storage/procarray.h"
78#include "utils/builtins.h"
79#include "utils/snapmgr.h"
80
81
82/*
83 * To be allowed to set hint bits, SetHintBits() needs to call
84 * BufferBeginSetHintBits(). However, that's not free, and some callsites call
85 * SetHintBits() on many tuples in a row. For those it makes sense to amortize
86 * the cost of BufferBeginSetHintBits(). Additionally it's desirable to defer
87 * the cost of BufferBeginSetHintBits() until a hint bit needs to actually be
88 * set. This enum serves as the necessary state space passed to
89 * SetHintBitsExt().
90 */
91typedef enum SetHintBitsState
92{
93 /* not yet checked if hint bits may be set */
95 /* failed to get permission to set hint bits, don't check again */
97 /* allowed to set hint bits */
100
101/*
102 * SetHintBitsExt()
103 *
104 * Set commit/abort hint bits on a tuple, if appropriate at this time.
105 *
106 * To be allowed to set a hint bit on a tuple, the page must not be undergoing
107 * IO at this time (otherwise we e.g. could corrupt PG's page checksum or even
108 * the filesystem's, as is known to happen with btrfs).
109 *
110 * The right to set a hint bit can be acquired on a page level with
111 * BufferBeginSetHintBits(). Only a single backend gets the right to set hint
112 * bits at a time. Alternatively, if called with a NULL SetHintBitsState*,
113 * hint bits are set with BufferSetHintBits16().
114 *
115 * It is only safe to set a transaction-committed hint bit if we know the
116 * transaction's commit record is guaranteed to be flushed to disk before the
117 * buffer, or if the table is temporary or unlogged and will be obliterated by
118 * a crash anyway. We cannot change the LSN of the page here, because we may
119 * hold only a share lock on the buffer, so we can only use the LSN to
120 * interlock this if the buffer's LSN already is newer than the commit LSN;
121 * otherwise we have to just refrain from setting the hint bit until some
122 * future re-examination of the tuple.
123 *
124 * We can always set hint bits when marking a transaction aborted. (Some
125 * code in heapam.c relies on that!)
126 *
127 * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
128 * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
129 * synchronous commits and didn't move tuples that weren't previously
130 * hinted. (This is not known by this subroutine, but is applied by its
131 * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this
132 * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we
133 * support in-place update from pre-9.0 databases.
134 *
135 * Normal commits may be asynchronous, so for those we need to get the LSN
136 * of the transaction and then check whether this is flushed.
137 *
138 * The caller should pass xid as the XID of the transaction to check, or
139 * InvalidTransactionId if no check is needed.
140 */
141static inline void
144{
145 /*
146 * In batched mode, if we previously did not get permission to set hint
147 * bits, don't try again - in all likelihood IO is still going on.
148 */
149 if (state && *state == SHB_DISABLED)
150 return;
151
152 if (TransactionIdIsValid(xid))
153 {
154 if (BufferIsPermanent(buffer))
155 {
156 /* NB: xid must be known committed here! */
158
161 {
162 /* not flushed and no LSN interlock, so don't set hint */
163 return;
164 }
165 }
166 }
167
168 /*
169 * If we're not operating in batch mode, use BufferSetHintBits16() to mark
170 * the page dirty, that's cheaper than
171 * BufferBeginSetHintBits()/BufferFinishSetHintBits(). That's important
172 * for cases where we set a lot of hint bits on a page individually.
173 */
174 if (!state)
175 {
177 tuple->t_infomask | infomask, buffer);
178 return;
179 }
180
181 if (*state == SHB_INITIAL)
182 {
183 if (!BufferBeginSetHintBits(buffer))
184 {
186 return;
187 }
188
190 }
191 tuple->t_infomask |= infomask;
192}
193
194/*
195 * Simple wrapper around SetHintBitExt(), use when operating on a single
196 * tuple.
197 */
198static inline void
201{
202 SetHintBitsExt(tuple, buffer, infomask, xid, NULL);
203}
204
205/*
206 * HeapTupleSetHintBits --- exported version of SetHintBits()
207 *
208 * This must be separate because of C99's brain-dead notions about how to
209 * implement inline functions.
210 */
211void
214{
215 /*
216 * The uses from heapam.c rely on being able to perform the hint bit
217 * updates, which can only be guaranteed if we are holding an exclusive
218 * lock on the buffer - which all callers are doing.
219 */
221
222 SetHintBits(tuple, buffer, infomask, xid);
223}
224
225/*
226 * If HEAP_MOVED_OFF or HEAP_MOVED_IN are set on the tuple, remove them and
227 * adjust hint bits. See the comment for SetHintBits() for more background.
228 *
229 * This helper returns false if the row ought to be invisible, true otherwise.
230 */
231static inline bool
233{
235
236 /* only used by pre-9.0 binary upgrades */
237 if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
238 return true;
239
241
243 elog(ERROR, "encountered tuple with HEAP_MOVED considered current");
244
246 elog(ERROR, "encountered tuple with HEAP_MOVED considered in-progress");
247
248 if (tuple->t_infomask & HEAP_MOVED_OFF)
249 {
251 {
252 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
254 return false;
255 }
256 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
258 }
259 else if (tuple->t_infomask & HEAP_MOVED_IN)
260 {
262 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
264 else
265 {
266 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
268 return false;
269 }
270 }
271
272 return true;
273}
274
275/*
276 * HeapTupleSatisfiesSelf
277 * True iff heap tuple is valid "for itself".
278 *
279 * See SNAPSHOT_MVCC's definition for the intended behaviour.
280 *
281 * Note:
282 * Assumes heap tuple is valid.
283 *
284 * The satisfaction of "itself" requires the following:
285 *
286 * ((Xmin == my-transaction && the row was updated by the current transaction, and
287 * (Xmax is null it was not deleted
288 * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
289 * ||
290 *
291 * (Xmin is committed && the row was modified by a committed transaction, and
292 * (Xmax is null || the row has not been deleted, or
293 * (Xmax != my-transaction && the row was deleted by another transaction
294 * Xmax is not committed))) that has not been committed
295 */
296static bool
298{
299 HeapTupleHeader tuple = htup->t_data;
300
302 Assert(htup->t_tableOid != InvalidOid);
303
305 {
307 return false;
308
309 if (!HeapTupleCleanMoved(tuple, buffer))
310 return false;
312 {
313 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
314 return true;
315
316 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
317 return true;
318
319 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
320 {
321 TransactionId xmax;
322
323 xmax = HeapTupleGetUpdateXid(tuple);
324
325 /* not LOCKED_ONLY, so it has to have an xmax */
327
328 /* updating subtransaction must have aborted */
330 return true;
331 else
332 return false;
333 }
334
336 {
337 /* deleting subtransaction must have aborted */
338 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
340 return true;
341 }
342
343 return false;
344 }
346 return false;
348 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
350 else
351 {
352 /* it must have aborted or crashed */
353 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
355 return false;
356 }
357 }
358
359 /* by here, the inserting transaction has committed */
360
361 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
362 return true;
363
364 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
365 {
367 return true;
368 return false; /* updated by other */
369 }
370
371 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
372 {
373 TransactionId xmax;
374
376 return true;
377
378 xmax = HeapTupleGetUpdateXid(tuple);
379
380 /* not LOCKED_ONLY, so it has to have an xmax */
382
384 return false;
386 return true;
387 if (TransactionIdDidCommit(xmax))
388 return false;
389 /* it must have aborted or crashed */
390 return true;
391 }
392
394 {
396 return true;
397 return false;
398 }
399
401 return true;
402
404 {
405 /* it must have aborted or crashed */
406 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
408 return true;
409 }
410
411 /* xmax transaction committed */
412
414 {
415 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
417 return true;
418 }
419
420 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
422 return false;
423}
424
425/*
426 * HeapTupleSatisfiesAny
427 * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
428 */
429static bool
431{
432 return true;
433}
434
435/*
436 * HeapTupleSatisfiesToast
437 * True iff heap tuple is valid as a TOAST row.
438 *
439 * See SNAPSHOT_TOAST's definition for the intended behaviour.
440 *
441 * This is a simplified version that only checks for VACUUM moving conditions.
442 * It's appropriate for TOAST usage because TOAST really doesn't want to do
443 * its own time qual checks; if you can see the main table row that contains
444 * a TOAST reference, you should be able to see the TOASTed value. However,
445 * vacuuming a TOAST table is independent of the main table, and in case such
446 * a vacuum fails partway through, we'd better do this much checking.
447 *
448 * Among other things, this means you can't do UPDATEs of rows in a TOAST
449 * table.
450 */
451static bool
453 Buffer buffer)
454{
455 HeapTupleHeader tuple = htup->t_data;
456
458 Assert(htup->t_tableOid != InvalidOid);
459
461 {
463 return false;
464
465 if (!HeapTupleCleanMoved(tuple, buffer))
466 return false;
467
468 /*
469 * An invalid Xmin can be left behind by a speculative insertion that
470 * is canceled by super-deleting the tuple. This also applies to
471 * TOAST tuples created during speculative insertion.
472 */
474 return false;
475 }
476
477 /* otherwise assume the tuple is valid for TOAST. */
478 return true;
479}
480
481/*
482 * HeapTupleSatisfiesUpdate
483 *
484 * This function returns a more detailed result code than most of the
485 * functions in this file, since UPDATE needs to know more than "is it
486 * visible?". It also allows for user-supplied CommandId rather than
487 * relying on CurrentCommandId.
488 *
489 * The possible return codes are:
490 *
491 * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
492 * was created by a later CommandId.
493 *
494 * TM_Ok: The tuple is valid and visible, so it may be updated.
495 *
496 * TM_SelfModified: The tuple was updated by the current transaction, after
497 * the current scan started.
498 *
499 * TM_Updated: The tuple was updated by a committed transaction (including
500 * the case where the tuple was moved into a different partition).
501 *
502 * TM_Deleted: The tuple was deleted by a committed transaction.
503 *
504 * TM_BeingModified: The tuple is being updated by an in-progress transaction
505 * other than the current transaction. (Note: this includes the case where
506 * the tuple is share-locked by a MultiXact, even if the MultiXact includes
507 * the current transaction. Callers that want to distinguish that case must
508 * test for it themselves.)
509 */
512 Buffer buffer)
513{
514 HeapTupleHeader tuple = htup->t_data;
515
517 Assert(htup->t_tableOid != InvalidOid);
518
520 {
522 return TM_Invisible;
523
524 else if (!HeapTupleCleanMoved(tuple, buffer))
525 return TM_Invisible;
527 {
528 if (HeapTupleHeaderGetCmin(tuple) >= curcid)
529 return TM_Invisible; /* inserted after scan started */
530
531 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
532 return TM_Ok;
533
535 {
536 TransactionId xmax;
537
538 xmax = HeapTupleHeaderGetRawXmax(tuple);
539
540 /*
541 * Careful here: even though this tuple was created by our own
542 * transaction, it might be locked by other transactions, if
543 * the original version was key-share locked when we updated
544 * it.
545 */
546
547 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
548 {
549 if (MultiXactIdIsRunning(xmax, true))
550 return TM_BeingModified;
551 else
552 return TM_Ok;
553 }
554
555 /*
556 * If the locker is gone, then there is nothing of interest
557 * left in this Xmax; otherwise, report the tuple as
558 * locked/updated.
559 */
560 if (!TransactionIdIsInProgress(xmax))
561 return TM_Ok;
562 return TM_BeingModified;
563 }
564
565 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
566 {
567 TransactionId xmax;
568
569 xmax = HeapTupleGetUpdateXid(tuple);
570
571 /* not LOCKED_ONLY, so it has to have an xmax */
573
574 /* deleting subtransaction must have aborted */
576 {
578 false))
579 return TM_BeingModified;
580 return TM_Ok;
581 }
582 else
583 {
584 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
585 return TM_SelfModified; /* updated after scan started */
586 else
587 return TM_Invisible; /* updated before scan started */
588 }
589 }
590
592 {
593 /* deleting subtransaction must have aborted */
594 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
596 return TM_Ok;
597 }
598
599 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
600 return TM_SelfModified; /* updated after scan started */
601 else
602 return TM_Invisible; /* updated before scan started */
603 }
605 return TM_Invisible;
607 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
609 else
610 {
611 /* it must have aborted or crashed */
612 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
614 return TM_Invisible;
615 }
616 }
617
618 /* by here, the inserting transaction has committed */
619
620 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
621 return TM_Ok;
622
623 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
624 {
626 return TM_Ok;
627 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
628 return TM_Updated; /* updated by other */
629 else
630 return TM_Deleted; /* deleted by other */
631 }
632
633 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
634 {
635 TransactionId xmax;
636
638 return TM_Ok;
639
641 {
643 return TM_BeingModified;
644
646 return TM_Ok;
647 }
648
649 xmax = HeapTupleGetUpdateXid(tuple);
650 if (!TransactionIdIsValid(xmax))
651 {
653 return TM_BeingModified;
654 }
655
656 /* not LOCKED_ONLY, so it has to have an xmax */
658
660 {
661 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
662 return TM_SelfModified; /* updated after scan started */
663 else
664 return TM_Invisible; /* updated before scan started */
665 }
666
668 return TM_BeingModified;
669
670 if (TransactionIdDidCommit(xmax))
671 {
672 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
673 return TM_Updated;
674 else
675 return TM_Deleted;
676 }
677
678 /*
679 * By here, the update in the Xmax is either aborted or crashed, but
680 * what about the other members?
681 */
682
684 {
685 /*
686 * There's no member, even just a locker, alive anymore, so we can
687 * mark the Xmax as invalid.
688 */
689 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
691 return TM_Ok;
692 }
693 else
694 {
695 /* There are lockers running */
696 return TM_BeingModified;
697 }
698 }
699
701 {
703 return TM_BeingModified;
704 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
705 return TM_SelfModified; /* updated after scan started */
706 else
707 return TM_Invisible; /* updated before scan started */
708 }
709
711 return TM_BeingModified;
712
714 {
715 /* it must have aborted or crashed */
716 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
718 return TM_Ok;
719 }
720
721 /* xmax transaction committed */
722
724 {
725 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
727 return TM_Ok;
728 }
729
730 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
732 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
733 return TM_Updated; /* updated by other */
734 else
735 return TM_Deleted; /* deleted by other */
736}
737
738/*
739 * HeapTupleSatisfiesDirty
740 * True iff heap tuple is valid including effects of open transactions.
741 *
742 * See SNAPSHOT_DIRTY's definition for the intended behaviour.
743 *
744 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
745 * the current transaction and committed/aborted xacts are concerned.
746 * However, we also include the effects of other xacts still in progress.
747 *
748 * A special hack is that the passed-in snapshot struct is used as an
749 * output argument to return the xids of concurrent xacts that affected the
750 * tuple. snapshot->xmin is set to the tuple's xmin if that is another
751 * transaction that's still in progress; or to InvalidTransactionId if the
752 * tuple's xmin is committed good, committed dead, or my own xact.
753 * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
754 * inserted speculatively, meaning that the inserter might still back down
755 * on the insertion without aborting the whole transaction, the associated
756 * token is also returned in snapshot->speculativeToken.
757 */
758static bool
760 Buffer buffer)
761{
762 HeapTupleHeader tuple = htup->t_data;
763
765 Assert(htup->t_tableOid != InvalidOid);
766
767 snapshot->xmin = snapshot->xmax = InvalidTransactionId;
768 snapshot->speculativeToken = 0;
769
771 {
773 return false;
774
775 if (!HeapTupleCleanMoved(tuple, buffer))
776 return false;
778 {
779 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
780 return true;
781
782 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
783 return true;
784
785 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
786 {
787 TransactionId xmax;
788
789 xmax = HeapTupleGetUpdateXid(tuple);
790
791 /* not LOCKED_ONLY, so it has to have an xmax */
793
794 /* updating subtransaction must have aborted */
796 return true;
797 else
798 return false;
799 }
800
802 {
803 /* deleting subtransaction must have aborted */
804 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
806 return true;
807 }
808
809 return false;
810 }
812 {
813 /*
814 * Return the speculative token to caller. Caller can worry about
815 * xmax, since it requires a conclusively locked row version, and
816 * a concurrent update to this tuple is a conflict of its
817 * purposes.
818 */
820 {
821 snapshot->speculativeToken =
823
824 Assert(snapshot->speculativeToken != 0);
825 }
826
827 snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
828 /* XXX shouldn't we fall through to look at xmax? */
829 return true; /* in insertion by other */
830 }
832 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
834 else
835 {
836 /* it must have aborted or crashed */
837 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
839 return false;
840 }
841 }
842
843 /* by here, the inserting transaction has committed */
844
845 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
846 return true;
847
848 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
849 {
851 return true;
852 return false; /* updated by other */
853 }
854
855 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
856 {
857 TransactionId xmax;
858
860 return true;
861
862 xmax = HeapTupleGetUpdateXid(tuple);
863
864 /* not LOCKED_ONLY, so it has to have an xmax */
866
868 return false;
870 {
871 snapshot->xmax = xmax;
872 return true;
873 }
874 if (TransactionIdDidCommit(xmax))
875 return false;
876 /* it must have aborted or crashed */
877 return true;
878 }
879
881 {
883 return true;
884 return false;
885 }
886
888 {
890 snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
891 return true;
892 }
893
895 {
896 /* it must have aborted or crashed */
897 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
899 return true;
900 }
901
902 /* xmax transaction committed */
903
905 {
906 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
908 return true;
909 }
910
911 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
913 return false; /* updated by other */
914}
915
916/*
917 * HeapTupleSatisfiesMVCC
918 * True iff heap tuple is valid for the given MVCC snapshot.
919 *
920 * See SNAPSHOT_MVCC's definition for the intended behaviour.
921 *
922 * Notice that here, we will not update the tuple status hint bits if the
923 * inserting/deleting transaction is still running according to our snapshot,
924 * even if in reality it's committed or aborted by now. This is intentional.
925 * Checking the true transaction state would require access to high-traffic
926 * shared data structures, creating contention we'd rather do without, and it
927 * would not change the result of our visibility check anyway. The hint bits
928 * will be updated by the first visitor that has a snapshot new enough to see
929 * the inserting/deleting transaction as done. In the meantime, the cost of
930 * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
931 * call will need to run TransactionIdIsCurrentTransactionId in addition to
932 * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
933 * coding where we tried to set the hint bits as soon as possible, we instead
934 * did TransactionIdIsInProgress in each call --- to no avail, as long as the
935 * inserting/deleting transaction was still running --- which was more cycles
936 * and more contention on ProcArrayLock.
937 */
938static inline bool
941{
942 HeapTupleHeader tuple = htup->t_data;
943
944 /*
945 * Assert that the caller has registered the snapshot. This function
946 * doesn't care about the registration as such, but in general you
947 * shouldn't try to use a snapshot without registration because it might
948 * get invalidated while it's still in use, and this is a convenient place
949 * to check for that.
950 */
951 Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
952
954 Assert(htup->t_tableOid != InvalidOid);
955
957 {
959 return false;
960
961 if (!HeapTupleCleanMoved(tuple, buffer))
962 return false;
964 {
965 if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
966 return false; /* inserted after scan started */
967
968 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
969 return true;
970
971 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
972 return true;
973
974 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
975 {
976 TransactionId xmax;
977
978 xmax = HeapTupleGetUpdateXid(tuple);
979
980 /* not LOCKED_ONLY, so it has to have an xmax */
982
983 /* updating subtransaction must have aborted */
985 return true;
986 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
987 return true; /* updated after scan started */
988 else
989 return false; /* updated before scan started */
990 }
991
993 {
994 /* deleting subtransaction must have aborted */
995 SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
997 return true;
998 }
999
1000 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1001 return true; /* deleted after scan started */
1002 else
1003 return false; /* deleted before scan started */
1004 }
1005 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
1006 return false;
1008 SetHintBitsExt(tuple, buffer, HEAP_XMIN_COMMITTED,
1010 else
1011 {
1012 /* it must have aborted or crashed */
1013 SetHintBitsExt(tuple, buffer, HEAP_XMIN_INVALID,
1015 return false;
1016 }
1017 }
1018 else
1019 {
1020 /* xmin is committed, but maybe not according to our snapshot */
1021 if (!HeapTupleHeaderXminFrozen(tuple) &&
1023 return false; /* treat as still in progress */
1024 }
1025
1026 /* by here, the inserting transaction has committed */
1027
1028 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
1029 return true;
1030
1032 return true;
1033
1034 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1035 {
1036 TransactionId xmax;
1037
1038 /* already checked above */
1040
1041 xmax = HeapTupleGetUpdateXid(tuple);
1042
1043 /* not LOCKED_ONLY, so it has to have an xmax */
1045
1047 {
1048 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1049 return true; /* deleted after scan started */
1050 else
1051 return false; /* deleted before scan started */
1052 }
1053 if (XidInMVCCSnapshot(xmax, snapshot))
1054 return true;
1055 if (TransactionIdDidCommit(xmax))
1056 return false; /* updating transaction committed */
1057 /* it must have aborted or crashed */
1058 return true;
1059 }
1060
1061 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1062 {
1064 {
1065 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
1066 return true; /* deleted after scan started */
1067 else
1068 return false; /* deleted before scan started */
1069 }
1070
1071 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1072 return true;
1073
1075 {
1076 /* it must have aborted or crashed */
1077 SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
1079 return true;
1080 }
1081
1082 /* xmax transaction committed */
1083 SetHintBitsExt(tuple, buffer, HEAP_XMAX_COMMITTED,
1085 }
1086 else
1087 {
1088 /* xmax is committed, but maybe not according to our snapshot */
1089 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1090 return true; /* treat as still in progress */
1091 }
1092
1093 /* xmax transaction committed */
1094
1095 return false;
1096}
1097
1098
1099/*
1100 * HeapTupleSatisfiesVacuum
1101 *
1102 * Determine the status of tuples for VACUUM purposes. Here, what
1103 * we mainly want to know is if a tuple is potentially visible to *any*
1104 * running transaction. If so, it can't be removed yet by VACUUM.
1105 *
1106 * OldestXmin is a cutoff XID (obtained from
1107 * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1108 * OldestXmin are deemed "recently dead"; they might still be visible to some
1109 * open transaction, so we can't remove them, even if we see that the deleting
1110 * transaction has committed.
1111 */
1114 Buffer buffer)
1115{
1117 HTSV_Result res;
1118
1119 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1120
1121 if (res == HEAPTUPLE_RECENTLY_DEAD)
1122 {
1124
1125 if (TransactionIdPrecedes(dead_after, OldestXmin))
1126 res = HEAPTUPLE_DEAD;
1127 }
1128 else
1130
1131 return res;
1132}
1133
1134/*
1135 * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1136 *
1137 * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1138 * tuple that could still be visible to some backend, stores the xid that
1139 * needs to be compared with the horizon in *dead_after, and returns
1140 * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1141 * the horizon. This is e.g. useful when comparing with different horizons.
1142 *
1143 * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1144 * transaction aborted.
1145 */
1148{
1149 HeapTupleHeader tuple = htup->t_data;
1150
1152 Assert(htup->t_tableOid != InvalidOid);
1154
1156
1157 /*
1158 * Has inserting transaction committed?
1159 *
1160 * If the inserting transaction aborted, then the tuple was never visible
1161 * to any other transaction, so we can delete it immediately.
1162 */
1163 if (!HeapTupleHeaderXminCommitted(tuple))
1164 {
1165 if (HeapTupleHeaderXminInvalid(tuple))
1166 return HEAPTUPLE_DEAD;
1167 else if (!HeapTupleCleanMoved(tuple, buffer))
1168 return HEAPTUPLE_DEAD;
1170 {
1171 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1173 /* only locked? run infomask-only check first, for performance */
1177 /* inserted and then deleted by same xact */
1180 /* deleting subtransaction must have aborted */
1182 }
1184 {
1185 /*
1186 * It'd be possible to discern between INSERT/DELETE in progress
1187 * here by looking at xmax - but that doesn't seem beneficial for
1188 * the majority of callers and even detrimental for some. We'd
1189 * rather have callers look at/wait for xmin than xmax. It's
1190 * always correct to return INSERT_IN_PROGRESS because that's
1191 * what's happening from the view of other backends.
1192 */
1194 }
1196 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1198 else
1199 {
1200 /*
1201 * Not in Progress, Not Committed, so either Aborted or crashed
1202 */
1203 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1205 return HEAPTUPLE_DEAD;
1206 }
1207
1208 /*
1209 * At this point the xmin is known committed, but we might not have
1210 * been able to set the hint bit yet; so we can no longer Assert that
1211 * it's set.
1212 */
1213 }
1214
1215 /*
1216 * Okay, the inserter committed, so it was good at some point. Now what
1217 * about the deleting transaction?
1218 */
1219 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1220 return HEAPTUPLE_LIVE;
1221
1223 {
1224 /*
1225 * "Deleting" xact really only locked it, so the tuple is live in any
1226 * case. However, we should make sure that either XMAX_COMMITTED or
1227 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1228 * examining the tuple for future xacts.
1229 */
1230 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1231 {
1232 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1233 {
1234 /*
1235 * If it's a pre-pg_upgrade tuple, the multixact cannot
1236 * possibly be running; otherwise have to check.
1237 */
1238 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1240 true))
1241 return HEAPTUPLE_LIVE;
1243 }
1244 else
1245 {
1247 return HEAPTUPLE_LIVE;
1248 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1250 }
1251 }
1252
1253 /*
1254 * We don't really care whether xmax did commit, abort or crash. We
1255 * know that xmax did lock the tuple, but it did not and will never
1256 * actually update it.
1257 */
1258
1259 return HEAPTUPLE_LIVE;
1260 }
1261
1262 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1263 {
1265
1266 /* already checked above */
1268
1269 /* not LOCKED_ONLY, so it has to have an xmax */
1271
1272 if (TransactionIdIsInProgress(xmax))
1274 else if (TransactionIdDidCommit(xmax))
1275 {
1276 /*
1277 * The multixact might still be running due to lockers. Need to
1278 * allow for pruning if below the xid horizon regardless --
1279 * otherwise we could end up with a tuple where the updater has to
1280 * be removed due to the horizon, but is not pruned away. It's
1281 * not a problem to prune that tuple, because any remaining
1282 * lockers will also be present in newer tuple versions.
1283 */
1284 *dead_after = xmax;
1286 }
1287 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1288 {
1289 /*
1290 * Not in Progress, Not Committed, so either Aborted or crashed.
1291 * Mark the Xmax as invalid.
1292 */
1294 }
1295
1296 return HEAPTUPLE_LIVE;
1297 }
1298
1299 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1300 {
1304 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1306 else
1307 {
1308 /*
1309 * Not in Progress, Not Committed, so either Aborted or crashed
1310 */
1311 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1313 return HEAPTUPLE_LIVE;
1314 }
1315
1316 /*
1317 * At this point the xmax is known committed, but we might not have
1318 * been able to set the hint bit yet; so we can no longer Assert that
1319 * it's set.
1320 */
1321 }
1322
1323 /*
1324 * Deleter committed, allow caller to check if it was recent enough that
1325 * some open transactions could still see the tuple.
1326 */
1329}
1330
1331
1332/*
1333 * HeapTupleSatisfiesNonVacuumable
1334 *
1335 * True if tuple might be visible to some transaction; false if it's
1336 * surely dead to everyone, ie, vacuumable.
1337 *
1338 * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1339 *
1340 * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1341 * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1342 * snapshot->vistest must have been set up with the horizon to use.
1343 */
1344static bool
1346 Buffer buffer)
1347{
1349 HTSV_Result res;
1350
1351 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1352
1353 if (res == HEAPTUPLE_RECENTLY_DEAD)
1354 {
1356
1358 res = HEAPTUPLE_DEAD;
1359 }
1360 else
1362
1363 return res != HEAPTUPLE_DEAD;
1364}
1365
1366
1367/*
1368 * HeapTupleIsSurelyDead
1369 *
1370 * Cheaply determine whether a tuple is surely dead to all onlookers.
1371 * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1372 * tuple has just been tested by another visibility routine (usually
1373 * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1374 * should already be set. We assume that if no hint bits are set, the xmin
1375 * or xmax transaction is still running. This is therefore faster than
1376 * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1377 * It's okay to return false when in doubt, but we must return true only
1378 * if the tuple is removable.
1379 */
1380bool
1382{
1383 HeapTupleHeader tuple = htup->t_data;
1384
1386 Assert(htup->t_tableOid != InvalidOid);
1387
1388 /*
1389 * If the inserting transaction is marked invalid, then it aborted, and
1390 * the tuple is definitely dead. If it's marked neither committed nor
1391 * invalid, then we assume it's still alive (since the presumption is that
1392 * all relevant hint bits were just set moments ago).
1393 */
1394 if (!HeapTupleHeaderXminCommitted(tuple))
1395 return HeapTupleHeaderXminInvalid(tuple);
1396
1397 /*
1398 * If the inserting transaction committed, but any deleting transaction
1399 * aborted, the tuple is still alive.
1400 */
1401 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1402 return false;
1403
1404 /*
1405 * If the XMAX is just a lock, the tuple is still alive.
1406 */
1408 return false;
1409
1410 /*
1411 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1412 * know without checking pg_multixact.
1413 */
1414 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1415 return false;
1416
1417 /* If deleter isn't known to have committed, assume it's still running. */
1418 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1419 return false;
1420
1421 /* Deleter committed, so tuple is dead if the XID is old enough. */
1422 return GlobalVisTestIsRemovableXid(vistest,
1424}
1425
1426/*
1427 * Is the tuple really only locked? That is, is it not updated?
1428 *
1429 * It's easy to check just infomask bits if the locker is not a multi; but
1430 * otherwise we need to verify that the updating transaction has not aborted.
1431 *
1432 * This function is here because it follows the same visibility rules laid out
1433 * at the top of this file.
1434 */
1435bool
1437{
1438 TransactionId xmax;
1439
1440 /* if there's no valid Xmax, then there's obviously no update either */
1441 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1442 return true;
1443
1444 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1445 return true;
1446
1447 /* invalid xmax means no update */
1449 return true;
1450
1451 /*
1452 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1453 * necessarily have been updated
1454 */
1455 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1456 return false;
1457
1458 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1459 xmax = HeapTupleGetUpdateXid(tuple);
1460
1461 /* not LOCKED_ONLY, so it has to have an xmax */
1463
1465 return false;
1466 if (TransactionIdIsInProgress(xmax))
1467 return false;
1468 if (TransactionIdDidCommit(xmax))
1469 return false;
1470
1471 /*
1472 * not current, not in progress, not committed -- must have aborted or
1473 * crashed
1474 */
1475 return true;
1476}
1477
1478/*
1479 * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1480 */
1481static bool
1483{
1484 return num > 0 &&
1485 bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1486}
1487
1488/*
1489 * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1490 * obeys.
1491 *
1492 * Only usable on tuples from catalog tables!
1493 *
1494 * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1495 * reading catalog pages which couldn't have been created in an older version.
1496 *
1497 * We don't set any hint bits in here as it seems unlikely to be beneficial as
1498 * those should already be set by normal access and it seems to be too
1499 * dangerous to do so as the semantics of doing so during timetravel are more
1500 * complicated than when dealing "only" with the present.
1501 */
1502static bool
1504 Buffer buffer)
1505{
1506 HeapTupleHeader tuple = htup->t_data;
1509
1511 Assert(htup->t_tableOid != InvalidOid);
1512
1513 /* inserting transaction aborted */
1514 if (HeapTupleHeaderXminInvalid(tuple))
1515 {
1517 return false;
1518 }
1519 /* check if it's one of our txids, toplevel is also in there */
1520 else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1521 {
1522 bool resolved;
1525
1526 /*
1527 * another transaction might have (tried to) delete this tuple or
1528 * cmin/cmax was stored in a combo CID. So we need to lookup the
1529 * actual values externally.
1530 */
1532 htup, buffer,
1533 &cmin, &cmax);
1534
1535 /*
1536 * If we haven't resolved the combo CID to cmin/cmax, that means we
1537 * have not decoded the combo CID yet. That means the cmin is
1538 * definitely in the future, and we're not supposed to see the tuple
1539 * yet.
1540 *
1541 * XXX This only applies to decoding of in-progress transactions. In
1542 * regular logical decoding we only execute this code at commit time,
1543 * at which point we should have seen all relevant combo CIDs. So
1544 * ideally, we should error out in this case but in practice, this
1545 * won't happen. If we are too worried about this then we can add an
1546 * elog inside ResolveCminCmaxDuringDecoding.
1547 *
1548 * XXX For the streaming case, we can track the largest combo CID
1549 * assigned, and error out based on this (when unable to resolve combo
1550 * CID below that observed maximum value).
1551 */
1552 if (!resolved)
1553 return false;
1554
1555 Assert(cmin != InvalidCommandId);
1556
1557 if (cmin >= snapshot->curcid)
1558 return false; /* inserted after scan started */
1559 /* fall through */
1560 }
1561 /* committed before our xmin horizon. Do a normal visibility check. */
1562 else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1563 {
1565 !TransactionIdDidCommit(xmin)));
1566
1567 /* check for hint bit first, consult clog afterwards */
1568 if (!HeapTupleHeaderXminCommitted(tuple) &&
1570 return false;
1571 /* fall through */
1572 }
1573 /* beyond our xmax horizon, i.e. invisible */
1574 else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1575 {
1576 return false;
1577 }
1578 /* check if it's a committed transaction in [xmin, xmax) */
1579 else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1580 {
1581 /* fall through */
1582 }
1583
1584 /*
1585 * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1586 * invisible.
1587 */
1588 else
1589 {
1590 return false;
1591 }
1592
1593 /* at this point we know xmin is visible, go on to check xmax */
1594
1595 /* xid invalid or aborted */
1596 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1597 return true;
1598 /* locked tuples are always visible */
1599 else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1600 return true;
1601
1602 /*
1603 * We can see multis here if we're looking at user tables or if somebody
1604 * SELECT ... FOR SHARE/UPDATE a system table.
1605 */
1606 else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1607 {
1608 xmax = HeapTupleGetUpdateXid(tuple);
1609 }
1610
1611 /* check if it's one of our txids, toplevel is also in there */
1612 if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1613 {
1614 bool resolved;
1615 CommandId cmin;
1617
1618 /* Lookup actual cmin/cmax values */
1620 htup, buffer,
1621 &cmin, &cmax);
1622
1623 /*
1624 * If we haven't resolved the combo CID to cmin/cmax, that means we
1625 * have not decoded the combo CID yet. That means the cmax is
1626 * definitely in the future, and we're still supposed to see the
1627 * tuple.
1628 *
1629 * XXX This only applies to decoding of in-progress transactions. In
1630 * regular logical decoding we only execute this code at commit time,
1631 * at which point we should have seen all relevant combo CIDs. So
1632 * ideally, we should error out in this case but in practice, this
1633 * won't happen. If we are too worried about this then we can add an
1634 * elog inside ResolveCminCmaxDuringDecoding.
1635 *
1636 * XXX For the streaming case, we can track the largest combo CID
1637 * assigned, and error out based on this (when unable to resolve combo
1638 * CID below that observed maximum value).
1639 */
1640 if (!resolved || cmax == InvalidCommandId)
1641 return true;
1642
1643 if (cmax >= snapshot->curcid)
1644 return true; /* deleted after scan started */
1645 else
1646 return false; /* deleted before scan started */
1647 }
1648 /* below xmin horizon, normal transaction state is valid */
1649 else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1650 {
1652 !TransactionIdDidCommit(xmax)));
1653
1654 /* check hint bit first */
1655 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1656 return false;
1657
1658 /* check clog */
1659 return !TransactionIdDidCommit(xmax);
1660 }
1661 /* above xmax horizon, we cannot possibly see the deleting transaction */
1662 else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1663 return true;
1664 /* xmax is between [xmin, xmax), check known committed array */
1665 else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1666 return false;
1667 /* xmax is between [xmin, xmax), but known not to have committed yet */
1668 else
1669 return true;
1670}
1671
1672/*
1673 * Perform HeaptupleSatisfiesMVCC() on each passed in tuple. This is more
1674 * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
1675 *
1676 * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
1677 * visibility is stored in batchmvcc->visible[]. In addition,
1678 * ->vistuples_dense is set to contain the offsets of visible tuples.
1679 *
1680 * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
1681 * avoids a cross-translation-unit function call for each tuple, allows the
1682 * compiler to optimize across calls to HeapTupleSatisfiesMVCC and allows
1683 * setting hint bits more efficiently (see the one BufferFinishSetHintBits()
1684 * call below).
1685 *
1686 * Returns the number of visible tuples.
1687 */
1688int
1690 int ntups,
1693{
1694 int nvis = 0;
1696
1697 Assert(IsMVCCSnapshot(snapshot));
1698
1699 for (int i = 0; i < ntups; i++)
1700 {
1701 bool valid;
1702 HeapTuple tup = &batchmvcc->tuples[i];
1703
1704 valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
1705 batchmvcc->visible[i] = valid;
1706
1707 if (likely(valid))
1708 {
1709 vistuples_dense[nvis] = tup->t_self.ip_posid;
1710 nvis++;
1711 }
1712 }
1713
1714 if (state == SHB_ENABLED)
1715 BufferFinishSetHintBits(buffer, true, true);
1716
1717 return nvis;
1718}
1719
1720/*
1721 * HeapTupleSatisfiesVisibility
1722 * True iff heap tuple satisfies a time qual.
1723 *
1724 * Notes:
1725 * Assumes heap tuple is valid, and buffer at least share locked.
1726 *
1727 * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1728 * if so, the indicated buffer is marked dirty.
1729 */
1730bool
1732{
1733 switch (snapshot->snapshot_type)
1734 {
1735 case SNAPSHOT_MVCC:
1736 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
1737 case SNAPSHOT_SELF:
1738 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1739 case SNAPSHOT_ANY:
1740 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1741 case SNAPSHOT_TOAST:
1742 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1743 case SNAPSHOT_DIRTY:
1744 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1746 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1748 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1749 }
1750
1751 return false; /* keep compiler quiet */
1752}
int Buffer
Definition buf.h:23
bool BufferSetHintBits16(uint16 *ptr, uint16 val, Buffer buffer)
Definition bufmgr.c:6960
void BufferFinishSetHintBits(Buffer buffer, bool mark_dirty, bool buffer_std)
Definition bufmgr.c:6937
bool BufferIsLockedByMeInMode(Buffer buffer, BufferLockMode mode)
Definition bufmgr.c:3003
bool BufferIsPermanent(Buffer buffer)
Definition bufmgr.c:4596
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition bufmgr.c:4632
bool BufferBeginSetHintBits(Buffer buffer)
Definition bufmgr.c:6909
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:220
#define InvalidCommandId
Definition c.h:755
#define likely(x)
Definition c.h:431
#define Assert(condition)
Definition c.h:945
uint16_t uint16
Definition c.h:617
uint32 CommandId
Definition c.h:752
uint32 TransactionId
Definition c.h:738
size_t Size
Definition c.h:691
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition combocid.c:104
CommandId HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup)
Definition combocid.c:118
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
Definition heapam.c:7679
HTSV_Result
Definition heapam.h:137
@ HEAPTUPLE_RECENTLY_DEAD
Definition heapam.h:140
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition heapam.h:141
@ HEAPTUPLE_LIVE
Definition heapam.h:139
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition heapam.h:142
@ HEAPTUPLE_DEAD
Definition heapam.h:138
static void SetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer, SetHintBitsState *state)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
static bool HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
static bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
SetHintBitsState
@ SHB_ENABLED
@ SHB_DISABLED
@ SHB_INITIAL
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 void SetHintBitsExt(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid, SetHintBitsState *state)
int HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer, int ntups, BatchMVCCState *batchmvcc, OffsetNumber *vistuples_dense)
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
static bool HeapTupleHeaderXminFrozen(const HeapTupleHeaderData *tup)
#define HEAP_XMIN_COMMITTED
static bool HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
static bool HeapTupleHeaderXminInvalid(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetXvac(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_LOCK_ONLY
#define HEAP_MOVED_IN
static CommandId HeapTupleHeaderGetRawCommandId(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetRawXmax(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_IS_MULTI
#define HEAP_XMAX_COMMITTED
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
#define HEAP_XMIN_INVALID
static BlockNumber HeapTupleHeaderGetSpeculativeToken(const HeapTupleHeaderData *tup)
#define HEAP_XMAX_INVALID
static TransactionId HeapTupleHeaderGetRawXmin(const HeapTupleHeaderData *tup)
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
static bool HEAP_LOCKED_UPGRADED(uint16 infomask)
static bool HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
int i
Definition isn.c:77
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition itemptr.c:35
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
Definition multixact.c:511
uint16 OffsetNumber
Definition off.h:24
#define InvalidOid
static int fb(int x)
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition procarray.c:4271
bool TransactionIdIsInProgress(TransactionId xid)
Definition procarray.c:1401
bool ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
Definition snapmgr.c:1869
HTAB * HistoricSnapshotGetTupleCids(void)
Definition snapmgr.c:1698
#define IsMVCCSnapshot(snapshot)
Definition snapmgr.h:59
@ 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
TransactionId xmin
Definition snapshot.h:153
int32 subxcnt
Definition snapshot.h:177
uint32 regd_count
Definition snapshot.h:201
uint32 active_count
Definition snapshot.h:200
CommandId curcid
Definition snapshot.h:183
struct GlobalVisState * vistest
Definition snapshot.h:195
uint32 xcnt
Definition snapshot.h:165
TransactionId * subxip
Definition snapshot.h:176
TransactionId xmax
Definition snapshot.h:154
uint32 speculativeToken
Definition snapshot.h:189
SnapshotType snapshot_type
Definition snapshot.h:140
TransactionId * xip
Definition snapshot.h:164
TM_Result
Definition tableam.h:73
@ TM_Ok
Definition tableam.h:78
@ TM_BeingModified
Definition tableam.h:100
@ TM_Deleted
Definition tableam.h:93
@ TM_Updated
Definition tableam.h:90
@ TM_SelfModified
Definition tableam.h:84
@ TM_Invisible
Definition tableam.h:81
bool TransactionIdDidCommit(TransactionId transactionId)
Definition transam.c:126
XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid)
Definition transam.c:318
#define InvalidTransactionId
Definition transam.h:31
static bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition transam.h:312
#define TransactionIdIsValid(xid)
Definition transam.h:41
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition transam.h:263
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:943
int xidComparator(const void *arg1, const void *arg2)
Definition xid.c:152
bool XLogNeedsFlush(XLogRecPtr record)
Definition xlog.c:3129
uint64 XLogRecPtr
Definition xlogdefs.h:21