Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

tqual.c

/*-------------------------------------------------------------------------
 *
 * tqual.c
 *      POSTGRES "time qualification" code, ie, tuple visibility rules.
 *
 * NOTE: all the HeapTupleSatisfies routines will update the tuple's
 * "hint" status bits if we see that the inserting or deleting transaction
 * has now committed or aborted (and it is safe to set the hint bits).
 * If the hint bits are changed, SetBufferCommitInfoNeedsSave is called on
 * the passed-in buffer.  The caller must hold not only a pin, but at least
 * shared buffer content lock on the buffer containing the tuple.
 *
 * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array)
 * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
 * pg_clog).  Otherwise we have a race condition: we might decide that a
 * just-committed transaction crashed, because none of the tests succeed.
 * xact.c is careful to record commit/abort in pg_clog before it unsets
 * MyProc->xid in PGPROC array.  That fixes that problem, but it also
 * means there is a window where TransactionIdIsInProgress and
 * TransactionIdDidCommit will both return true.  If we check only
 * TransactionIdDidCommit, we could consider a tuple committed when a
 * later GetSnapshotData call will still think the originating transaction
 * is in progress, which leads to application-level inconsistency.      The
 * upshot is that we gotta check TransactionIdIsInProgress first in all
 * code paths, except for a few cases where we are looking at
 * subtransactions of our own main transaction and so there can't be any
 * race condition.
 *
 * Summary of visibility functions:
 *
 *     HeapTupleSatisfiesMVCC()
 *            visible to supplied snapshot, excludes current command
 *     HeapTupleSatisfiesNow()
 *            visible to instant snapshot, excludes current command
 *     HeapTupleSatisfiesUpdate()
 *            like HeapTupleSatisfiesNow(), but with user-supplied command
 *            counter and more complex result
 *     HeapTupleSatisfiesSelf()
 *            visible to instant snapshot and current command
 *     HeapTupleSatisfiesDirty()
 *            like HeapTupleSatisfiesSelf(), but includes open transactions
 *     HeapTupleSatisfiesVacuum()
 *            visible to any running transaction, used by VACUUM
 *     HeapTupleSatisfiesToast()
 *            visible unless part of interrupted vacuum, used for TOAST
 *     HeapTupleSatisfiesAny()
 *            all tuples are visible
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.113 2009/06/11 14:49:06 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/multixact.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
#include "utils/tqual.h"


/* Static variables representing various special snapshot semantics */
SnapshotData SnapshotNowData = {HeapTupleSatisfiesNow};
SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};

/* local functions */
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);


/*
 * SetHintBits()
 *
 * Set commit/abort hint bits on a tuple, if appropriate at this time.
 *
 * It is only safe to set a transaction-committed hint bit if we know the
 * transaction's commit record has been flushed to disk.  We cannot change
 * the LSN of the page here because we may hold only a share lock on the
 * buffer, so we can't use the LSN to interlock this; we have to just refrain
 * from setting the hint bit until some future re-examination of the tuple.
 *
 * We can always set hint bits when marking a transaction aborted.      (Some
 * code in heapam.c relies on that!)
 *
 * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
 * we can always set the hint bits, since VACUUM FULL always uses synchronous
 * commits and doesn't move tuples that weren't previously hinted.      (This is
 * not known by this subroutine, but is applied by its callers.)
 *
 * Normal commits may be asynchronous, so for those we need to get the LSN
 * of the transaction and then check whether this is flushed.
 *
 * The caller should pass xid as the XID of the transaction to check, or
 * InvalidTransactionId if no check is needed.
 */
static inline void
SetHintBits(HeapTupleHeader tuple, Buffer buffer,
                  uint16 infomask, TransactionId xid)
{
      if (TransactionIdIsValid(xid))
      {
            /* NB: xid must be known committed here! */
            XLogRecPtr  commitLSN = TransactionIdGetCommitLSN(xid);

            if (XLogNeedsFlush(commitLSN))
                  return;                       /* not flushed yet, so don't set hint */
      }

      tuple->t_infomask |= infomask;
      SetBufferCommitInfoNeedsSave(buffer);
}

/*
 * HeapTupleSetHintBits --- exported version of SetHintBits()
 *
 * This must be separate because of C99's brain-dead notions about how to
 * implement inline functions.
 */
