Skip to content

File mvcc.c

File List > cubrid > src > transaction > mvcc.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * mvcc.c - mvcc snapshot
 */

#ident "$Id$"

#include "mvcc.h"
#include "dbtype.h"
#include "heap_file.h"
#include "page_buffer.h"
#include "overflow_file.h"
#include "perf_monitor.h"
#include "porting_inline.hpp"
#include "vacuum.h"
#if 0
// This file contains `placement new` being used within it, and there are
// no explicit cases of calling `new`. Since heap memory monitoring does not
// encounter any holes even without tracking this file, it is considered an
// exception from the tracked files.
//
// To bring this file into the scope of memory monitoring, the usage of
// `placement new` needs to be removed.

// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#endif

#define MVCC_IS_REC_INSERTER_ACTIVE(thread_p, rec_header_p) \
  (mvcc_is_active_id (thread_p, (rec_header_p)->mvcc_ins_id))

#define MVCC_IS_REC_DELETER_ACTIVE(thread_p, rec_header_p) \
  (mvcc_is_active_id (thread_p, (rec_header_p)->mvcc_del_id))

#define MVCC_IS_REC_INSERTER_IN_SNAPSHOT(thread_p, rec_header_p, snapshot) \
  (mvcc_is_id_in_snapshot (thread_p, (rec_header_p)->mvcc_ins_id, (snapshot)))

#define MVCC_IS_REC_DELETER_IN_SNAPSHOT(thread_p, rec_header_p, snapshot) \
  (mvcc_is_id_in_snapshot (thread_p, (rec_header_p)->mvcc_del_id, (snapshot)))

#define MVCC_IS_REC_INSERTED_SINCE_MVCCID(rec_header_p, mvcc_id) \
  (!MVCC_ID_PRECEDES ((rec_header_p)->mvcc_ins_id, (mvcc_id)))

#define MVCC_IS_REC_DELETED_SINCE_MVCCID(rec_header_p, mvcc_id) \
  (!MVCC_ID_PRECEDES ((rec_header_p)->mvcc_del_id, (mvcc_id)))


/* Used by mvcc_chain_satisfies_vacuum to avoid handling the same OID twice */
enum
{
  /* Any positive value should be an index in relocated slots array */
  NOT_VISITED = -1,
  VISITED_DEAD = -2,
  VISITED_ALIVE = -3
};

/* the lowest active mvcc id computed for last */
/* MVCCID recent_snapshot_lowest_active_mvccid = MVCCID_NULL; */

static INLINE bool mvcc_is_id_in_snapshot (THREAD_ENTRY * thread_p, MVCCID mvcc_id, MVCC_SNAPSHOT * snapshot)
  __attribute__ ((ALWAYS_INLINE));

static INLINE bool mvcc_is_active_id (THREAD_ENTRY * thread_p, MVCCID mvccid) __attribute__ ((ALWAYS_INLINE));

/*
 * mvcc_is_id_in_snapshot () - check whether mvcc id is in snapshot -
 *                             is mvcc id active from snapshot point of view?
 *   return: true/false
 *   thread_p(in): thread entry
 *   mvcc_id(in): mvcc id
 *   snapshot(in): mvcc snapshot
 */
STATIC_INLINE bool
mvcc_is_id_in_snapshot (THREAD_ENTRY * thread_p, MVCCID mvcc_id, MVCC_SNAPSHOT * snapshot)
{
  assert (snapshot != NULL);

  if (MVCC_ID_PRECEDES (mvcc_id, snapshot->lowest_active_mvccid))
    {
      /* MVCC id is not active */
      return false;
    }

  if (MVCC_ID_FOLLOW_OR_EQUAL (mvcc_id, snapshot->highest_completed_mvccid))
    {
      /* MVCC id is active */
      return true;
    }

  return snapshot->m_active_mvccs.is_active (mvcc_id);
}

/*
 * mvcc_is_active_id - check whether given mvccid is active
 *
 * return: bool
 *
 *   thread_p(in): thread entry
 *   id(in): MVCC id
 *
 * Note: Check whether an active transaction is active by searching transactions
 *  status into current history position. The data from current history position
 *  is atomically checked (See logtb_get_mvcc_snapshot_data comments).
 */
