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 * 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
116{
117 if (TransactionIdIsValid(xid))
118 {
119 /* NB: xid must be known committed here! */
121
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
143{
144 /*
145 * The uses from heapam.c rely on being able to perform the hint bit
146 * updates, which can only be guaranteed if we are holding an exclusive
147 * lock on the buffer - which all callers are doing.
148 */
150
151 SetHintBits(tuple, buffer, infomask, xid);
152}
153
154/*
155 * If HEAP_MOVED_OFF or HEAP_MOVED_IN are set on the tuple, remove them and
156 * adjust hint bits. See the comment for SetHintBits() for more background.
157 *
158 * This helper returns false if the row ought to be invisible, true otherwise.
159 */
160static inline bool
162{
164
165 /* only used by pre-9.0 binary upgrades */
166 if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
167 return true;
168
170
172 elog(ERROR, "encountered tuple with HEAP_MOVED considered current");
173
175 elog(ERROR, "encountered tuple with HEAP_MOVED considered in-progress");
176
177 if (tuple->t_infomask & HEAP_MOVED_OFF)
178 {
180 {
181 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
183 return false;
184 }
185 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
187 }
188 else if (tuple->t_infomask & HEAP_MOVED_IN)
189 {
191 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
193 else
194 {
195 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
197 return false;
198 }
199 }
200
201 return true;
202}
203
204/*
205 * HeapTupleSatisfiesSelf
206 * True iff heap tuple is valid "for itself".
207 *
208 * See SNAPSHOT_MVCC's definition for the intended behaviour.
209 *
210 * Note:
211 * Assumes heap tuple is valid.
212 *
213 * The satisfaction of "itself" requires the following:
214 *
215 * ((Xmin == my-transaction && the row was updated by the current transaction, and
216 * (Xmax is null it was not deleted
217 * [|| Xmax != my-transaction)]) [or it was deleted by another transaction]
218 * ||
219 *
220 * (Xmin is committed && the row was modified by a committed transaction, and
221 * (Xmax is null || the row has not been deleted, or
222 * (Xmax != my-transaction && the row was deleted by another transaction
223 * Xmax is not committed))) that has not been committed
224 */
225static bool
227{
228 HeapTupleHeader tuple = htup->t_data;
229
231 Assert(htup->t_tableOid != InvalidOid);
232
234 {
236 return false;
237
238 if (!HeapTupleCleanMoved(tuple, buffer))
239 return false;
241 {
242 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
243 return true;
244
245 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
246 return true;
247
248 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
249 {
250 TransactionId xmax;
251
252 xmax = HeapTupleGetUpdateXid(tuple);
253
254 /* not LOCKED_ONLY, so it has to have an xmax */
256
257 /* updating subtransaction must have aborted */
259 return true;
260 else
261 return false;
262 }
263
265 {
266 /* deleting subtransaction must have aborted */
267 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
269 return true;
270 }
271
272 return false;
273 }
275 return false;
277 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
279 else
280 {
281 /* it must have aborted or crashed */
282 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
284 return false;
285 }
286 }
287
288 /* by here, the inserting transaction has committed */
289
290 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
291 return true;
292
293 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
294 {
296 return true;
297 return false; /* updated by other */
298 }
299
300 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
301 {
302 TransactionId xmax;
303
305 return true;
306
307 xmax = HeapTupleGetUpdateXid(tuple);
308
309 /* not LOCKED_ONLY, so it has to have an xmax */
311
313 return false;
315 return true;
316 if (TransactionIdDidCommit(xmax))
317 return false;
318 /* it must have aborted or crashed */
319 return true;
320 }
321
323 {
325 return true;
326 return false;
327 }
328
330 return true;
331
333 {
334 /* it must have aborted or crashed */
335 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
337 return true;
338 }
339
340 /* xmax transaction committed */
341
343 {
344 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
346 return true;
347 }
348
349 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
351 return false;
352}
353
354/*
355 * HeapTupleSatisfiesAny
356 * Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
357 */
358static bool
360{
361 return true;
362}
363
364/*
365 * HeapTupleSatisfiesToast
366 * True iff heap tuple is valid as a TOAST row.
367 *
368 * See SNAPSHOT_TOAST's definition for the intended behaviour.
369 *
370 * This is a simplified version that only checks for VACUUM moving conditions.
371 * It's appropriate for TOAST usage because TOAST really doesn't want to do
372 * its own time qual checks; if you can see the main table row that contains
373 * a TOAST reference, you should be able to see the TOASTed value. However,
374 * vacuuming a TOAST table is independent of the main table, and in case such
375 * a vacuum fails partway through, we'd better do this much checking.
376 *
377 * Among other things, this means you can't do UPDATEs of rows in a TOAST
378 * table.
379 */
380static bool
382 Buffer buffer)
383{
384 HeapTupleHeader tuple = htup->t_data;
385
387 Assert(htup->t_tableOid != InvalidOid);
388
390 {
392 return false;
393
394 if (!HeapTupleCleanMoved(tuple, buffer))
395 return false;
396
397 /*
398 * An invalid Xmin can be left behind by a speculative insertion that
399 * is canceled by super-deleting the tuple. This also applies to
400 * TOAST tuples created during speculative insertion.
401 */
403 return false;
404 }
405
406 /* otherwise assume the tuple is valid for TOAST. */
407 return true;
408}
409
410/*
411 * HeapTupleSatisfiesUpdate
412 *
413 * This function returns a more detailed result code than most of the
414 * functions in this file, since UPDATE needs to know more than "is it
415 * visible?". It also allows for user-supplied CommandId rather than
416 * relying on CurrentCommandId.
417 *
418 * The possible return codes are:
419 *
420 * TM_Invisible: the tuple didn't exist at all when the scan started, e.g. it
421 * was created by a later CommandId.
422 *
423 * TM_Ok: The tuple is valid and visible, so it may be updated.
424 *
425 * TM_SelfModified: The tuple was updated by the current transaction, after
426 * the current scan started.
427 *
428 * TM_Updated: The tuple was updated by a committed transaction (including
429 * the case where the tuple was moved into a different partition).
430 *
431 * TM_Deleted: The tuple was deleted by a committed transaction.
432 *
433 * TM_BeingModified: The tuple is being updated by an in-progress transaction
434 * other than the current transaction. (Note: this includes the case where
435 * the tuple is share-locked by a MultiXact, even if the MultiXact includes
436 * the current transaction. Callers that want to distinguish that case must
437 * test for it themselves.)
438 */
441 Buffer buffer)
442{
443 HeapTupleHeader tuple = htup->t_data;
444
446 Assert(htup->t_tableOid != InvalidOid);
447
449 {
451 return TM_Invisible;
452
453 else if (!HeapTupleCleanMoved(tuple, buffer))
454 return TM_Invisible;
456 {
457 if (HeapTupleHeaderGetCmin(tuple) >= curcid)
458 return TM_Invisible; /* inserted after scan started */
459
460 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
461 return TM_Ok;
462
464 {
465 TransactionId xmax;
466
467 xmax = HeapTupleHeaderGetRawXmax(tuple);
468
469 /*
470 * Careful here: even though this tuple was created by our own
471 * transaction, it might be locked by other transactions, if
472 * the original version was key-share locked when we updated
473 * it.
474 */
475
476 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
477 {
478 if (MultiXactIdIsRunning(xmax, true))
479 return TM_BeingModified;
480 else
481 return TM_Ok;
482 }
483
484 /*
485 * If the locker is gone, then there is nothing of interest
486 * left in this Xmax; otherwise, report the tuple as
487 * locked/updated.
488 */
489 if (!TransactionIdIsInProgress(xmax))
490 return TM_Ok;
491 return TM_BeingModified;
492 }
493
494 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
495 {
496 TransactionId xmax;
497
498 xmax = HeapTupleGetUpdateXid(tuple);
499
500 /* not LOCKED_ONLY, so it has to have an xmax */
502
503 /* deleting subtransaction must have aborted */
505 {
507 false))
508 return TM_BeingModified;
509 return TM_Ok;
510 }
511 else
512 {
513 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
514 return TM_SelfModified; /* updated after scan started */
515 else
516 return TM_Invisible; /* updated before scan started */
517 }
518 }
519
521 {
522 /* deleting subtransaction must have aborted */
523 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
525 return TM_Ok;
526 }
527
528 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
529 return TM_SelfModified; /* updated after scan started */
530 else
531 return TM_Invisible; /* updated before scan started */
532 }
534 return TM_Invisible;
536 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
538 else
539 {
540 /* it must have aborted or crashed */
541 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
543 return TM_Invisible;
544 }
545 }
546
547 /* by here, the inserting transaction has committed */
548
549 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
550 return TM_Ok;
551
552 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
553 {
555 return TM_Ok;
556 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
557 return TM_Updated; /* updated by other */
558 else
559 return TM_Deleted; /* deleted by other */
560 }
561
562 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
563 {
564 TransactionId xmax;
565
567 return TM_Ok;
568
570 {
572 return TM_BeingModified;
573
575 return TM_Ok;
576 }
577
578 xmax = HeapTupleGetUpdateXid(tuple);
579 if (!TransactionIdIsValid(xmax))
580 {
582 return TM_BeingModified;
583 }
584
585 /* not LOCKED_ONLY, so it has to have an xmax */
587
589 {
590 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
591 return TM_SelfModified; /* updated after scan started */
592 else
593 return TM_Invisible; /* updated before scan started */
594 }
595
597 return TM_BeingModified;
598
599 if (TransactionIdDidCommit(xmax))
600 {
601 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
602 return TM_Updated;
603 else
604 return TM_Deleted;
605 }
606
607 /*
608 * By here, the update in the Xmax is either aborted or crashed, but
609 * what about the other members?
610 */
611
613 {
614 /*
615 * There's no member, even just a locker, alive anymore, so we can
616 * mark the Xmax as invalid.
617 */
618 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
620 return TM_Ok;
621 }
622 else
623 {
624 /* There are lockers running */
625 return TM_BeingModified;
626 }
627 }
628
630 {
632 return TM_BeingModified;
633 if (HeapTupleHeaderGetCmax(tuple) >= curcid)
634 return TM_SelfModified; /* updated after scan started */
635 else
636 return TM_Invisible; /* updated before scan started */
637 }
638
640 return TM_BeingModified;
641
643 {
644 /* it must have aborted or crashed */
645 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
647 return TM_Ok;
648 }
649
650 /* xmax transaction committed */
651
653 {
654 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
656 return TM_Ok;
657 }
658
659 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
661 if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
662 return TM_Updated; /* updated by other */
663 else
664 return TM_Deleted; /* deleted by other */
665}
666
667/*
668 * HeapTupleSatisfiesDirty
669 * True iff heap tuple is valid including effects of open transactions.
670 *
671 * See SNAPSHOT_DIRTY's definition for the intended behaviour.
672 *
673 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
674 * the current transaction and committed/aborted xacts are concerned.
675 * However, we also include the effects of other xacts still in progress.
676 *
677 * A special hack is that the passed-in snapshot struct is used as an
678 * output argument to return the xids of concurrent xacts that affected the
679 * tuple. snapshot->xmin is set to the tuple's xmin if that is another
680 * transaction that's still in progress; or to InvalidTransactionId if the
681 * tuple's xmin is committed good, committed dead, or my own xact.
682 * Similarly for snapshot->xmax and the tuple's xmax. If the tuple was
683 * inserted speculatively, meaning that the inserter might still back down
684 * on the insertion without aborting the whole transaction, the associated
685 * token is also returned in snapshot->speculativeToken.
686 */
687static bool
689 Buffer buffer)
690{
691 HeapTupleHeader tuple = htup->t_data;
692
694 Assert(htup->t_tableOid != InvalidOid);
695
696 snapshot->xmin = snapshot->xmax = InvalidTransactionId;
697 snapshot->speculativeToken = 0;
698
700 {
702 return false;
703
704 if (!HeapTupleCleanMoved(tuple, buffer))
705 return false;
707 {
708 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
709 return true;
710
711 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
712 return true;
713
714 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
715 {
716 TransactionId xmax;
717
718 xmax = HeapTupleGetUpdateXid(tuple);
719
720 /* not LOCKED_ONLY, so it has to have an xmax */
722
723 /* updating subtransaction must have aborted */
725 return true;
726 else
727 return false;
728 }
729
731 {
732 /* deleting subtransaction must have aborted */
733 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
735 return true;
736 }
737
738 return false;
739 }
741 {
742 /*
743 * Return the speculative token to caller. Caller can worry about
744 * xmax, since it requires a conclusively locked row version, and
745 * a concurrent update to this tuple is a conflict of its
746 * purposes.
747 */
749 {
750 snapshot->speculativeToken =
752
753 Assert(snapshot->speculativeToken != 0);
754 }
755
756 snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
757 /* XXX shouldn't we fall through to look at xmax? */
758 return true; /* in insertion by other */
759 }
761 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
763 else
764 {
765 /* it must have aborted or crashed */
766 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
768 return false;
769 }
770 }
771
772 /* by here, the inserting transaction has committed */
773
774 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
775 return true;
776
777 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
778 {
780 return true;
781 return false; /* updated by other */
782 }
783
784 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
785 {
786 TransactionId xmax;
787
789 return true;
790
791 xmax = HeapTupleGetUpdateXid(tuple);
792
793 /* not LOCKED_ONLY, so it has to have an xmax */
795
797 return false;
799 {
800 snapshot->xmax = xmax;
801 return true;
802 }
803 if (TransactionIdDidCommit(xmax))
804 return false;
805 /* it must have aborted or crashed */
806 return true;
807 }
808
810 {
812 return true;
813 return false;
814 }
815
817 {
819 snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
820 return true;
821 }
822
824 {
825 /* it must have aborted or crashed */
826 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
828 return true;
829 }
830
831 /* xmax transaction committed */
832
834 {
835 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
837 return true;
838 }
839
840 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
842 return false; /* updated by other */
843}
844
845/*
846 * HeapTupleSatisfiesMVCC
847 * True iff heap tuple is valid for the given MVCC snapshot.
848 *
849 * See SNAPSHOT_MVCC's definition for the intended behaviour.
850 *
851 * Notice that here, we will not update the tuple status hint bits if the
852 * inserting/deleting transaction is still running according to our snapshot,
853 * even if in reality it's committed or aborted by now. This is intentional.
854 * Checking the true transaction state would require access to high-traffic
855 * shared data structures, creating contention we'd rather do without, and it
856 * would not change the result of our visibility check anyway. The hint bits
857 * will be updated by the first visitor that has a snapshot new enough to see
858 * the inserting/deleting transaction as done. In the meantime, the cost of
859 * leaving the hint bits unset is basically that each HeapTupleSatisfiesMVCC
860 * call will need to run TransactionIdIsCurrentTransactionId in addition to
861 * XidInMVCCSnapshot (but it would have to do the latter anyway). In the old
862 * coding where we tried to set the hint bits as soon as possible, we instead
863 * did TransactionIdIsInProgress in each call --- to no avail, as long as the
864 * inserting/deleting transaction was still running --- which was more cycles
865 * and more contention on ProcArrayLock.
866 */
867static bool
869 Buffer buffer)
870{
871 HeapTupleHeader tuple = htup->t_data;
872
873 /*
874 * Assert that the caller has registered the snapshot. This function
875 * doesn't care about the registration as such, but in general you
876 * shouldn't try to use a snapshot without registration because it might
877 * get invalidated while it's still in use, and this is a convenient place
878 * to check for that.
879 */
880 Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);
881
883 Assert(htup->t_tableOid != InvalidOid);
884
886 {
888 return false;
889
890 if (!HeapTupleCleanMoved(tuple, buffer))
891 return false;
893 {
894 if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
895 return false; /* inserted after scan started */
896
897 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
898 return true;
899
900 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
901 return true;
902
903 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
904 {
905 TransactionId xmax;
906
907 xmax = HeapTupleGetUpdateXid(tuple);
908
909 /* not LOCKED_ONLY, so it has to have an xmax */
911
912 /* updating subtransaction must have aborted */
914 return true;
915 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
916 return true; /* updated after scan started */
917 else
918 return false; /* updated before scan started */
919 }
920
922 {
923 /* deleting subtransaction must have aborted */
924 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
926 return true;
927 }
928
929 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
930 return true; /* deleted after scan started */
931 else
932 return false; /* deleted before scan started */
933 }
934 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
935 return false;
937 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
939 else
940 {
941 /* it must have aborted or crashed */
942 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
944 return false;
945 }
946 }
947 else
948 {
949 /* xmin is committed, but maybe not according to our snapshot */
950 if (!HeapTupleHeaderXminFrozen(tuple) &&
952 return false; /* treat as still in progress */
953 }
954
955 /* by here, the inserting transaction has committed */
956
957 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
958 return true;
959
961 return true;
962
963 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
964 {
965 TransactionId xmax;
966
967 /* already checked above */
969
970 xmax = HeapTupleGetUpdateXid(tuple);
971
972 /* not LOCKED_ONLY, so it has to have an xmax */
974
976 {
977 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
978 return true; /* deleted after scan started */
979 else
980 return false; /* deleted before scan started */
981 }
982 if (XidInMVCCSnapshot(xmax, snapshot))
983 return true;
984 if (TransactionIdDidCommit(xmax))
985 return false; /* updating transaction committed */
986 /* it must have aborted or crashed */
987 return true;
988 }
989
990 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
991 {
993 {
994 if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
995 return true; /* deleted after scan started */
996 else
997 return false; /* deleted before scan started */
998 }
999
1000 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1001 return true;
1002
1004 {
1005 /* it must have aborted or crashed */
1006 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1008 return true;
1009 }
1010
1011 /* xmax transaction committed */
1012 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1014 }
1015 else
1016 {
1017 /* xmax is committed, but maybe not according to our snapshot */
1018 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
1019 return true; /* treat as still in progress */
1020 }
1021
1022 /* xmax transaction committed */
1023
1024 return false;
1025}
1026
1027
1028/*
1029 * HeapTupleSatisfiesVacuum
1030 *
1031 * Determine the status of tuples for VACUUM purposes. Here, what
1032 * we mainly want to know is if a tuple is potentially visible to *any*
1033 * running transaction. If so, it can't be removed yet by VACUUM.
1034 *
1035 * OldestXmin is a cutoff XID (obtained from
1036 * GetOldestNonRemovableTransactionId()). Tuples deleted by XIDs >=
1037 * OldestXmin are deemed "recently dead"; they might still be visible to some
1038 * open transaction, so we can't remove them, even if we see that the deleting
1039 * transaction has committed.
1040 */
1043 Buffer buffer)
1044{
1046 HTSV_Result res;
1047
1048 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1049
1050 if (res == HEAPTUPLE_RECENTLY_DEAD)
1051 {
1053
1054 if (TransactionIdPrecedes(dead_after, OldestXmin))
1055 res = HEAPTUPLE_DEAD;
1056 }
1057 else
1059
1060 return res;
1061}
1062
1063/*
1064 * Work horse for HeapTupleSatisfiesVacuum and similar routines.
1065 *
1066 * In contrast to HeapTupleSatisfiesVacuum this routine, when encountering a
1067 * tuple that could still be visible to some backend, stores the xid that
1068 * needs to be compared with the horizon in *dead_after, and returns
1069 * HEAPTUPLE_RECENTLY_DEAD. The caller then can perform the comparison with
1070 * the horizon. This is e.g. useful when comparing with different horizons.
1071 *
1072 * Note: HEAPTUPLE_DEAD can still be returned here, e.g. if the inserting
1073 * transaction aborted.
1074 */
1077{
1078 HeapTupleHeader tuple = htup->t_data;
1079
1081 Assert(htup->t_tableOid != InvalidOid);
1083
1085
1086 /*
1087 * Has inserting transaction committed?
1088 *
1089 * If the inserting transaction aborted, then the tuple was never visible
1090 * to any other transaction, so we can delete it immediately.
1091 */
1092 if (!HeapTupleHeaderXminCommitted(tuple))
1093 {
1094 if (HeapTupleHeaderXminInvalid(tuple))
1095 return HEAPTUPLE_DEAD;
1096 else if (!HeapTupleCleanMoved(tuple, buffer))
1097 return HEAPTUPLE_DEAD;
1099 {
1100 if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
1102 /* only locked? run infomask-only check first, for performance */
1106 /* inserted and then deleted by same xact */
1109 /* deleting subtransaction must have aborted */
1111 }
1113 {
1114 /*
1115 * It'd be possible to discern between INSERT/DELETE in progress
1116 * here by looking at xmax - but that doesn't seem beneficial for
1117 * the majority of callers and even detrimental for some. We'd
1118 * rather have callers look at/wait for xmin than xmax. It's
1119 * always correct to return INSERT_IN_PROGRESS because that's
1120 * what's happening from the view of other backends.
1121 */
1123 }
1125 SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
1127 else
1128 {
1129 /*
1130 * Not in Progress, Not Committed, so either Aborted or crashed
1131 */
1132 SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
1134 return HEAPTUPLE_DEAD;
1135 }
1136
1137 /*
1138 * At this point the xmin is known committed, but we might not have
1139 * been able to set the hint bit yet; so we can no longer Assert that
1140 * it's set.
1141 */
1142 }
1143
1144 /*
1145 * Okay, the inserter committed, so it was good at some point. Now what
1146 * about the deleting transaction?
1147 */
1148 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1149 return HEAPTUPLE_LIVE;
1150
1152 {
1153 /*
1154 * "Deleting" xact really only locked it, so the tuple is live in any
1155 * case. However, we should make sure that either XMAX_COMMITTED or
1156 * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
1157 * examining the tuple for future xacts.
1158 */
1159 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1160 {
1161 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1162 {
1163 /*
1164 * If it's a pre-pg_upgrade tuple, the multixact cannot
1165 * possibly be running; otherwise have to check.
1166 */
1167 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
1169 true))
1170 return HEAPTUPLE_LIVE;
1172 }
1173 else
1174 {
1176 return HEAPTUPLE_LIVE;
1177 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1179 }
1180 }
1181
1182 /*
1183 * We don't really care whether xmax did commit, abort or crash. We
1184 * know that xmax did lock the tuple, but it did not and will never
1185 * actually update it.
1186 */
1187
1188 return HEAPTUPLE_LIVE;
1189 }
1190
1191 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1192 {
1194
1195 /* already checked above */
1197
1198 /* not LOCKED_ONLY, so it has to have an xmax */
1200
1201 if (TransactionIdIsInProgress(xmax))
1203 else if (TransactionIdDidCommit(xmax))
1204 {
1205 /*
1206 * The multixact might still be running due to lockers. Need to
1207 * allow for pruning if below the xid horizon regardless --
1208 * otherwise we could end up with a tuple where the updater has to
1209 * be removed due to the horizon, but is not pruned away. It's
1210 * not a problem to prune that tuple, because any remaining
1211 * lockers will also be present in newer tuple versions.
1212 */
1213 *dead_after = xmax;
1215 }
1216 else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
1217 {
1218 /*
1219 * Not in Progress, Not Committed, so either Aborted or crashed.
1220 * Mark the Xmax as invalid.
1221 */
1223 }
1224
1225 return HEAPTUPLE_LIVE;
1226 }
1227
1228 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1229 {
1233 SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1235 else
1236 {
1237 /*
1238 * Not in Progress, Not Committed, so either Aborted or crashed
1239 */
1240 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1242 return HEAPTUPLE_LIVE;
1243 }
1244
1245 /*
1246 * At this point the xmax is known committed, but we might not have
1247 * been able to set the hint bit yet; so we can no longer Assert that
1248 * it's set.
1249 */
1250 }
1251
1252 /*
1253 * Deleter committed, allow caller to check if it was recent enough that
1254 * some open transactions could still see the tuple.
1255 */
1258}
1259
1260
1261/*
1262 * HeapTupleSatisfiesNonVacuumable
1263 *
1264 * True if tuple might be visible to some transaction; false if it's
1265 * surely dead to everyone, ie, vacuumable.
1266 *
1267 * See SNAPSHOT_NON_VACUUMABLE's definition for the intended behaviour.
1268 *
1269 * This is an interface to HeapTupleSatisfiesVacuum that's callable via
1270 * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
1271 * snapshot->vistest must have been set up with the horizon to use.
1272 */
1273static bool
1275 Buffer buffer)
1276{
1278 HTSV_Result res;
1279
1280 res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
1281
1282 if (res == HEAPTUPLE_RECENTLY_DEAD)
1283 {
1285
1287 res = HEAPTUPLE_DEAD;
1288 }
1289 else
1291
1292 return res != HEAPTUPLE_DEAD;
1293}
1294
1295
1296/*
1297 * HeapTupleIsSurelyDead
1298 *
1299 * Cheaply determine whether a tuple is surely dead to all onlookers.
1300 * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the
1301 * tuple has just been tested by another visibility routine (usually
1302 * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set
1303 * should already be set. We assume that if no hint bits are set, the xmin
1304 * or xmax transaction is still running. This is therefore faster than
1305 * HeapTupleSatisfiesVacuum, because we consult neither procarray nor CLOG.
1306 * It's okay to return false when in doubt, but we must return true only
1307 * if the tuple is removable.
1308 */
1309bool
1311{
1312 HeapTupleHeader tuple = htup->t_data;
1313
1315 Assert(htup->t_tableOid != InvalidOid);
1316
1317 /*
1318 * If the inserting transaction is marked invalid, then it aborted, and
1319 * the tuple is definitely dead. If it's marked neither committed nor
1320 * invalid, then we assume it's still alive (since the presumption is that
1321 * all relevant hint bits were just set moments ago).
1322 */
1323 if (!HeapTupleHeaderXminCommitted(tuple))
1324 return HeapTupleHeaderXminInvalid(tuple);
1325
1326 /*
1327 * If the inserting transaction committed, but any deleting transaction
1328 * aborted, the tuple is still alive.
1329 */
1330 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1331 return false;
1332
1333 /*
1334 * If the XMAX is just a lock, the tuple is still alive.
1335 */
1337 return false;
1338
1339 /*
1340 * If the Xmax is a MultiXact, it might be dead or alive, but we cannot
1341 * know without checking pg_multixact.
1342 */
1343 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1344 return false;
1345
1346 /* If deleter isn't known to have committed, assume it's still running. */
1347 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
1348 return false;
1349
1350 /* Deleter committed, so tuple is dead if the XID is old enough. */
1351 return GlobalVisTestIsRemovableXid(vistest,
1353}
1354
1355/*
1356 * Is the tuple really only locked? That is, is it not updated?
1357 *
1358 * It's easy to check just infomask bits if the locker is not a multi; but
1359 * otherwise we need to verify that the updating transaction has not aborted.
1360 *
1361 * This function is here because it follows the same visibility rules laid out
1362 * at the top of this file.
1363 */
1364bool
1366{
1367 TransactionId xmax;
1368
1369 /* if there's no valid Xmax, then there's obviously no update either */
1370 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1371 return true;
1372
1373 if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
1374 return true;
1375
1376 /* invalid xmax means no update */
1378 return true;
1379
1380 /*
1381 * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must
1382 * necessarily have been updated
1383 */
1384 if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
1385 return false;
1386
1387 /* ... but if it's a multi, then perhaps the updating Xid aborted. */
1388 xmax = HeapTupleGetUpdateXid(tuple);
1389
1390 /* not LOCKED_ONLY, so it has to have an xmax */
1392
1394 return false;
1395 if (TransactionIdIsInProgress(xmax))
1396 return false;
1397 if (TransactionIdDidCommit(xmax))
1398 return false;
1399
1400 /*
1401 * not current, not in progress, not committed -- must have aborted or
1402 * crashed
1403 */
1404 return true;
1405}
1406
1407/*
1408 * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
1409 */
1410static bool
1412{
1413 return num > 0 &&
1414 bsearch(&xid, xip, num, sizeof(TransactionId), xidComparator) != NULL;
1415}
1416
1417/*
1418 * See the comments for HeapTupleSatisfiesMVCC for the semantics this function
1419 * obeys.
1420 *
1421 * Only usable on tuples from catalog tables!
1422 *
1423 * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support
1424 * reading catalog pages which couldn't have been created in an older version.
1425 *
1426 * We don't set any hint bits in here as it seems unlikely to be beneficial as
1427 * those should already be set by normal access and it seems to be too
1428 * dangerous to do so as the semantics of doing so during timetravel are more
1429 * complicated than when dealing "only" with the present.
1430 */
1431static bool
1433 Buffer buffer)
1434{
1435 HeapTupleHeader tuple = htup->t_data;
1438
1440 Assert(htup->t_tableOid != InvalidOid);
1441
1442 /* inserting transaction aborted */
1443 if (HeapTupleHeaderXminInvalid(tuple))
1444 {
1446 return false;
1447 }
1448 /* check if it's one of our txids, toplevel is also in there */
1449 else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
1450 {
1451 bool resolved;
1454
1455 /*
1456 * another transaction might have (tried to) delete this tuple or
1457 * cmin/cmax was stored in a combo CID. So we need to lookup the
1458 * actual values externally.
1459 */
1461 htup, buffer,
1462 &cmin, &cmax);
1463
1464 /*
1465 * If we haven't resolved the combo CID to cmin/cmax, that means we
1466 * have not decoded the combo CID yet. That means the cmin is
1467 * definitely in the future, and we're not supposed to see the tuple
1468 * yet.
1469 *
1470 * XXX This only applies to decoding of in-progress transactions. In
1471 * regular logical decoding we only execute this code at commit time,
1472 * at which point we should have seen all relevant combo CIDs. So
1473 * ideally, we should error out in this case but in practice, this
1474 * won't happen. If we are too worried about this then we can add an
1475 * elog inside ResolveCminCmaxDuringDecoding.
1476 *
1477 * XXX For the streaming case, we can track the largest combo CID
1478 * assigned, and error out based on this (when unable to resolve combo
1479 * CID below that observed maximum value).
1480 */
1481 if (!resolved)
1482 return false;
1483
1484 Assert(cmin != InvalidCommandId);
1485
1486 if (cmin >= snapshot->curcid)
1487 return false; /* inserted after scan started */
1488 /* fall through */
1489 }
1490 /* committed before our xmin horizon. Do a normal visibility check. */
1491 else if (TransactionIdPrecedes(xmin, snapshot->xmin))
1492 {
1494 !TransactionIdDidCommit(xmin)));
1495
1496 /* check for hint bit first, consult clog afterwards */
1497 if (!HeapTupleHeaderXminCommitted(tuple) &&
1499 return false;
1500 /* fall through */
1501 }
1502 /* beyond our xmax horizon, i.e. invisible */
1503 else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
1504 {
1505 return false;
1506 }
1507 /* check if it's a committed transaction in [xmin, xmax) */
1508 else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
1509 {
1510 /* fall through */
1511 }
1512
1513 /*
1514 * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e.
1515 * invisible.
1516 */
1517 else
1518 {
1519 return false;
1520 }
1521
1522 /* at this point we know xmin is visible, go on to check xmax */
1523
1524 /* xid invalid or aborted */
1525 if (tuple->t_infomask & HEAP_XMAX_INVALID)
1526 return true;
1527 /* locked tuples are always visible */
1528 else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
1529 return true;
1530
1531 /*
1532 * We can see multis here if we're looking at user tables or if somebody
1533 * SELECT ... FOR SHARE/UPDATE a system table.
1534 */
1535 else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
1536 {
1537 xmax = HeapTupleGetUpdateXid(tuple);
1538 }
1539
1540 /* check if it's one of our txids, toplevel is also in there */
1541 if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
1542 {
1543 bool resolved;
1544 CommandId cmin;
1546
1547 /* Lookup actual cmin/cmax values */
1549 htup, buffer,
1550 &cmin, &cmax);
1551
1552 /*
1553 * If we haven't resolved the combo CID to cmin/cmax, that means we
1554 * have not decoded the combo CID yet. That means the cmax is
1555 * definitely in the future, and we're still supposed to see the
1556 * tuple.
1557 *
1558 * XXX This only applies to decoding of in-progress transactions. In
1559 * regular logical decoding we only execute this code at commit time,
1560 * at which point we should have seen all relevant combo CIDs. So
1561 * ideally, we should error out in this case but in practice, this
1562 * won't happen. If we are too worried about this then we can add an
1563 * elog inside ResolveCminCmaxDuringDecoding.
1564 *
1565 * XXX For the streaming case, we can track the largest combo CID
1566 * assigned, and error out based on this (when unable to resolve combo
1567 * CID below that observed maximum value).
1568 */
1569 if (!resolved || cmax == InvalidCommandId)
1570 return true;
1571
1572 if (cmax >= snapshot->curcid)
1573 return true; /* deleted after scan started */
1574 else
1575 return false; /* deleted before scan started */
1576 }
1577 /* below xmin horizon, normal transaction state is valid */
1578 else if (TransactionIdPrecedes(xmax, snapshot->xmin))
1579 {
1581 !TransactionIdDidCommit(xmax)));
1582
1583 /* check hint bit first */
1584 if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
1585 return false;
1586
1587 /* check clog */
1588 return !TransactionIdDidCommit(xmax);
1589 }
1590 /* above xmax horizon, we cannot possibly see the deleting transaction */
1591 else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
1592 return true;
1593 /* xmax is between [xmin, xmax), check known committed array */
1594 else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
1595 return false;
1596 /* xmax is between [xmin, xmax), but known not to have committed yet */
1597 else
1598 return true;
1599}
1600
1601/*
1602 * Perform HeaptupleSatisfiesMVCC() on each passed in tuple. This is more
1603 * efficient than doing HeapTupleSatisfiesMVCC() one-by-one.
1604 *
1605 * To be checked tuples are passed via BatchMVCCState->tuples. Each tuple's
1606 * visibility is stored in batchmvcc->visible[]. In addition,
1607 * ->vistuples_dense is set to contain the offsets of visible tuples.
1608 *
1609 * The reason this is more efficient than HeapTupleSatisfiesMVCC() is that it
1610 * avoids a cross-translation-unit function call for each tuple and allows the
1611 * compiler to optimize across calls to HeapTupleSatisfiesMVCC. In the future
1612 * it will also allow more efficient setting of hint bits.
1613 *
1614 * Returns the number of visible tuples.
1615 */
1616int
1618 int ntups,
1621{
1622 int nvis = 0;
1623
1624 Assert(IsMVCCSnapshot(snapshot));
1625
1626 for (int i = 0; i < ntups; i++)
1627 {
1628 bool valid;
1629 HeapTuple tup = &batchmvcc->tuples[i];
1630
1631 valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer);
1632 batchmvcc->visible[i] = valid;
1633
1634 if (likely(valid))
1635 {
1636 vistuples_dense[nvis] = tup->t_self.ip_posid;
1637 nvis++;
1638 }
1639 }
1640
1641 return nvis;
1642}
1643
1644/*
1645 * HeapTupleSatisfiesVisibility
1646 * True iff heap tuple satisfies a time qual.
1647 *
1648 * Notes:
1649 * Assumes heap tuple is valid, and buffer at least share locked.
1650 *
1651 * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
1652 * if so, the indicated buffer is marked dirty.
1653 */
1654bool
1656{
1657 switch (snapshot->snapshot_type)
1658 {
1659 case SNAPSHOT_MVCC:
1660 return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1661 case SNAPSHOT_SELF:
1662 return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
1663 case SNAPSHOT_ANY:
1664 return HeapTupleSatisfiesAny(htup, snapshot, buffer);
1665 case SNAPSHOT_TOAST:
1666 return HeapTupleSatisfiesToast(htup, snapshot, buffer);
1667 case SNAPSHOT_DIRTY:
1668 return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
1670 return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
1672 return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
1673 }
1674
1675 return false; /* keep compiler quiet */
1676}
int Buffer
Definition buf.h:23
bool BufferIsLockedByMeInMode(Buffer buffer, BufferLockMode mode)
Definition bufmgr.c:2997
bool BufferIsPermanent(Buffer buffer)
Definition bufmgr.c:4604
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition bufmgr.c:4634
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition bufmgr.c:5565
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:220
#define InvalidCommandId
Definition c.h:683
#define likely(x)
Definition c.h:411
#define Assert(condition)
Definition c.h:873
uint16_t uint16
Definition c.h:545
uint32 CommandId
Definition c.h:680
uint32 TransactionId
Definition c.h:666
size_t Size
Definition c.h:619
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:7668
HTSV_Result
Definition heapam.h:125
@ HEAPTUPLE_RECENTLY_DEAD
Definition heapam.h:128
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition heapam.h:129
@ HEAPTUPLE_LIVE
Definition heapam.h:127
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition heapam.h:130
@ HEAPTUPLE_DEAD
Definition heapam.h:126
static void SetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, uint16 infomask, TransactionId xid)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
static bool HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
static bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, Buffer buffer)
static bool HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, Buffer buffer)
bool HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
static bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
static bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer)
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:463
uint16 OffsetNumber
Definition off.h:24
#define InvalidOid
static int fb(int x)
bool GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
Definition procarray.c:4243
bool TransactionIdIsInProgress(TransactionId xid)
Definition procarray.c:1404
bool ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data, Snapshot snapshot, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax)
bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
Definition snapmgr.c:1869
HTAB * HistoricSnapshotGetTupleCids(void)
Definition snapmgr.c:1698
#define IsMVCCSnapshot(snapshot)
Definition snapmgr.h:55
@ 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:942
int xidComparator(const void *arg1, const void *arg2)
Definition xid.c:152
bool XLogNeedsFlush(XLogRecPtr record)
Definition xlog.c:3145
uint64 XLogRecPtr
Definition xlogdefs.h:21