void
HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
                               uint16 infomask, TransactionId xid)
{
      SetHintBits(tuple, buffer, infomask, xid);
}


/*
 * HeapTupleSatisfiesSelf
 *          True iff heap tuple is valid "for itself".
 *
 *    Here, we consider the effects of:
 *          all committed transactions (as of the current instant)
 *          previous commands of this transaction
 *          changes made by the current command
 *
 * Note:
 *          Assumes heap tuple is valid.
 *
 * The satisfaction of "itself" requires the following:
 *
 * ((Xmin == my-transaction &&                        the row was updated by the current transaction, and
 *          (Xmax is null                                   it was not deleted
 *           [|| Xmax != my-transaction)])                  [or it was deleted by another transaction]
 * ||
 *
 * (Xmin is committed &&                              the row was modified by a committed transaction, and
 *          (Xmax is null ||                          the row has not been deleted, or
 *                (Xmax != my-transaction &&                the row was deleted by another transaction
 *                 Xmax is not committed)))                 that has not been committed
 */
bool
HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
{
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return false;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return false;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return false;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                  }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return true;

                  if (tuple->t_infomask & HEAP_IS_LOCKED)         /* not deleter */
                        return true;

                  Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));

                  if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
                  {
                        /* deleting subtransaction must have aborted */
                        SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                          InvalidTransactionId);
                        return true;
                  }

                  return false;
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
                  return false;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return false;
            }
      }

      /* by here, the inserting transaction has committed */

      if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
            return true;

      if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            return false;                 /* updated by other */
      }

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);
            return true;
      }

      if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            return false;
      }

      if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
            return true;

      if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
      {
            /* it must have aborted or crashed */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      /* xmax transaction committed */

      if (tuple->t_infomask & HEAP_IS_LOCKED)
      {
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                        HeapTupleHeaderGetXmax(tuple));
      return false;
}

/*
 * HeapTupleSatisfiesNow
 *          True iff heap tuple is valid "now".
 *
 *    Here, we consider the effects of:
 *          all committed transactions (as of the current instant)
 *          previous commands of this transaction
 *
 * Note we do _not_ include changes made by the current command.  This
 * solves the "Halloween problem" wherein an UPDATE might try to re-update
 * its own output tuples, http://en.wikipedia.org/wiki/Halloween_Problem.
 *
 * Note:
 *          Assumes heap tuple is valid.
 *
 * The satisfaction of "now" requires the following:
 *
 * ((Xmin == my-transaction &&                        inserted by the current transaction
 *     Cmin < my-command &&                           before this command, and
 *     (Xmax is null ||                               the row has not been deleted, or
 *      (Xmax == my-transaction &&              it was deleted by the current transaction
 *       Cmax >= my-command)))                        but not before this command,
 * ||                                                       or
 *    (Xmin is committed &&                           the row was inserted by a committed transaction, and
 *          (Xmax is null ||                          the row has not been deleted, or
 *           (Xmax == my-transaction &&               the row is being deleted by this transaction
 *            Cmax >= my-command) ||                  but it's not deleted "yet", or
 *           (Xmax != my-transaction &&               the row was deleted by another transaction
 *            Xmax is not committed))))               that has not been committed
 *
 *          mao says 17 march 1993:  the tests in this routine are correct;
 *          if you think they're not, you're wrong, and you should think
 *          about it again.  i know, it happened to me.  we don't need to
 *          check commit time against the start time of this transaction
 *          because 2ph locking protects us from doing the wrong thing.
 *          if you mess around here, you'll break serializability.  the only
 *          problem with this code is that it does the wrong thing for system
 *          catalog updates, because the catalogs aren't subject to 2ph, so
 *          the serializability guarantees we provide don't extend to xacts
 *          that do catalog accesses.  this is unfortunate, but not critical.
 */
bool
HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
{
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return false;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return false;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return false;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                  }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId(false))
                        return false;     /* inserted after scan started */

                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return true;

                  if (tuple->t_infomask & HEAP_IS_LOCKED)         /* not deleter */
                        return true;

                  Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));

                  if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
                  {
                        /* deleting subtransaction must have aborted */
                        SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                          InvalidTransactionId);
                        return true;
                  }

                  if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
                        return true;      /* deleted after scan started */
                  else
                        return false;     /* deleted before scan started */
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
                  return false;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return false;
            }
      }

      /* by here, the inserting transaction has committed */

      if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
            return true;

      if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            return false;
      }

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);
            return true;
      }

      if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
                  return true;            /* deleted after scan started */
            else
                  return false;           /* deleted before scan started */
      }

      if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
            return true;

      if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
      {
            /* it must have aborted or crashed */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      /* xmax transaction committed */

      if (tuple->t_infomask & HEAP_IS_LOCKED)
      {
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                        HeapTupleHeaderGetXmax(tuple));
      return false;
}