STATIC_INLINE bool
mvcc_is_active_id (THREAD_ENTRY * thread_p, MVCCID mvccid)
{
  LOG_TDES *tdes = LOG_FIND_TDES (LOG_FIND_THREAD_TRAN_INDEX (thread_p));
  MVCC_INFO *curr_mvcc_info = NULL;

  assert (tdes != NULL && mvccid != MVCCID_NULL);

  curr_mvcc_info = &tdes->mvccinfo;
  if (MVCC_ID_PRECEDES (mvccid, curr_mvcc_info->recent_snapshot_lowest_active_mvccid))
    {
      return false;
    }

  if (logtb_is_current_mvccid (thread_p, mvccid))
    {
      return true;
    }

  return log_Gl.mvcc_table.is_active (mvccid);
}

/*
 * mvcc_satisfies_snapshot () - Check whether a record is valid for
 *                  a snapshot
 *   return: - SNAPSHOT_SATISFIED: record is valid for snapshot
 *       - TOO_NEW_FOR_SNAPSHOT: record was either inserted or updated recently; commited after snapshot
 *       - TOO_OLD_FOR_SNAPSHOT: record not visible; deleted and commited
 *   thread_p(in): thread entry
 *   rec_header(out): the record header
 *   snapshot(in): the snapshot used for record validation
 *   page_ptr(in): the page where the record reside
 */
