LCOV - code coverage report
Current view: top level - src/backend/access/heap - heapam_visibility.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 77.8 % 500 389
Test Date: 2026-03-05 16:15:32 Functions: 100.0 % 18 18
Legend: Lines:     hit not hit

            Line data    Source code
       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              :  */
     113              : static inline void
     114      9682018 : SetHintBits(HeapTupleHeader tuple, Buffer buffer,
     115              :             uint16 infomask, TransactionId xid)
     116              : {
     117      9682018 :     if (TransactionIdIsValid(xid))
     118              :     {
     119              :         /* NB: xid must be known committed here! */
     120      9548062 :         XLogRecPtr  commitLSN = TransactionIdGetCommitLSN(xid);
     121              : 
     122      9708021 :         if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
     123       159959 :             BufferGetLSNAtomic(buffer) < commitLSN)
     124              :         {
     125              :             /* not flushed and no LSN interlock, so don't set hint */
     126       139167 :             return;
     127              :         }
     128              :     }
     129              : 
     130      9542851 :     tuple->t_infomask |= infomask;
     131      9542851 :     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              :  */
     140              : void
     141          225 : HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
     142              :                      uint16 infomask, TransactionId xid)
     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              :      */
     149              :     Assert(BufferIsLockedByMeInMode(buffer, BUFFER_LOCK_EXCLUSIVE));
     150              : 
     151          225 :     SetHintBits(tuple, buffer, infomask, xid);
     152          225 : }
     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              :  */
     160              : static inline bool
     161     26910449 : HeapTupleCleanMoved(HeapTupleHeader tuple, Buffer buffer)
     162              : {
     163              :     TransactionId xvac;
     164              : 
     165              :     /* only used by pre-9.0 binary upgrades */
     166     26910449 :     if (likely(!(tuple->t_infomask & (HEAP_MOVED_OFF | HEAP_MOVED_IN))))
     167     26910449 :         return true;
     168              : 
     169            0 :     xvac = HeapTupleHeaderGetXvac(tuple);
     170              : 
     171            0 :     if (TransactionIdIsCurrentTransactionId(xvac))
     172            0 :         elog(ERROR, "encountered tuple with HEAP_MOVED considered current");
     173              : 
     174            0 :     if (TransactionIdIsInProgress(xvac))
     175            0 :         elog(ERROR, "encountered tuple with HEAP_MOVED considered in-progress");
     176              : 
     177            0 :     if (tuple->t_infomask & HEAP_MOVED_OFF)
     178              :     {
     179            0 :         if (TransactionIdDidCommit(xvac))
     180              :         {
     181            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     182              :                         InvalidTransactionId);
     183            0 :             return false;
     184              :         }
     185            0 :         SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     186              :                     InvalidTransactionId);
     187              :     }
     188            0 :     else if (tuple->t_infomask & HEAP_MOVED_IN)
     189              :     {
     190            0 :         if (TransactionIdDidCommit(xvac))
     191            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     192              :                         InvalidTransactionId);
     193              :         else
     194              :         {
     195            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     196              :                         InvalidTransactionId);
     197            0 :             return false;
     198              :         }
     199              :     }
     200              : 
     201            0 :     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              :  */
     225              : static bool
     226         2664 : HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
     227              : {
     228         2664 :     HeapTupleHeader tuple = htup->t_data;
     229              : 
     230              :     Assert(ItemPointerIsValid(&htup->t_self));
     231              :     Assert(htup->t_tableOid != InvalidOid);
     232              : 
     233         2664 :     if (!HeapTupleHeaderXminCommitted(tuple))
     234              :     {
     235         2613 :         if (HeapTupleHeaderXminInvalid(tuple))
     236            0 :             return false;
     237              : 
     238         2613 :         if (!HeapTupleCleanMoved(tuple, buffer))
     239            0 :             return false;
     240         2613 :         else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
     241              :         {
     242         2613 :             if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid */
     243         2552 :                 return true;
     244              : 
     245           61 :             if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
     246           10 :                 return true;
     247              : 
     248           51 :             if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     249              :             {
     250              :                 TransactionId xmax;
     251              : 
     252            0 :                 xmax = HeapTupleGetUpdateXid(tuple);
     253              : 
     254              :                 /* not LOCKED_ONLY, so it has to have an xmax */
     255              :                 Assert(TransactionIdIsValid(xmax));
     256              : 
     257              :                 /* updating subtransaction must have aborted */
     258            0 :                 if (!TransactionIdIsCurrentTransactionId(xmax))
     259            0 :                     return true;
     260              :                 else
     261            0 :                     return false;
     262              :             }
     263              : 
     264           51 :             if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     265              :             {
     266              :                 /* deleting subtransaction must have aborted */
     267            9 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     268              :                             InvalidTransactionId);
     269            9 :                 return true;
     270              :             }
     271              : 
     272           42 :             return false;
     273              :         }
     274            0 :         else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
     275            0 :             return false;
     276            0 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
     277            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     278              :                         HeapTupleHeaderGetRawXmin(tuple));
     279              :         else
     280              :         {
     281              :             /* it must have aborted or crashed */
     282            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     283              :                         InvalidTransactionId);
     284            0 :             return false;
     285              :         }
     286              :     }
     287              : 
     288              :     /* by here, the inserting transaction has committed */
     289              : 
     290           51 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid or aborted */
     291           51 :         return true;
     292              : 
     293            0 :     if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
     294              :     {
     295            0 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     296            0 :             return true;
     297            0 :         return false;           /* updated by other */
     298              :     }
     299              : 
     300            0 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     301              :     {
     302              :         TransactionId xmax;
     303              : 
     304            0 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     305            0 :             return true;
     306              : 
     307            0 :         xmax = HeapTupleGetUpdateXid(tuple);
     308              : 
     309              :         /* not LOCKED_ONLY, so it has to have an xmax */
     310              :         Assert(TransactionIdIsValid(xmax));
     311              : 
     312            0 :         if (TransactionIdIsCurrentTransactionId(xmax))
     313            0 :             return false;
     314            0 :         if (TransactionIdIsInProgress(xmax))
     315            0 :             return true;
     316            0 :         if (TransactionIdDidCommit(xmax))
     317            0 :             return false;
     318              :         /* it must have aborted or crashed */
     319            0 :         return true;
     320              :     }
     321              : 
     322            0 :     if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     323              :     {
     324            0 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     325            0 :             return true;
     326            0 :         return false;
     327              :     }
     328              : 
     329            0 :     if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
     330            0 :         return true;
     331              : 
     332            0 :     if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
     333              :     {
     334              :         /* it must have aborted or crashed */
     335            0 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     336              :                     InvalidTransactionId);
     337            0 :         return true;
     338              :     }
     339              : 
     340              :     /* xmax transaction committed */
     341              : 
     342            0 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     343              :     {
     344            0 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     345              :                     InvalidTransactionId);
     346            0 :         return true;
     347              :     }
     348              : 
     349            0 :     SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
     350              :                 HeapTupleHeaderGetRawXmax(tuple));
     351            0 :     return false;
     352              : }
     353              : 
     354              : /*
     355              :  * HeapTupleSatisfiesAny
     356              :  *      Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
     357              :  */
     358              : static bool
     359      7550149 : HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
     360              : {
     361      7550149 :     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              :  */
     380              : static bool
     381        85028 : HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
     382              :                         Buffer buffer)
     383              : {
     384        85028 :     HeapTupleHeader tuple = htup->t_data;
     385              : 
     386              :     Assert(ItemPointerIsValid(&htup->t_self));
     387              :     Assert(htup->t_tableOid != InvalidOid);
     388              : 
     389        85028 :     if (!HeapTupleHeaderXminCommitted(tuple))
     390              :     {
     391        63543 :         if (HeapTupleHeaderXminInvalid(tuple))
     392            0 :             return false;
     393              : 
     394        63543 :         if (!HeapTupleCleanMoved(tuple, buffer))
     395            0 :             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              :          */
     402        63543 :         else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
     403            0 :             return false;
     404              :     }
     405              : 
     406              :     /* otherwise assume the tuple is valid for TOAST. */
     407        85028 :     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              :  */
     439              : TM_Result
     440      2201828 : HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
     441              :                          Buffer buffer)
     442              : {
     443      2201828 :     HeapTupleHeader tuple = htup->t_data;
     444              : 
     445              :     Assert(ItemPointerIsValid(&htup->t_self));
     446              :     Assert(htup->t_tableOid != InvalidOid);
     447              : 
     448      2201828 :     if (!HeapTupleHeaderXminCommitted(tuple))
     449              :     {
     450       250029 :         if (HeapTupleHeaderXminInvalid(tuple))
     451            0 :             return TM_Invisible;
     452              : 
     453       250029 :         else if (!HeapTupleCleanMoved(tuple, buffer))
     454            0 :             return TM_Invisible;
     455       250029 :         else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
     456              :         {
     457       246515 :             if (HeapTupleHeaderGetCmin(tuple) >= curcid)
     458           21 :                 return TM_Invisible;    /* inserted after scan started */
     459              : 
     460       246494 :             if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid */
     461       206271 :                 return TM_Ok;
     462              : 
     463        40223 :             if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     464              :             {
     465              :                 TransactionId xmax;
     466              : 
     467        40213 :                 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        40213 :                 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     477              :                 {
     478           31 :                     if (MultiXactIdIsRunning(xmax, true))
     479           31 :                         return TM_BeingModified;
     480              :                     else
     481            0 :                         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        40182 :                 if (!TransactionIdIsInProgress(xmax))
     490            0 :                     return TM_Ok;
     491        40182 :                 return TM_BeingModified;
     492              :             }
     493              : 
     494           10 :             if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     495              :             {
     496              :                 TransactionId xmax;
     497              : 
     498            7 :                 xmax = HeapTupleGetUpdateXid(tuple);
     499              : 
     500              :                 /* not LOCKED_ONLY, so it has to have an xmax */
     501              :                 Assert(TransactionIdIsValid(xmax));
     502              : 
     503              :                 /* deleting subtransaction must have aborted */
     504            7 :                 if (!TransactionIdIsCurrentTransactionId(xmax))
     505              :                 {
     506            7 :                     if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
     507              :                                              false))
     508            7 :                         return TM_BeingModified;
     509            0 :                     return TM_Ok;
     510              :                 }
     511              :                 else
     512              :                 {
     513            0 :                     if (HeapTupleHeaderGetCmax(tuple) >= curcid)
     514            0 :                         return TM_SelfModified; /* updated after scan started */
     515              :                     else
     516            0 :                         return TM_Invisible;    /* updated before scan started */
     517              :                 }
     518              :             }
     519              : 
     520            3 :             if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     521              :             {
     522              :                 /* deleting subtransaction must have aborted */
     523            0 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     524              :                             InvalidTransactionId);
     525            0 :                 return TM_Ok;
     526              :             }
     527              : 
     528            3 :             if (HeapTupleHeaderGetCmax(tuple) >= curcid)
     529            3 :                 return TM_SelfModified; /* updated after scan started */
     530              :             else
     531            0 :                 return TM_Invisible;    /* updated before scan started */
     532              :         }
     533         3514 :         else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
     534            0 :             return TM_Invisible;
     535         3514 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
     536         3514 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     537              :                         HeapTupleHeaderGetRawXmin(tuple));
     538              :         else
     539              :         {
     540              :             /* it must have aborted or crashed */
     541            0 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     542              :                         InvalidTransactionId);
     543            0 :             return TM_Invisible;
     544              :         }
     545              :     }
     546              : 
     547              :     /* by here, the inserting transaction has committed */
     548              : 
     549      1955313 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid or aborted */
     550      1833427 :         return TM_Ok;
     551              : 
     552       121886 :     if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
     553              :     {
     554          159 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     555            0 :             return TM_Ok;
     556          159 :         if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
     557          159 :             return TM_Updated;  /* updated by other */
     558              :         else
     559            0 :             return TM_Deleted;  /* deleted by other */
     560              :     }
     561              : 
     562       121727 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     563              :     {
     564              :         TransactionId xmax;
     565              : 
     566        73981 :         if (HEAP_LOCKED_UPGRADED(tuple->t_infomask))
     567            0 :             return TM_Ok;
     568              : 
     569        73981 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     570              :         {
     571        71826 :             if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
     572        71324 :                 return TM_BeingModified;
     573              : 
     574          502 :             SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
     575          502 :             return TM_Ok;
     576              :         }
     577              : 
     578         2155 :         xmax = HeapTupleGetUpdateXid(tuple);
     579         2155 :         if (!TransactionIdIsValid(xmax))
     580              :         {
     581            0 :             if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
     582            0 :                 return TM_BeingModified;
     583              :         }
     584              : 
     585              :         /* not LOCKED_ONLY, so it has to have an xmax */
     586              :         Assert(TransactionIdIsValid(xmax));
     587              : 
     588         2155 :         if (TransactionIdIsCurrentTransactionId(xmax))
     589              :         {
     590            0 :             if (HeapTupleHeaderGetCmax(tuple) >= curcid)
     591            0 :                 return TM_SelfModified; /* updated after scan started */
     592              :             else
     593            0 :                 return TM_Invisible;    /* updated before scan started */
     594              :         }
     595              : 
     596         2155 :         if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
     597         2150 :             return TM_BeingModified;
     598              : 
     599            5 :         if (TransactionIdDidCommit(xmax))
     600              :         {
     601            1 :             if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
     602            0 :                 return TM_Updated;
     603              :             else
     604            1 :                 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              : 
     612            4 :         if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
     613              :         {
     614              :             /*
     615              :              * There's no member, even just a locker, alive anymore, so we can
     616              :              * mark the Xmax as invalid.
     617              :              */
     618            4 :             SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     619              :                         InvalidTransactionId);
     620            4 :             return TM_Ok;
     621              :         }
     622              :         else
     623              :         {
     624              :             /* There are lockers running */
     625            0 :             return TM_BeingModified;
     626              :         }
     627              :     }
     628              : 
     629        47746 :     if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     630              :     {
     631        42806 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     632        42730 :             return TM_BeingModified;
     633           76 :         if (HeapTupleHeaderGetCmax(tuple) >= curcid)
     634           76 :             return TM_SelfModified; /* updated after scan started */
     635              :         else
     636            0 :             return TM_Invisible;    /* updated before scan started */
     637              :     }
     638              : 
     639         4940 :     if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
     640         1345 :         return TM_BeingModified;
     641              : 
     642         3595 :     if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
     643              :     {
     644              :         /* it must have aborted or crashed */
     645          190 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     646              :                     InvalidTransactionId);
     647          190 :         return TM_Ok;
     648              :     }
     649              : 
     650              :     /* xmax transaction committed */
     651              : 
     652         3405 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     653              :     {
     654         3336 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     655              :                     InvalidTransactionId);
     656         3336 :         return TM_Ok;
     657              :     }
     658              : 
     659           69 :     SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
     660              :                 HeapTupleHeaderGetRawXmax(tuple));
     661           69 :     if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
     662           68 :         return TM_Updated;      /* updated by other */
     663              :     else
     664            1 :         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              :  */
     687              : static bool
     688      5918869 : HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
     689              :                         Buffer buffer)
     690              : {
     691      5918869 :     HeapTupleHeader tuple = htup->t_data;
     692              : 
     693              :     Assert(ItemPointerIsValid(&htup->t_self));
     694              :     Assert(htup->t_tableOid != InvalidOid);
     695              : 
     696      5918869 :     snapshot->xmin = snapshot->xmax = InvalidTransactionId;
     697      5918869 :     snapshot->speculativeToken = 0;
     698              : 
     699      5918869 :     if (!HeapTupleHeaderXminCommitted(tuple))
     700              :     {
     701      5578428 :         if (HeapTupleHeaderXminInvalid(tuple))
     702          502 :             return false;
     703              : 
     704      5577926 :         if (!HeapTupleCleanMoved(tuple, buffer))
     705            0 :             return false;
     706      5577926 :         else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
     707              :         {
     708      5557479 :             if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid */
     709        31051 :                 return true;
     710              : 
     711      5526428 :             if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
     712         5040 :                 return true;
     713              : 
     714      5521388 :             if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     715              :             {
     716              :                 TransactionId xmax;
     717              : 
     718           16 :                 xmax = HeapTupleGetUpdateXid(tuple);
     719              : 
     720              :                 /* not LOCKED_ONLY, so it has to have an xmax */
     721              :                 Assert(TransactionIdIsValid(xmax));
     722              : 
     723              :                 /* updating subtransaction must have aborted */
     724           16 :                 if (!TransactionIdIsCurrentTransactionId(xmax))
     725            0 :                     return true;
     726              :                 else
     727           16 :                     return false;
     728              :             }
     729              : 
     730      5521372 :             if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     731              :             {
     732              :                 /* deleting subtransaction must have aborted */
     733            0 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     734              :                             InvalidTransactionId);
     735            0 :                 return true;
     736              :             }
     737              : 
     738      5521372 :             return false;
     739              :         }
     740        20447 :         else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
     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              :              */
     748           48 :             if (HeapTupleHeaderIsSpeculative(tuple))
     749              :             {
     750            3 :                 snapshot->speculativeToken =
     751            3 :                     HeapTupleHeaderGetSpeculativeToken(tuple);
     752              : 
     753              :                 Assert(snapshot->speculativeToken != 0);
     754              :             }
     755              : 
     756           48 :             snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
     757              :             /* XXX shouldn't we fall through to look at xmax? */
     758           48 :             return true;        /* in insertion by other */
     759              :         }
     760        20399 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
     761        18579 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     762              :                         HeapTupleHeaderGetRawXmin(tuple));
     763              :         else
     764              :         {
     765              :             /* it must have aborted or crashed */
     766         1820 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     767              :                         InvalidTransactionId);
     768         1820 :             return false;
     769              :         }
     770              :     }
     771              : 
     772              :     /* by here, the inserting transaction has committed */
     773              : 
     774       359020 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid or aborted */
     775       133262 :         return true;
     776              : 
     777       225758 :     if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
     778              :     {
     779        75249 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     780            0 :             return true;
     781        75249 :         return false;           /* updated by other */
     782              :     }
     783              : 
     784       150509 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     785              :     {
     786              :         TransactionId xmax;
     787              : 
     788           30 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     789           13 :             return true;
     790              : 
     791           17 :         xmax = HeapTupleGetUpdateXid(tuple);
     792              : 
     793              :         /* not LOCKED_ONLY, so it has to have an xmax */
     794              :         Assert(TransactionIdIsValid(xmax));
     795              : 
     796           17 :         if (TransactionIdIsCurrentTransactionId(xmax))
     797            1 :             return false;
     798           16 :         if (TransactionIdIsInProgress(xmax))
     799              :         {
     800            0 :             snapshot->xmax = xmax;
     801            0 :             return true;
     802              :         }
     803           16 :         if (TransactionIdDidCommit(xmax))
     804           16 :             return false;
     805              :         /* it must have aborted or crashed */
     806            0 :         return true;
     807              :     }
     808              : 
     809       150479 :     if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     810              :     {
     811       116333 :         if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     812           48 :             return true;
     813       116285 :         return false;
     814              :     }
     815              : 
     816        34146 :     if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
     817              :     {
     818           22 :         if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     819           12 :             snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
     820           22 :         return true;
     821              :     }
     822              : 
     823        34124 :     if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
     824              :     {
     825              :         /* it must have aborted or crashed */
     826           37 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     827              :                     InvalidTransactionId);
     828           37 :         return true;
     829              :     }
     830              : 
     831              :     /* xmax transaction committed */
     832              : 
     833        34087 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     834              :     {
     835        13467 :         SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     836              :                     InvalidTransactionId);
     837        13467 :         return true;
     838              :     }
     839              : 
     840        20620 :     SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
     841              :                 HeapTupleHeaderGetRawXmax(tuple));
     842        20620 :     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              :  */
     867              : static bool
     868     85941176 : HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
     869              :                        Buffer buffer)
     870              : {
     871     85941176 :     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              : 
     882              :     Assert(ItemPointerIsValid(&htup->t_self));
     883              :     Assert(htup->t_tableOid != InvalidOid);
     884              : 
     885     85941176 :     if (!HeapTupleHeaderXminCommitted(tuple))
     886              :     {
     887     15274880 :         if (HeapTupleHeaderXminInvalid(tuple))
     888       344792 :             return false;
     889              : 
     890     14930088 :         if (!HeapTupleCleanMoved(tuple, buffer))
     891            0 :             return false;
     892     14930088 :         else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
     893              :         {
     894     10928205 :             if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
     895         7514 :                 return false;   /* inserted after scan started */
     896              : 
     897     10920691 :             if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid */
     898      7974339 :                 return true;
     899              : 
     900      2946352 :             if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */
     901         2157 :                 return true;
     902              : 
     903      2944195 :             if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     904              :             {
     905              :                 TransactionId xmax;
     906              : 
     907            7 :                 xmax = HeapTupleGetUpdateXid(tuple);
     908              : 
     909              :                 /* not LOCKED_ONLY, so it has to have an xmax */
     910              :                 Assert(TransactionIdIsValid(xmax));
     911              : 
     912              :                 /* updating subtransaction must have aborted */
     913            7 :                 if (!TransactionIdIsCurrentTransactionId(xmax))
     914            7 :                     return true;
     915            0 :                 else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
     916            0 :                     return true;    /* updated after scan started */
     917              :                 else
     918            0 :                     return false;   /* updated before scan started */
     919              :             }
     920              : 
     921      2944188 :             if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     922              :             {
     923              :                 /* deleting subtransaction must have aborted */
     924           35 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
     925              :                             InvalidTransactionId);
     926           35 :                 return true;
     927              :             }
     928              : 
     929      2944153 :             if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
     930          848 :                 return true;    /* deleted after scan started */
     931              :             else
     932      2943305 :                 return false;   /* deleted before scan started */
     933              :         }
     934      4001883 :         else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
     935        20081 :             return false;
     936      3981802 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
     937      3928384 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
     938              :                         HeapTupleHeaderGetRawXmin(tuple));
     939              :         else
     940              :         {
     941              :             /* it must have aborted or crashed */
     942        53418 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
     943              :                         InvalidTransactionId);
     944        53418 :             return false;
     945              :         }
     946              :     }
     947              :     else
     948              :     {
     949              :         /* xmin is committed, but maybe not according to our snapshot */
     950    137165597 :         if (!HeapTupleHeaderXminFrozen(tuple) &&
     951     66499301 :             XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
     952         2492 :             return false;       /* treat as still in progress */
     953              :     }
     954              : 
     955              :     /* by here, the inserting transaction has committed */
     956              : 
     957     74592188 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid or aborted */
     958     67668347 :         return true;
     959              : 
     960      6923841 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
     961       116221 :         return true;
     962              : 
     963      6807620 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
     964              :     {
     965              :         TransactionId xmax;
     966              : 
     967              :         /* already checked above */
     968              :         Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
     969              : 
     970        78683 :         xmax = HeapTupleGetUpdateXid(tuple);
     971              : 
     972              :         /* not LOCKED_ONLY, so it has to have an xmax */
     973              :         Assert(TransactionIdIsValid(xmax));
     974              : 
     975        78683 :         if (TransactionIdIsCurrentTransactionId(xmax))
     976              :         {
     977           23 :             if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
     978            0 :                 return true;    /* deleted after scan started */
     979              :             else
     980           23 :                 return false;   /* deleted before scan started */
     981              :         }
     982        78660 :         if (XidInMVCCSnapshot(xmax, snapshot))
     983         2127 :             return true;
     984        76533 :         if (TransactionIdDidCommit(xmax))
     985        76517 :             return false;       /* updating transaction committed */
     986              :         /* it must have aborted or crashed */
     987           16 :         return true;
     988              :     }
     989              : 
     990      6728937 :     if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
     991              :     {
     992       475648 :         if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
     993              :         {
     994       143851 :             if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
     995         1364 :                 return true;    /* deleted after scan started */
     996              :             else
     997       142487 :                 return false;   /* deleted before scan started */
     998              :         }
     999              : 
    1000       331797 :         if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
    1001        10375 :             return true;
    1002              : 
    1003       321422 :         if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
    1004              :         {
    1005              :             /* it must have aborted or crashed */
    1006         7090 :             SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
    1007              :                         InvalidTransactionId);
    1008         7090 :             return true;
    1009              :         }
    1010              : 
    1011              :         /* xmax transaction committed */
    1012       314332 :         SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
    1013              :                     HeapTupleHeaderGetRawXmax(tuple));
    1014              :     }
    1015              :     else
    1016              :     {
    1017              :         /* xmax is committed, but maybe not according to our snapshot */
    1018      6253289 :         if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
    1019          751 :             return true;        /* treat as still in progress */
    1020              :     }
    1021              : 
    1022              :     /* xmax transaction committed */
    1023              : 
    1024      6566870 :     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              :  */
    1041              : HTSV_Result
    1042      7405679 : HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
    1043              :                          Buffer buffer)
    1044              : {
    1045      7405679 :     TransactionId dead_after = InvalidTransactionId;
    1046              :     HTSV_Result res;
    1047              : 
    1048      7405679 :     res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
    1049              : 
    1050      7405679 :     if (res == HEAPTUPLE_RECENTLY_DEAD)
    1051              :     {
    1052              :         Assert(TransactionIdIsValid(dead_after));
    1053              : 
    1054       137193 :         if (TransactionIdPrecedes(dead_after, OldestXmin))
    1055        15744 :             res = HEAPTUPLE_DEAD;
    1056              :     }
    1057              :     else
    1058              :         Assert(!TransactionIdIsValid(dead_after));
    1059              : 
    1060      7405679 :     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              :  */
    1075              : HTSV_Result
    1076     39551593 : HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
    1077              : {
    1078     39551593 :     HeapTupleHeader tuple = htup->t_data;
    1079              : 
    1080              :     Assert(ItemPointerIsValid(&htup->t_self));
    1081              :     Assert(htup->t_tableOid != InvalidOid);
    1082              :     Assert(dead_after != NULL);
    1083              : 
    1084     39551593 :     *dead_after = InvalidTransactionId;
    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     39551593 :     if (!HeapTupleHeaderXminCommitted(tuple))
    1093              :     {
    1094      6100375 :         if (HeapTupleHeaderXminInvalid(tuple))
    1095        14125 :             return HEAPTUPLE_DEAD;
    1096      6086250 :         else if (!HeapTupleCleanMoved(tuple, buffer))
    1097            0 :             return HEAPTUPLE_DEAD;
    1098      6086250 :         else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
    1099              :         {
    1100      2101520 :             if (tuple->t_infomask & HEAP_XMAX_INVALID)   /* xid invalid */
    1101      2064979 :                 return HEAPTUPLE_INSERT_IN_PROGRESS;
    1102              :             /* only locked? run infomask-only check first, for performance */
    1103        64968 :             if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
    1104        28427 :                 HeapTupleHeaderIsOnlyLocked(tuple))
    1105         8114 :                 return HEAPTUPLE_INSERT_IN_PROGRESS;
    1106              :             /* inserted and then deleted by same xact */
    1107        28427 :             if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
    1108        28427 :                 return HEAPTUPLE_DELETE_IN_PROGRESS;
    1109              :             /* deleting subtransaction must have aborted */
    1110            0 :             return HEAPTUPLE_INSERT_IN_PROGRESS;
    1111              :         }
    1112      3984730 :         else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
    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              :              */
    1122         3736 :             return HEAPTUPLE_INSERT_IN_PROGRESS;
    1123              :         }
    1124      3980994 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
    1125      3941901 :             SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
    1126              :                         HeapTupleHeaderGetRawXmin(tuple));
    1127              :         else
    1128              :         {
    1129              :             /*
    1130              :              * Not in Progress, Not Committed, so either Aborted or crashed
    1131              :              */
    1132        39093 :             SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
    1133              :                         InvalidTransactionId);
    1134        39093 :             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     37393119 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)
    1149     28638388 :         return HEAPTUPLE_LIVE;
    1150              : 
    1151      8754731 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
    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        13532 :         if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
    1160              :         {
    1161        13532 :             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          484 :                 if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
    1168          242 :                     MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
    1169              :                                          true))
    1170           19 :                     return HEAPTUPLE_LIVE;
    1171          223 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
    1172              :             }
    1173              :             else
    1174              :             {
    1175        13290 :                 if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
    1176            4 :                     return HEAPTUPLE_LIVE;
    1177        13286 :                 SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
    1178              :                             InvalidTransactionId);
    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        13509 :         return HEAPTUPLE_LIVE;
    1189              :     }
    1190              : 
    1191      8741199 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
    1192              :     {
    1193          102 :         TransactionId xmax = HeapTupleGetUpdateXid(tuple);
    1194              : 
    1195              :         /* already checked above */
    1196              :         Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
    1197              : 
    1198              :         /* not LOCKED_ONLY, so it has to have an xmax */
    1199              :         Assert(TransactionIdIsValid(xmax));
    1200              : 
    1201          102 :         if (TransactionIdIsInProgress(xmax))
    1202            1 :             return HEAPTUPLE_DELETE_IN_PROGRESS;
    1203          101 :         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          101 :             *dead_after = xmax;
    1214          101 :             return HEAPTUPLE_RECENTLY_DEAD;
    1215              :         }
    1216            0 :         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              :              */
    1222            0 :             SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
    1223              :         }
    1224              : 
    1225            0 :         return HEAPTUPLE_LIVE;
    1226              :     }
    1227              : 
    1228      8741097 :     if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
    1229              :     {
    1230      8046035 :         if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
    1231      6724151 :             return HEAPTUPLE_DELETE_IN_PROGRESS;
    1232      1321884 :         else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
    1233      1320515 :             SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
    1234              :                         HeapTupleHeaderGetRawXmax(tuple));
    1235              :         else
    1236              :         {
    1237              :             /*
    1238              :              * Not in Progress, Not Committed, so either Aborted or crashed
    1239              :              */
    1240         1369 :             SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
    1241              :                         InvalidTransactionId);
    1242         1369 :             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              :      */
    1256      2015577 :     *dead_after = HeapTupleHeaderGetRawXmax(tuple);
    1257      2015577 :     return HEAPTUPLE_RECENTLY_DEAD;
    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              :  */
    1273              : static bool
    1274       376853 : HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
    1275              :                                 Buffer buffer)
    1276              : {
    1277       376853 :     TransactionId dead_after = InvalidTransactionId;
    1278              :     HTSV_Result res;
    1279              : 
    1280       376853 :     res = HeapTupleSatisfiesVacuumHorizon(htup, buffer, &dead_after);
    1281              : 
    1282       376853 :     if (res == HEAPTUPLE_RECENTLY_DEAD)
    1283              :     {
    1284              :         Assert(TransactionIdIsValid(dead_after));
    1285              : 
    1286        86152 :         if (GlobalVisTestIsRemovableXid(snapshot->vistest, dead_after))
    1287        72626 :             res = HEAPTUPLE_DEAD;
    1288              :     }
    1289              :     else
    1290              :         Assert(!TransactionIdIsValid(dead_after));
    1291              : 
    1292       376853 :     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              :  */
    1309              : bool
    1310      6463203 : HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
    1311              : {
    1312      6463203 :     HeapTupleHeader tuple = htup->t_data;
    1313              : 
    1314              :     Assert(ItemPointerIsValid(&htup->t_self));
    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      6463203 :     if (!HeapTupleHeaderXminCommitted(tuple))
    1324      5649173 :         return HeapTupleHeaderXminInvalid(tuple);
    1325              : 
    1326              :     /*
    1327              :      * If the inserting transaction committed, but any deleting transaction
    1328              :      * aborted, the tuple is still alive.
    1329              :      */
    1330       814030 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)
    1331           21 :         return false;
    1332              : 
    1333              :     /*
    1334              :      * If the XMAX is just a lock, the tuple is still alive.
    1335              :      */
    1336       814009 :     if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
    1337            2 :         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       814007 :     if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
    1344          146 :         return false;
    1345              : 
    1346              :     /* If deleter isn't known to have committed, assume it's still running. */
    1347       813861 :     if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
    1348       220968 :         return false;
    1349              : 
    1350              :     /* Deleter committed, so tuple is dead if the XID is old enough. */
    1351       592893 :     return GlobalVisTestIsRemovableXid(vistest,
    1352              :                                        HeapTupleHeaderGetRawXmax(tuple));
    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              :  */
    1364              : bool
    1365       113274 : HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
    1366              : {
    1367              :     TransactionId xmax;
    1368              : 
    1369              :     /* if there's no valid Xmax, then there's obviously no update either */
    1370       113274 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)
    1371            0 :         return true;
    1372              : 
    1373       113274 :     if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY)
    1374        71677 :         return true;
    1375              : 
    1376              :     /* invalid xmax means no update */
    1377        41597 :     if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
    1378            0 :         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        41597 :     if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
    1385        39451 :         return false;
    1386              : 
    1387              :     /* ... but if it's a multi, then perhaps the updating Xid aborted. */
    1388         2146 :     xmax = HeapTupleGetUpdateXid(tuple);
    1389              : 
    1390              :     /* not LOCKED_ONLY, so it has to have an xmax */
    1391              :     Assert(TransactionIdIsValid(xmax));
    1392              : 
    1393         2146 :     if (TransactionIdIsCurrentTransactionId(xmax))
    1394            0 :         return false;
    1395         2146 :     if (TransactionIdIsInProgress(xmax))
    1396         2112 :         return false;
    1397           34 :     if (TransactionIdDidCommit(xmax))
    1398           11 :         return false;
    1399              : 
    1400              :     /*
    1401              :      * not current, not in progress, not committed -- must have aborted or
    1402              :      * crashed
    1403              :      */
    1404           23 :     return true;
    1405              : }
    1406              : 
    1407              : /*
    1408              :  * check whether the transaction id 'xid' is in the pre-sorted array 'xip'.
    1409              :  */
    1410              : static bool
    1411        35053 : TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
    1412              : {
    1413        46518 :     return num > 0 &&
    1414        11465 :         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              :  */
    1431              : static bool
    1432        28902 : HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
    1433              :                                Buffer buffer)
    1434              : {
    1435        28902 :     HeapTupleHeader tuple = htup->t_data;
    1436        28902 :     TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
    1437        28902 :     TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
    1438              : 
    1439              :     Assert(ItemPointerIsValid(&htup->t_self));
    1440              :     Assert(htup->t_tableOid != InvalidOid);
    1441              : 
    1442              :     /* inserting transaction aborted */
    1443        28902 :     if (HeapTupleHeaderXminInvalid(tuple))
    1444              :     {
    1445              :         Assert(!TransactionIdDidCommit(xmin));
    1446            0 :         return false;
    1447              :     }
    1448              :     /* check if it's one of our txids, toplevel is also in there */
    1449        28902 :     else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
    1450              :     {
    1451              :         bool        resolved;
    1452          438 :         CommandId   cmin = HeapTupleHeaderGetRawCommandId(tuple);
    1453          438 :         CommandId   cmax = InvalidCommandId;
    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              :          */
    1460          438 :         resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
    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          438 :         if (!resolved)
    1482           30 :             return false;
    1483              : 
    1484              :         Assert(cmin != InvalidCommandId);
    1485              : 
    1486          433 :         if (cmin >= snapshot->curcid)
    1487           25 :             return false;       /* inserted after scan started */
    1488              :         /* fall through */
    1489              :     }
    1490              :     /* committed before our xmin horizon. Do a normal visibility check. */
    1491        28464 :     else if (TransactionIdPrecedes(xmin, snapshot->xmin))
    1492              :     {
    1493              :         Assert(!(HeapTupleHeaderXminCommitted(tuple) &&
    1494              :                  !TransactionIdDidCommit(xmin)));
    1495              : 
    1496              :         /* check for hint bit first, consult clog afterwards */
    1497        25683 :         if (!HeapTupleHeaderXminCommitted(tuple) &&
    1498           37 :             !TransactionIdDidCommit(xmin))
    1499            0 :             return false;
    1500              :         /* fall through */
    1501              :     }
    1502              :     /* beyond our xmax horizon, i.e. invisible */
    1503         2781 :     else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax))
    1504              :     {
    1505           63 :         return false;
    1506              :     }
    1507              :     /* check if it's a committed transaction in [xmin, xmax) */
    1508         2718 :     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            0 :         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        28809 :     if (tuple->t_infomask & HEAP_XMAX_INVALID)
    1526        26062 :         return true;
    1527              :     /* locked tuples are always visible */
    1528         2747 :     else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
    1529          414 :         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         2333 :     else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
    1536              :     {
    1537            3 :         xmax = HeapTupleGetUpdateXid(tuple);
    1538              :     }
    1539              : 
    1540              :     /* check if it's one of our txids, toplevel is also in there */
    1541         2333 :     if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
    1542              :     {
    1543              :         bool        resolved;
    1544              :         CommandId   cmin;
    1545          273 :         CommandId   cmax = HeapTupleHeaderGetRawCommandId(tuple);
    1546              : 
    1547              :         /* Lookup actual cmin/cmax values */
    1548          273 :         resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot,
    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          273 :         if (!resolved || cmax == InvalidCommandId)
    1570           11 :             return true;
    1571              : 
    1572          262 :         if (cmax >= snapshot->curcid)
    1573           73 :             return true;        /* deleted after scan started */
    1574              :         else
    1575          189 :             return false;       /* deleted before scan started */
    1576              :     }
    1577              :     /* below xmin horizon, normal transaction state is valid */
    1578         2060 :     else if (TransactionIdPrecedes(xmax, snapshot->xmin))
    1579              :     {
    1580              :         Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED &&
    1581              :                  !TransactionIdDidCommit(xmax)));
    1582              : 
    1583              :         /* check hint bit first */
    1584          710 :         if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
    1585          696 :             return false;
    1586              : 
    1587              :         /* check clog */
    1588           14 :         return !TransactionIdDidCommit(xmax);
    1589              :     }
    1590              :     /* above xmax horizon, we cannot possibly see the deleting transaction */
    1591         1350 :     else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
    1592          250 :         return true;
    1593              :     /* xmax is between [xmin, xmax), check known committed array */
    1594         1100 :     else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
    1595         1100 :         return false;
    1596              :     /* xmax is between [xmin, xmax), but known not to have committed yet */
    1597              :     else
    1598            0 :         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              :  */
    1616              : int
    1617      1864935 : HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
    1618              :                             int ntups,
    1619              :                             BatchMVCCState *batchmvcc,
    1620              :                             OffsetNumber *vistuples_dense)
    1621              : {
    1622      1864935 :     int         nvis = 0;
    1623              : 
    1624              :     Assert(IsMVCCSnapshot(snapshot));
    1625              : 
    1626     69328025 :     for (int i = 0; i < ntups; i++)
    1627              :     {
    1628              :         bool        valid;
    1629     67463090 :         HeapTuple   tup = &batchmvcc->tuples[i];
    1630              : 
    1631     67463090 :         valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer);
    1632     67463090 :         batchmvcc->visible[i] = valid;
    1633              : 
    1634     67463090 :         if (likely(valid))
    1635              :         {
    1636     58629463 :             vistuples_dense[nvis] = tup->t_self.ip_posid;
    1637     58629463 :             nvis++;
    1638              :         }
    1639              :     }
    1640              : 
    1641      1864935 :     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              :  */
    1654              : bool
    1655     32440551 : HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
    1656              : {
    1657     32440551 :     switch (snapshot->snapshot_type)
    1658              :     {
    1659     18478086 :         case SNAPSHOT_MVCC:
    1660     18478086 :             return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
    1661         2664 :         case SNAPSHOT_SELF:
    1662         2664 :             return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
    1663      7550149 :         case SNAPSHOT_ANY:
    1664      7550149 :             return HeapTupleSatisfiesAny(htup, snapshot, buffer);
    1665        85028 :         case SNAPSHOT_TOAST:
    1666        85028 :             return HeapTupleSatisfiesToast(htup, snapshot, buffer);
    1667      5918869 :         case SNAPSHOT_DIRTY:
    1668      5918869 :             return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
    1669        28902 :         case SNAPSHOT_HISTORIC_MVCC:
    1670        28902 :             return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
    1671       376853 :         case SNAPSHOT_NON_VACUUMABLE:
    1672       376853 :             return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
    1673              :     }
    1674              : 
    1675            0 :     return false;               /* keep compiler quiet */
    1676              : }
        

Generated by: LCOV version 2.0-1