/*
 * HeapTupleSatisfiesAny
 *          Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
 */
bool
HeapTupleSatisfiesAny(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
{
      return true;
}

/*
 * HeapTupleSatisfiesToast
 *          True iff heap tuple is valid as a TOAST row.
 *
 * This is a simplified version that only checks for VACUUM moving conditions.
 * It's appropriate for TOAST usage because TOAST really doesn't want to do
 * its own time qual checks; if you can see the main table row that contains
 * a TOAST reference, you should be able to see the TOASTed value.      However,
 * vacuuming a TOAST table is independent of the main table, and in case such
 * a vacuum fails partway through, we'd better do this much checking.
 *
 * Among other things, this means you can't do UPDATEs of rows in a TOAST
 * table.
 */
bool
HeapTupleSatisfiesToast(HeapTupleHeader tuple, Snapshot snapshot,
                                    Buffer buffer)
{
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return false;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return false;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return false;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                  }
            }
      }

      /* otherwise assume the tuple is valid for TOAST. */
      return true;
}

/*
 * HeapTupleSatisfiesUpdate
 *
 *    Same logic as HeapTupleSatisfiesNow, but returns a more detailed result
 *    code, since UPDATE needs to know more than "is it visible?".  Also,
 *    tuples of my own xact are tested against the passed CommandId not
 *    CurrentCommandId.
 *
 *    The possible return codes are:
 *
 *    HeapTupleInvisible: the tuple didn't exist at all when the scan started,
 *    e.g. it was created by a later CommandId.
 *
 *    HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
 *    updated.
 *
 *    HeapTupleSelfUpdated: The tuple was updated by the current transaction,
 *    after the current scan started.
 *
 *    HeapTupleUpdated: The tuple was updated by a committed transaction.
 *
 *    HeapTupleBeingUpdated: The tuple is being updated by an in-progress
 *    transaction other than the current transaction.  (Note: this includes
 *    the case where the tuple is share-locked by a MultiXact, even if the
 *    MultiXact includes the current transaction.  Callers that want to
 *    distinguish that case must test for it themselves.)
 */
HTSU_Result
HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
                                     Buffer buffer)
{
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return HeapTupleInvisible;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return HeapTupleInvisible;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return HeapTupleInvisible;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return HeapTupleInvisible;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return HeapTupleInvisible;
                        }
                  }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (HeapTupleHeaderGetCmin(tuple) >= curcid)
                        return HeapTupleInvisible;          /* inserted after scan started */

                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return HeapTupleMayBeUpdated;

                  if (tuple->t_infomask & HEAP_IS_LOCKED)         /* not deleter */
                        return HeapTupleMayBeUpdated;

                  Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));

                  if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
                  {
                        /* deleting subtransaction must have aborted */
                        SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                          InvalidTransactionId);
                        return HeapTupleMayBeUpdated;
                  }

                  if (HeapTupleHeaderGetCmax(tuple) >= curcid)
                        return HeapTupleSelfUpdated;  /* updated after scan started */
                  else
                        return HeapTupleInvisible;          /* updated before scan started */
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
                  return HeapTupleInvisible;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return HeapTupleInvisible;
            }
      }

      /* by here, the inserting transaction has committed */

      if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
            return HeapTupleMayBeUpdated;

      if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return HeapTupleMayBeUpdated;
            return HeapTupleUpdated;      /* updated by other */
      }

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);

            if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
                  return HeapTupleBeingUpdated;
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return HeapTupleMayBeUpdated;
      }

      if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return HeapTupleMayBeUpdated;
            if (HeapTupleHeaderGetCmax(tuple) >= curcid)
                  return HeapTupleSelfUpdated;        /* updated after scan started */
            else
                  return HeapTupleInvisible;    /* updated before scan started */
      }

      if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
            return HeapTupleBeingUpdated;

      if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
      {
            /* it must have aborted or crashed */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return HeapTupleMayBeUpdated;
      }

      /* xmax transaction committed */

      if (tuple->t_infomask & HEAP_IS_LOCKED)
      {
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return HeapTupleMayBeUpdated;
      }

      SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                        HeapTupleHeaderGetXmax(tuple));
      return HeapTupleUpdated;      /* updated by other */
}