MVCC_SATISFIES_SNAPSHOT_RESULT
mvcc_satisfies_snapshot (THREAD_ENTRY * thread_p, MVCC_REC_HEADER * rec_header, MVCC_SNAPSHOT * snapshot)
{
  assert (rec_header != NULL && snapshot != NULL);

  if (!MVCC_IS_HEADER_DELID_VALID (rec_header))
    {
      /* The record is not deleted */
      if (!MVCC_IS_FLAG_SET (rec_header, OR_MVCC_FLAG_VALID_INSID))
    {
      /* Record was inserted and is visible for all transactions */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT, PERF_SNAPSHOT_RECORD_INSERTED_VACUUMED,
                     PERF_SNAPSHOT_VISIBLE);
        }

      return SNAPSHOT_SATISFIED;
    }
      else if (MVCC_IS_REC_INSERTED_BY_ME (thread_p, rec_header))
    {
      /* Record was inserted by current transaction and is visible */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_INSERTED_CURR_TRAN, PERF_SNAPSHOT_VISIBLE);
        }
      return SNAPSHOT_SATISFIED;
    }
      else if (MVCC_IS_REC_INSERTER_IN_SNAPSHOT (thread_p, rec_header, snapshot))
    {
      /* Record was inserted by an active transaction or by a transaction that has committed after snapshot was
       * obtained. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_INSERTED_OTHER_TRAN, PERF_SNAPSHOT_INVISIBLE);
        }
      return TOO_NEW_FOR_SNAPSHOT;
    }
      else
    {
      /* The inserter transaction has committed and the record is visible to current transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (rec_header->mvcc_ins_id != MVCCID_ALL_VISIBLE && vacuum_is_mvccid_vacuumed (rec_header->mvcc_ins_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED_LOST, PERF_SNAPSHOT_VISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED, PERF_SNAPSHOT_VISIBLE);
        }
        }
      return SNAPSHOT_SATISFIED;
    }
    }
  else
    {
      /* The record is deleted */
      if (MVCC_IS_REC_DELETED_BY_ME (thread_p, rec_header))
    {
      /* The record was deleted by current transaction and it is not visible anymore. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT, PERF_SNAPSHOT_RECORD_DELETED_CURR_TRAN,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return TOO_OLD_FOR_SNAPSHOT;
    }
      else if (MVCC_IS_REC_INSERTER_IN_SNAPSHOT (thread_p, rec_header, snapshot))
    {
      /* !!TODO: Is this check necessary? It seems that if inserter is active, then so will be the deleter (actually
       *       they will be the same). It only adds an extra-check in a function frequently called.
       */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT, PERF_SNAPSHOT_RECORD_INSERTED_DELETED,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return TOO_NEW_FOR_SNAPSHOT;
    }
      else if (MVCC_IS_REC_DELETER_IN_SNAPSHOT (thread_p, rec_header, snapshot))
    {
      /* The record was deleted by an active transaction or by a transaction that has committed after snapshot was
       * obtained. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_DELETED_OTHER_TRAN, PERF_SNAPSHOT_VISIBLE);
        }
      return SNAPSHOT_SATISFIED;
    }
      else
    {
      /* The deleter transaction has committed and the record is not visible to current transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (vacuum_is_mvccid_vacuumed (rec_header->mvcc_del_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED_LOST, PERF_SNAPSHOT_INVISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_SNAPSHOT,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED, PERF_SNAPSHOT_INVISIBLE);
        }
        }
      return TOO_OLD_FOR_SNAPSHOT;
    }
    }
}

/*
 * mvcc_is_not_deleted_for_snapshot () - Check whether a record is deleted or not regarding the snapshot
 *   return      : TOO_OLD_FOR_SNAPSHOT if record is deleted and deleter is either me or is not active.
 *             SNAPSHOT_SATISFIED if record is not deleted or deleter is neither me nor is still active.
 *   thread_p (in)   : thread entry
 *   rec_header (in) : the record header
 *   snapshot (in)   : the snapshot used for record validation
 */
MVCC_SATISFIES_SNAPSHOT_RESULT
mvcc_is_not_deleted_for_snapshot (THREAD_ENTRY * thread_p, MVCC_REC_HEADER * rec_header, MVCC_SNAPSHOT * snapshot)
{
  assert (rec_header != NULL && snapshot != NULL);

  if (!MVCC_IS_HEADER_DELID_VALID (rec_header))
    {
      /* The record is not deleted */
      return SNAPSHOT_SATISFIED;
    }
  else
    {
      /* The record is deleted */
      if (MVCC_IS_REC_DELETED_BY_ME (thread_p, rec_header))
    {
      /* The record was deleted by current transaction and it is not visible anymore. */
      return TOO_OLD_FOR_SNAPSHOT;
    }
      else if (MVCC_IS_REC_DELETER_IN_SNAPSHOT (thread_p, rec_header, snapshot))
    {
      /* The record was deleted by an active transaction or by a transaction that has committed after snapshot was
       * obtained. */
      return SNAPSHOT_SATISFIED;
    }
      else
    {
      /* The deleter transaction has committed and the record is not visible to current transaction. */
      return TOO_OLD_FOR_SNAPSHOT;
    }
    }
}

/*
 * mvcc_satisfies_vacuum () - Check whether record satisfies VACUUM
 *
 * return         : Heap record satisfies vacuum result.
 * thread_p (in)      : Thread entry.
 * rec_header (in)    : MVCC record header.
 * oldest_mvccid (in) : MVCCID for oldest active transaction.
 * page_p (in)        : Heap page pointer.
 */
MVCC_SATISFIES_VACUUM_RESULT
mvcc_satisfies_vacuum (THREAD_ENTRY * thread_p, MVCC_REC_HEADER * rec_header, MVCCID oldest_mvccid)
{
  if (!MVCC_IS_HEADER_DELID_VALID (rec_header) || MVCC_IS_REC_DELETED_SINCE_MVCCID (rec_header, oldest_mvccid))
    {
      /* The record was not deleted or was recently deleted and cannot be vacuumed completely. */
      if (!MVCC_IS_HEADER_INSID_NOT_ALL_VISIBLE (rec_header)
      || MVCC_IS_REC_INSERTED_SINCE_MVCCID (rec_header, oldest_mvccid))
    {
      /* 1: Record is all visible, and insert MVCCID was already removed/replaced. 2: Record was recently inserted
       * and is not yet visible to all active transactions. Cannot vacuum insert MVCCID. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (MVCC_IS_HEADER_DELID_VALID (rec_header)
          && MVCC_IS_REC_DELETED_SINCE_MVCCID (rec_header, oldest_mvccid))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_VACUUM,
                     PERF_SNAPSHOT_RECORD_DELETED_OTHER_TRAN, PERF_SNAPSHOT_INVISIBLE);
        }
          else if (!MVCC_IS_HEADER_INSID_NOT_ALL_VISIBLE (rec_header))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_VACUUM,
                     PERF_SNAPSHOT_RECORD_INSERTED_VACUUMED, PERF_SNAPSHOT_INVISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_VACUUM,
                     PERF_SNAPSHOT_RECORD_INSERTED_OTHER_TRAN, PERF_SNAPSHOT_INVISIBLE);
        }
        }
      return VACUUM_RECORD_CANNOT_VACUUM;
    }
      else
    {
      /* The inserter transaction has committed and the record is visible to all running transactions. Insert
       * MVCCID and previous version lsa can be removed. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_VACUUM, PERF_SNAPSHOT_RECORD_INSERTED_COMMITED,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return VACUUM_RECORD_DELETE_INSID_PREV_VER;
    }
    }
  else
    {
      /* The deleter transaction has committed and the record is not visible to any running transactions. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
    {
      perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_VACUUM, PERF_SNAPSHOT_RECORD_DELETED_COMMITTED,
                 PERF_SNAPSHOT_VISIBLE);
    }
      return VACUUM_RECORD_REMOVE;
    }
}

/*
 * mvcc_satisfies_delete () - Check whether a record is valid for
 *          instant snapshot
 *   return: true, if the record is valid for snapshot
 *   thread_p(in): thread entry
 *   rec_header(out): the record header
 *   snapshot(in): the snapshot used for record validation
 *   page_ptr(in): the page where the record reside
 *
 * Note: The function return a complex result since delete/update commands
 *      needs to know not only if the row is visible or not
 */
MVCC_SATISFIES_DELETE_RESULT
mvcc_satisfies_delete (THREAD_ENTRY * thread_p, MVCC_REC_HEADER * rec_header)
{
  assert (rec_header != NULL);

  if (!MVCC_IS_HEADER_DELID_VALID (rec_header))
    {
      /* Record was not deleted */
      if (!MVCC_IS_FLAG_SET (rec_header, OR_MVCC_FLAG_VALID_INSID))
    {
      /* Record was inserted and is visible for all transactions */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE, PERF_SNAPSHOT_RECORD_INSERTED_VACUUMED,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return DELETE_RECORD_CAN_DELETE;
    }

      if (MVCC_IS_REC_INSERTED_BY_ME (thread_p, rec_header))
    {
      /* Record is only visible to current transaction and can be safely deleted. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE, PERF_SNAPSHOT_RECORD_INSERTED_CURR_TRAN,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return DELETE_RECORD_CAN_DELETE;
    }
      else if (MVCC_IS_REC_INSERTER_ACTIVE (thread_p, rec_header))
    {
      /* Record is inserted by an active transaction and is not visible to current transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE, PERF_SNAPSHOT_RECORD_INSERTED_OTHER_TRAN,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return DELETE_RECORD_INSERT_IN_PROGRESS;
    }
      else
    {
      /* The inserter transaction has committed and the record can be deleted by current transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (rec_header->mvcc_ins_id != MVCCID_ALL_VISIBLE && vacuum_is_mvccid_vacuumed (rec_header->mvcc_ins_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED_LOST, PERF_SNAPSHOT_VISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED, PERF_SNAPSHOT_VISIBLE);
        }
        }
      return DELETE_RECORD_CAN_DELETE;
    }
    }
  else
    {
      /* Record was already deleted */
      if (MVCC_IS_REC_DELETED_BY_ME (thread_p, rec_header))
    {
      /* Record was already deleted by me... */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE, PERF_SNAPSHOT_RECORD_DELETED_CURR_TRAN,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return DELETE_RECORD_SELF_DELETED;
    }
      else if (MVCC_IS_REC_DELETER_ACTIVE (thread_p, rec_header))
    {
      /* Record was deleted by an active transaction. Current transaction must wait until the deleter completes. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE, PERF_SNAPSHOT_RECORD_DELETED_OTHER_TRAN,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return DELETE_RECORD_DELETE_IN_PROGRESS;
    }
      else
    {
      /* Record was already deleted and the deleter has committed. Cannot be updated by current transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (vacuum_is_mvccid_vacuumed (rec_header->mvcc_del_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED_LOST, PERF_SNAPSHOT_INVISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DELETE,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED, PERF_SNAPSHOT_INVISIBLE);
        }
        }
      return DELETE_RECORD_DELETED;
    }
    }
}

/*
 * mvcc_satisfies_dirty () - Check whether a record is visible considering following effects:
 *                - committed transactions
 *                - in progress transactions
 *                - previous commands of current transaction
 *
 *   return      : TOO_OLD_FOR_SNAPSHOT, if the record is deleted and deleter is either me or is not active.
 *             SNAPSHOT_SATISFIED, if the record is not deleted and deleter is neither me nor is not active.
 *   thread_p (in)   : thread entry
 *   rec_header (in) : the record header
 *   snapshot (in)   : the snapshot used for record validation
 *
 * NOTE: Besides returning snapshot result, when the result is SNAPSHOT_SATISFIED, the function have also have side
 *   effects, changing snapshot->lowest_active_mvccid and snapshot->highest_completed_mvccid.
 *   If record is recently inserted and inserter is considered active, its MVCCID is saved in
 *   snapshot->lowest_active_mvccid.
 *   If record is recently deleted and deleter is considered active, its MVCCID is saved in
 *   snapshot->highest_completed_mvccid.
 *   Otherwise, the two values are set to MVCCID_NULL.
 *
 * NOTE: The snapshot argument can never be the transaction snapshot!
 */
MVCC_SATISFIES_SNAPSHOT_RESULT
mvcc_satisfies_dirty (THREAD_ENTRY * thread_p, MVCC_REC_HEADER * rec_header, MVCC_SNAPSHOT * snapshot)
{
  assert (rec_header != NULL && snapshot != NULL);

  snapshot->lowest_active_mvccid = MVCCID_NULL;
  snapshot->highest_completed_mvccid = MVCCID_NULL;

  if (!MVCC_IS_HEADER_DELID_VALID (rec_header))
    {
      /* Record was not deleted */
      if (!MVCC_IS_FLAG_SET (rec_header, OR_MVCC_FLAG_VALID_INSID))
    {
      /* Record was inserted and is visible for all transactions */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY, PERF_SNAPSHOT_RECORD_INSERTED_VACUUMED,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return SNAPSHOT_SATISFIED;
    }
      else if (MVCC_IS_REC_INSERTED_BY_ME (thread_p, rec_header))
    {
      /* Record was inserted by current transaction and is visible */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY, PERF_SNAPSHOT_RECORD_INSERTED_CURR_TRAN,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return SNAPSHOT_SATISFIED;
    }
      else if (MVCC_IS_REC_INSERTER_ACTIVE (thread_p, rec_header))
    {
      /* Record is inserted by an active transaction and is visible */
      snapshot->lowest_active_mvccid = MVCC_GET_INSID (rec_header);
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY, PERF_SNAPSHOT_RECORD_INSERTED_OTHER_TRAN,
                     PERF_SNAPSHOT_VISIBLE);
        }
      return SNAPSHOT_SATISFIED;
    }
      else
    {
      /* Record is inserted by committed transaction. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (rec_header->mvcc_ins_id != MVCCID_ALL_VISIBLE && vacuum_is_mvccid_vacuumed (rec_header->mvcc_ins_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED_LOST, PERF_SNAPSHOT_VISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY,
                     PERF_SNAPSHOT_RECORD_INSERTED_COMMITED, PERF_SNAPSHOT_VISIBLE);
        }
        }
      return SNAPSHOT_SATISFIED;
    }
    }
  else
    {
      /* Record was already deleted */
      if (MVCC_IS_REC_DELETED_BY_ME (thread_p, rec_header))
    {
      /* Record was deleted by current transaction and is not visible */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY, PERF_SNAPSHOT_RECORD_DELETED_CURR_TRAN,
                     PERF_SNAPSHOT_INVISIBLE);
        }
      return TOO_OLD_FOR_SNAPSHOT;
    }
      else if (MVCC_IS_REC_DELETER_ACTIVE (thread_p, rec_header))
    {
      /* Record was deleted by other active transaction and is still visible */
      snapshot->highest_completed_mvccid = rec_header->mvcc_del_id;
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY, PERF_SNAPSHOT_RECORD_DELETED_OTHER_TRAN,
                     PERF_SNAPSHOT_VISIBLE);
        }

      return SNAPSHOT_SATISFIED;
    }
      else
    {
      /* Record was already deleted and the deleter has committed. */
      if (perfmon_is_perf_tracking_and_active (PERFMON_ACTIVATION_FLAG_MVCC_SNAPSHOT))
        {
          if (vacuum_is_mvccid_vacuumed (rec_header->mvcc_del_id))
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED_LOST, PERF_SNAPSHOT_INVISIBLE);
        }
          else
        {
          perfmon_mvcc_snapshot (thread_p, PERF_SNAPSHOT_SATISFIES_DIRTY,
                     PERF_SNAPSHOT_RECORD_DELETED_COMMITTED, PERF_SNAPSHOT_INVISIBLE);
        }
        }
      return TOO_OLD_FOR_SNAPSHOT;
    }
    }
}

/*
* mvcc_is_mvcc_disabled_class () - MVCC is disabled for root class and
*                   _db_serial, db_partition. this is a slow operation so cache this result.
*
* return      : True if MVCC is disabled for class.
* thread_p (in)  : Thread entry.
* class_oid (in) : Class OID.
*/
bool
mvcc_is_mvcc_disabled_class (const OID * class_oid)
{
  assert (class_oid != NULL);

  if (OID_ISNULL (class_oid) || OID_IS_ROOTOID (class_oid))
    {
      /* MVCC is disabled for root class */
      return true;
    }

  if (oid_is_serial (class_oid))
    {
      return true;
    }

  if (oid_check_cached_class_oid (OID_CACHE_COLLATION_CLASS_ID, class_oid))
    {
      return true;
    }

  if (oid_check_cached_class_oid (OID_CACHE_HA_APPLY_INFO_CLASS_ID, class_oid))
    {
      return true;
    }

  return false;
}

// *INDENT-OFF*
mvcc_snapshot::mvcc_snapshot ()
  : lowest_active_mvccid (MVCCID_NULL)
  , highest_completed_mvccid (MVCCID_NULL)
  , m_active_mvccs ()
  , snapshot_fnc (NULL)
  , valid (false)
{
}

void
mvcc_snapshot::reset ()
{
  snapshot_fnc = NULL;
  lowest_active_mvccid = MVCCID_NULL;
  highest_completed_mvccid = MVCCID_NULL;

  m_active_mvccs.reset ();

  valid = false;
}

void
mvcc_snapshot::copy_to (mvcc_snapshot & dest) const
{
  dest.m_active_mvccs.initialize ();
  m_active_mvccs.copy_to (dest.m_active_mvccs, mvcc_active_tran::copy_safety::THREAD_SAFE);

  dest.lowest_active_mvccid = lowest_active_mvccid;
  dest.highest_completed_mvccid = highest_completed_mvccid;
  dest.snapshot_fnc = snapshot_fnc;
  dest.valid = valid;
}

mvcc_info::mvcc_info ()
  : snapshot ()
  , id (MVCCID_NULL)
  , recent_snapshot_lowest_active_mvccid (MVCCID_NULL)
  , sub_ids ()
{
}

void
mvcc_info::init ()
{
  new (this) mvcc_info ();
}

void
mvcc_info::reset ()
{
  snapshot.reset ();
  id = MVCCID_NULL;
  recent_snapshot_lowest_active_mvccid = MVCCID_NULL;
  sub_ids.clear ();
}

void
mvcc_info::copy_to (mvcc_info & dest) const
{
  this->snapshot.copy_to (dest.snapshot);
  dest.id = this->id;
  dest.recent_snapshot_lowest_active_mvccid = this->recent_snapshot_lowest_active_mvccid;
  dest.sub_ids = this->sub_ids;
  dest.is_sub_active = this->is_sub_active;
}
// *INDENT-ON*