/*
 * HeapTupleSatisfiesDirty
 *          True iff heap tuple is valid including effects of open transactions.
 *
 *    Here, we consider the effects of:
 *          all committed and in-progress transactions (as of the current instant)
 *          previous commands of this transaction
 *          changes made by the current command
 *
 * This is essentially like HeapTupleSatisfiesSelf as far as effects of
 * the current transaction and committed/aborted xacts are concerned.
 * However, we also include the effects of other xacts still in progress.
 *
 * A special hack is that the passed-in snapshot struct is used as an
 * output argument to return the xids of concurrent xacts that affected the
 * tuple.  snapshot->xmin is set to the tuple's xmin if that is another
 * transaction that's still in progress; or to InvalidTransactionId if the
 * tuple's xmin is committed good, committed dead, or my own xact.  Similarly
 * for snapshot->xmax and the tuple's xmax.
 */
bool
HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
                                    Buffer buffer)
{
      snapshot->xmin = snapshot->xmax = InvalidTransactionId;

      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return false;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return false;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return false;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                  }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return true;

                  if (tuple->t_infomask & HEAP_IS_LOCKED)         /* not deleter */
                        return true;

                  Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));

                  if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
                  {
                        /* deleting subtransaction must have aborted */
                        SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                          InvalidTransactionId);
                        return true;
                  }

                  return false;
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
            {
                  snapshot->xmin = HeapTupleHeaderGetXmin(tuple);
                  /* XXX shouldn't we fall through to look at xmax? */
                  return true;            /* in insertion by other */
            }
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return false;
            }
      }

      /* by here, the inserting transaction has committed */

      if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
            return true;

      if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            return false;                 /* updated by other */
      }

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);
            return true;
      }

      if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
      {
            if (tuple->t_infomask & HEAP_IS_LOCKED)
                  return true;
            return false;
      }

      if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
      {
            snapshot->xmax = HeapTupleHeaderGetXmax(tuple);
            return true;
      }

      if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
      {
            /* it must have aborted or crashed */
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      /* xmax transaction committed */

      if (tuple->t_infomask & HEAP_IS_LOCKED)
      {
            SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                              InvalidTransactionId);
            return true;
      }

      SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                        HeapTupleHeaderGetXmax(tuple));
      return false;                       /* updated by other */
}

/*
 * HeapTupleSatisfiesMVCC
 *          True iff heap tuple is valid for the given MVCC snapshot.
 *
 *    Here, we consider the effects of:
 *          all transactions committed as of the time of the given snapshot
 *          previous commands of this transaction
 *
 *    Does _not_ include:
 *          transactions shown as in-progress by the snapshot
 *          transactions started after the snapshot was taken
 *          changes made by the current command
 *
 * This is the same as HeapTupleSatisfiesNow, except that transactions that
 * were in progress or as yet unstarted when the snapshot was taken will
 * be treated as uncommitted, even if they have committed by now.
 *
 * (Notice, however, that the tuple status hint bits will be updated on the
 * basis of the true state of the transaction, even if we then pretend we
 * can't see it.)
 */
bool
HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
                                 Buffer buffer)
{
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return false;

            if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return false;
                  if (!TransactionIdIsInProgress(xvac))
                  {
                        if (TransactionIdDidCommit(xvac))
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  }
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (!TransactionIdIsCurrentTransactionId(xvac))
                  {
                        if (TransactionIdIsInProgress(xvac))
                              return false;
                        if (TransactionIdDidCommit(xvac))
                              SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                                InvalidTransactionId);
                        else
                        {
                              SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                                InvalidTransactionId);
                              return false;
                        }
                  }
            }
            else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
                        return false;     /* inserted after scan started */

                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return true;

                  if (tuple->t_infomask & HEAP_IS_LOCKED)         /* not deleter */
                        return true;

                  Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));

                  if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
                  {
                        /* deleting subtransaction must have aborted */
                        SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                          InvalidTransactionId);
                        return true;
                  }

                  if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                        return true;      /* deleted after scan started */
                  else
                        return false;     /* deleted before scan started */
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
                  return false;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return false;
            }
      }

      /*
       * By here, the inserting transaction has committed - have to check
       * when...
       */
      if (XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
            return false;                 /* treat as still in progress */

      if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
            return true;

      if (tuple->t_infomask & HEAP_IS_LOCKED)
            return true;

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);
            return true;
      }

      if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
      {
            if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
            {
                  if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
                        return true;      /* deleted after scan started */
                  else
                        return false;     /* deleted before scan started */
            }

            if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
                  return true;

            if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
            {
                  /* it must have aborted or crashed */
                  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                    InvalidTransactionId);
                  return true;
            }

            /* xmax transaction committed */
            SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                              HeapTupleHeaderGetXmax(tuple));
      }

      /*
       * OK, the deleting transaction committed too ... but when?
       */
      if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot))
            return true;                  /* treat as still in progress */

      return false;
}


/*
 * HeapTupleSatisfiesVacuum
 *
 *    Determine the status of tuples for VACUUM purposes.  Here, what
 *    we mainly want to know is if a tuple is potentially visible to *any*
 *    running transaction.  If so, it can't be removed yet by VACUUM.
 *
 * OldestXmin is a cutoff XID (obtained from GetOldestXmin()).    Tuples
 * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
 * still be visible to some open transaction, so we can't remove them,
 * even if we see that the deleting transaction has committed.
 */
HTSV_Result
HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
                                     Buffer buffer)
{
      /*
       * Has inserting transaction committed?
       *
       * If the inserting transaction aborted, then the tuple was never visible
       * to any other transaction, so we can delete it immediately.
       */
      if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
      {
            if (tuple->t_infomask & HEAP_XMIN_INVALID)
                  return HEAPTUPLE_DEAD;
            else if (tuple->t_infomask & HEAP_MOVED_OFF)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return HEAPTUPLE_DELETE_IN_PROGRESS;
                  if (TransactionIdIsInProgress(xvac))
                        return HEAPTUPLE_DELETE_IN_PROGRESS;
                  if (TransactionIdDidCommit(xvac))
                  {
                        SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                          InvalidTransactionId);
                        return HEAPTUPLE_DEAD;
                  }
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    InvalidTransactionId);
            }
            else if (tuple->t_infomask & HEAP_MOVED_IN)
            {
                  TransactionId xvac = HeapTupleHeaderGetXvac(tuple);

                  if (TransactionIdIsCurrentTransactionId(xvac))
                        return HEAPTUPLE_INSERT_IN_PROGRESS;
                  if (TransactionIdIsInProgress(xvac))
                        return HEAPTUPLE_INSERT_IN_PROGRESS;
                  if (TransactionIdDidCommit(xvac))
                        SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                          InvalidTransactionId);
                  else
                  {
                        SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                          InvalidTransactionId);
                        return HEAPTUPLE_DEAD;
                  }
            }
            else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
            {
                  if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
                        return HEAPTUPLE_INSERT_IN_PROGRESS;
                  if (tuple->t_infomask & HEAP_IS_LOCKED)
                        return HEAPTUPLE_INSERT_IN_PROGRESS;
                  /* inserted and then deleted by same xact */
                  return HEAPTUPLE_DELETE_IN_PROGRESS;
            }
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
                                    HeapTupleHeaderGetXmin(tuple));
            else
            {
                  /*
                   * Not in Progress, Not Committed, so either Aborted or crashed
                   */
                  SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
                                    InvalidTransactionId);
                  return HEAPTUPLE_DEAD;
            }

            /*
             * At this point the xmin is known committed, but we might not have
             * been able to set the hint bit yet; so we can no longer Assert that
             * it's set.
             */
      }

      /*
       * Okay, the inserter committed, so it was good at some point.    Now what
       * about the deleting transaction?
       */
      if (tuple->t_infomask & HEAP_XMAX_INVALID)
            return HEAPTUPLE_LIVE;

      if (tuple->t_infomask & HEAP_IS_LOCKED)
      {
            /*
             * "Deleting" xact really only locked it, so the tuple is live in any
             * case.  However, we should make sure that either XMAX_COMMITTED or
             * XMAX_INVALID gets set once the xact is gone, to reduce the costs of
             * examining the tuple for future xacts.  Also, marking dead
             * MultiXacts as invalid here provides defense against MultiXactId
             * wraparound (see also comments in heap_freeze_tuple()).
             */
            if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
            {
                  if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
                  {
                        if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
                              return HEAPTUPLE_LIVE;
                  }
                  else
                  {
                        if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
                              return HEAPTUPLE_LIVE;
                  }

                  /*
                   * We don't really care whether xmax did commit, abort or crash.
                   * We know that xmax did lock the tuple, but it did not and will
                   * never actually update it.
                   */
                  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                    InvalidTransactionId);
            }
            return HEAPTUPLE_LIVE;
      }

      if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
      {
            /* MultiXacts are currently only allowed to lock tuples */
            Assert(tuple->t_infomask & HEAP_IS_LOCKED);
            return HEAPTUPLE_LIVE;
      }

      if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
      {
            if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
                  return HEAPTUPLE_DELETE_IN_PROGRESS;
            else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
                  SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
                                    HeapTupleHeaderGetXmax(tuple));
            else
            {
                  /*
                   * Not in Progress, Not Committed, so either Aborted or crashed
                   */
                  SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
                                    InvalidTransactionId);
                  return HEAPTUPLE_LIVE;
            }

            /*
             * At this point the xmax is known committed, but we might not have
             * been able to set the hint bit yet; so we can no longer Assert that
             * it's set.
             */
      }

      /*
       * Deleter committed, but check special cases.
       */

      if (TransactionIdEquals(HeapTupleHeaderGetXmin(tuple),
                                          HeapTupleHeaderGetXmax(tuple)))
      {
            /*
             * Inserter also deleted it, so it was never visible to anyone else.
             * However, we can only remove it early if it's not an updated tuple;
             * else its parent tuple is linking to it via t_ctid, and this tuple
             * mustn't go away before the parent does.
             */
            if (!(tuple->t_infomask & HEAP_UPDATED))
                  return HEAPTUPLE_DEAD;
      }

      if (!TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin))
      {
            /* deleting xact is too recent, tuple could still be visible */
            return HEAPTUPLE_RECENTLY_DEAD;
      }

      /* Otherwise, it's dead and removable */
      return HEAPTUPLE_DEAD;
}


/*
 * XidInMVCCSnapshot
 *          Is the given XID still-in-progress according to the snapshot?
 *
 * Note: GetSnapshotData never stores either top xid or subxids of our own
 * backend into a snapshot, so these xids will not be reported as "running"
 * by this function.  This is OK for current uses, because we actually only
 * apply this for known-committed XIDs.
 */
static bool
XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
{
      uint32            i;

      /*
       * Make a quick range check to eliminate most XIDs without looking at the
       * xip arrays.    Note that this is OK even if we convert a subxact XID to
       * its parent below, because a subxact with XID < xmin has surely also got
       * a parent with XID < xmin, while one with XID >= xmax must belong to a
       * parent that was not yet committed at the time of this snapshot.
       */

      /* Any xid < xmin is not in-progress */
      if (TransactionIdPrecedes(xid, snapshot->xmin))
            return false;
      /* Any xid >= xmax is in-progress */
      if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
            return true;

      /*
       * If the snapshot contains full subxact data, the fastest way to check
       * things is just to compare the given XID against both subxact XIDs and
       * top-level XIDs.      If the snapshot overflowed, we have to use pg_subtrans
       * to convert a subxact XID to its parent XID, but then we need only look
       * at top-level XIDs not subxacts.
       */
      if (snapshot->subxcnt >= 0)
      {
            /* full data, so search subxip */
            int32       j;

            for (j = 0; j < snapshot->subxcnt; j++)
            {
                  if (TransactionIdEquals(xid, snapshot->subxip[j]))
                        return true;
            }

            /* not there, fall through to search xip[] */
      }
      else
      {
            /* overflowed, so convert xid to top-level */
            xid = SubTransGetTopmostTransaction(xid);

            /*
             * If xid was indeed a subxact, we might now have an xid < xmin, so
             * recheck to avoid an array scan.  No point in rechecking xmax.
             */
            if (TransactionIdPrecedes(xid, snapshot->xmin))
                  return false;
      }

      for (i = 0; i < snapshot->xcnt; i++)
      {
            if (TransactionIdEquals(xid, snapshot->xip[i]))
                  return true;
      }

      return false;
}

Generated by  Doxygen 1.6.0   Back to index