Skip to content

File transaction_transient.cpp

File List > cubrid > src > transaction > transaction_transient.cpp

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.
 *
 */

#include "transaction_transient.hpp"

#include "es.h"
#include "heap_file.h"
#include "lob_locator.hpp"
#include "oid.h"
#include "memory_hash.h"
#include "memory_private_allocator.hpp"
#include "rb_tree.h"
#include "string_buffer.hpp"
#include "vacuum.h"

#include <cstring>  // for std::strcpy
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
//
// TODO: Create a "transaction transient" abstract interface and split this into multiple files
//

//
// Modified classes
//

tx_transient_class_entry::tx_transient_class_entry (const char *class_name, const OID &class_oid, const LOG_LSA &lsa)
  : m_class_oid (class_oid)
  , m_last_modified_lsa (lsa)
  , m_class_name (class_name)
{
}

const char *
tx_transient_class_entry::get_classname () const
{
  return m_class_name.c_str ();
}

void
tx_transient_class_registry::add (const char *classname, const OID &class_oid, const LOG_LSA &lsa)
{
  assert (classname != NULL);
  assert (!OID_ISNULL (&class_oid));
  assert (class_oid.volid >= 0);      // is not temp volume

  for (auto &it : m_list)
    {
      if (it.m_class_name == classname && OID_EQ (&it.m_class_oid, &class_oid))
    {
      it.m_last_modified_lsa = lsa;
      return;
    }
    }
  m_list.emplace_front (classname, class_oid, lsa);
}

bool
tx_transient_class_registry::has_class (const OID &class_oid) const
{
  for (auto &it : m_list)
    {
      if (OID_EQ (&class_oid, &it.m_class_oid))
    {
      return true;
    }
    }
  return false;
}

char *
tx_transient_class_registry::to_string () const
{
  const size_t DEFAULT_STRBUF_SIZE = 128;
  string_buffer strbuf (cubmem::PRIVATE_BLOCK_ALLOCATOR, DEFAULT_STRBUF_SIZE);

  strbuf ("{");
  for (list_type::const_iterator it = m_list.cbegin (); it != m_list.cend (); it++)
    {
      if (it != m_list.cbegin ())
    {
      strbuf (", ");
    }
      strbuf ("name=%s, oid=%d|%d|%d, lsa=%lld|%d", it->m_class_name.c_str (), OID_AS_ARGS (&it->m_class_oid),
          LSA_AS_ARGS (&it->m_last_modified_lsa));
    }
  strbuf ("}");
  return strbuf.release_ptr ();
}

void
tx_transient_class_registry::map (const map_func_type &func) const
{
  bool stop = false;
  for (const auto &it : m_list)
    {
      func (it, stop);
      if (stop)
    {
      return;
    }
    }
}

bool
tx_transient_class_registry::empty () const
{
  return m_list.empty ();
}

void
tx_transient_class_registry::decache_heap_repr (const LOG_LSA &downto_lsa)
{
  for (auto &it : m_list)
    {
      assert (!it.m_last_modified_lsa.is_null ());
      if (downto_lsa.is_null () || it.m_last_modified_lsa > downto_lsa)
    {
      (void) heap_classrepr_decache (NULL, &it.m_class_oid);
    }
    }
}

void
tx_transient_class_registry::clear ()
{
  m_list.clear ();
}

//
// Lobs
//

struct lob_savepoint_entry
{
  LOB_LOCATOR_STATE state;
  LOG_LSA savept_lsa;
  lob_savepoint_entry *prev;
  ES_URI locator;
};

struct lob_locator_entry
{
  /* RB_ENTRY defines red-black tree node header for this structure. see base/rb_tree.h for more information. */
  RB_ENTRY (lob_locator_entry) head;
  lob_savepoint_entry *top;
  /* key_hash is used to reduce the number of strcmp. see the comment of lob_locator_cmp for more information */
  int key_hash;
  std::string m_key;
};

static void lob_locator_put_meta (const char *locator, std::string &meta_name);  // copy meta from locator
static void lob_locator_free (lob_locator_entry *&entry);
static int lob_locator_cmp (const lob_locator_entry *e1, const lob_locator_entry *e2);
/* RB_PROTOTYPE_STATIC declares red-black tree functions. see base/rb_tree.h */
RB_PROTOTYPE_STATIC (lob_rb_root, lob_locator_entry, head, lob_locator_cmp);

int
xtx_add_lob_locator (cubthread::entry *thread_p, const char *locator, LOB_LOCATOR_STATE state)
{
  int tran_index;
  LOG_TDES *tdes = NULL;
  lob_locator_entry *entry = NULL;
  lob_savepoint_entry *savept = NULL;

  assert (lob_locator_is_valid (locator));

  tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
  tdes = LOG_FIND_TDES (tran_index);
  if (tdes == NULL)
    {
      er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_UNKNOWN_TRANINDEX, 1, tran_index);
      return ER_LOG_UNKNOWN_TRANINDEX;
    }

  entry = new lob_locator_entry ();
  savept = new lob_savepoint_entry ();

  entry->top = savept;
  entry->m_key = lob_locator_key (locator);
  entry->key_hash = (int) mht_5strhash (entry->m_key.c_str (), INT_MAX);

  savept->state = state;
  savept->savept_lsa = LSA_LT (&tdes->savept_lsa, &tdes->topop_lsa) ? tdes->topop_lsa : tdes->savept_lsa;
  savept->prev = NULL;
  strlcpy (savept->locator, locator, sizeof (ES_URI));

  /* Insert entry to the red-black tree (see base/rb_tree.h) */
  RB_INSERT (lob_rb_root, &tdes->lob_locator_root, entry);

  return NO_ERROR;
}

LOB_LOCATOR_STATE
xtx_find_lob_locator (cubthread::entry *thread_p, const char *locator, char *real_locator)
{
  int tran_index;
  LOG_TDES *tdes;

  assert (lob_locator_is_valid (locator));
  assert (real_locator != NULL);

  tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
  tdes = LOG_FIND_TDES (tran_index);

  if (tdes != NULL)
    {
      lob_locator_entry *entry, find;

      find.m_key = lob_locator_key (locator);
      find.key_hash = (int) mht_5strhash (find.m_key.c_str (), INT_MAX);
      /* Find entry from red-black tree (see base/rb_tree.h) */
      entry = RB_FIND (lob_rb_root, &tdes->lob_locator_root, &find);
      if (entry != NULL)
    {
      std::strcpy (real_locator, entry->top->locator);
      return entry->top->state;
    }
    }
  else
    {
      // is this acceptable?
    }

  std::strcpy (real_locator, locator);
  return LOB_NOT_FOUND;
}

int
xtx_change_state_of_locator (cubthread::entry *thread_p, const char *locator, const char *new_locator,
                 LOB_LOCATOR_STATE state)
{
  int tran_index;
  LOG_TDES *tdes;
  lob_locator_entry *entry, find;

  assert (lob_locator_is_valid (locator));

  tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
  tdes = LOG_FIND_TDES (tran_index);
  if (tdes == NULL)
    {
      er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_UNKNOWN_TRANINDEX, 1, tran_index);
      return ER_LOG_UNKNOWN_TRANINDEX;
    }

  find.m_key = lob_locator_key (locator);
  find.key_hash = (int) mht_5strhash (find.m_key.c_str (), INT_MAX);
  entry = RB_FIND (lob_rb_root, &tdes->lob_locator_root, &find);

  if (entry != NULL)
    {
      LOG_LSA last_lsa;

      last_lsa = LSA_GE (&tdes->savept_lsa, &tdes->topop_lsa) ? tdes->savept_lsa : tdes->topop_lsa;

      /* if it is created prior to current savepoint, push the previous state in the savepoint list */
      if (LSA_LT (&entry->top->savept_lsa, &last_lsa))
    {
      lob_savepoint_entry *savept = new lob_savepoint_entry ();

      /* copy structure (avoid redundant memory copy) */
      savept->state = entry->top->state;
      savept->savept_lsa = entry->top->savept_lsa;
      std::strcpy (savept->locator, entry->top->locator);
      savept->prev = entry->top;
      entry->top = savept;
    }

      /* set the current state */
      if (new_locator != NULL)
    {
      strlcpy (entry->top->locator, new_locator, sizeof (ES_URI));
    }
      entry->top->state = state;
      entry->top->savept_lsa = last_lsa;
    }

  return NO_ERROR;
}

/*
 * xlog_drop_lob_locator -
 *
 * return: error code
 *
 *   thread_p(in):
 *   locator(in):
 *
 * NOTE:
 */
int
xtx_drop_lob_locator (cubthread::entry *thread_p, const char *locator)
{
  int tran_index;
  LOG_TDES *tdes;
  lob_locator_entry *entry, find;

  assert (lob_locator_is_valid (locator));

  tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
  tdes = LOG_FIND_TDES (tran_index);
  if (tdes == NULL)
    {
      er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_UNKNOWN_TRANINDEX, 1, tran_index);
      return ER_LOG_UNKNOWN_TRANINDEX;
    }

  find.m_key = lob_locator_key (locator);
  find.key_hash = (int) mht_5strhash (find.m_key.c_str (), INT_MAX);
  /* Remove entry that matches 'find' entry from the red-black tree. see base/rb_tree.h for more information */
  entry = RB_FIND (lob_rb_root, &tdes->lob_locator_root, &find);
  if (entry != NULL)
    {
      entry = RB_REMOVE (lob_rb_root, &tdes->lob_locator_root, entry);
      if (entry != NULL)
    {
      lob_locator_free (entry);
    }
    }

  return NO_ERROR;
}

void
lob_locator_put_meta (const char *locator, std::string &meta_name)
{
  const char *keyp = lob_locator_key (locator);
  const char *metap = lob_locator_meta (locator);

  if (keyp - 1 <= metap)
    {
      assert (false);
      return;
    }
  size_t diff = (size_t) (keyp - metap - 1);

  meta_name.append (metap, diff);
  meta_name[diff] = '\0';
}

static void
lob_locator_free (lob_locator_entry *&entry)
{
  while (entry->top != NULL)
    {
      lob_savepoint_entry *savept;

      savept = entry->top;
      entry->top = savept->prev;
      delete savept;
    }

  delete entry;
  entry = NULL;
}

void
tx_lob_locator_clear (cubthread::entry *thread_p, log_tdes *tdes, bool at_commit, LOG_LSA *savept_lsa)
{
  lob_locator_entry *entry, *next;
  bool need_to_delete;

  if (tdes == NULL)
    {
      return;
    }

  for (entry = RB_MIN (lob_rb_root, &tdes->lob_locator_root); entry != NULL; entry = next)
    {
      /* setup next link before destroy */
      next = RB_NEXT (lob_rb_root, &tdes->lob_locator_root, entry);

      need_to_delete = false;

      if (at_commit)
    {
      if (entry->top->state != LOB_PERMANENT_CREATED)
        {
          need_to_delete = true;
        }
    }
      else          /* rollback */
    {
      /* at partial rollback, pop the previous states in the savepoint list util it meets the rollback savepoint */
      if (savept_lsa != NULL)
        {
          lob_savepoint_entry *savept, *tmp;

          assert (entry->top != NULL);
          savept = entry->top->prev;

          while (savept != NULL)
        {
          if (LSA_LT (&entry->top->savept_lsa, savept_lsa))
            {
              break;
            }

          /* rollback file renaming */
          if (strcmp (entry->top->locator, savept->locator) != 0)
            {
              std::string meta_name;

              assert (lob_locator_is_valid (savept->locator));
              lob_locator_put_meta (savept->locator, meta_name);
              /* ignore return value */
              (void) es_rename_file (entry->top->locator, meta_name.c_str (), savept->locator);
            }
          tmp = entry->top;
          entry->top = savept;
          savept = savept->prev;
          delete tmp;
        }
        }

      /* delete the locator to be created */
      if ((savept_lsa == NULL || LSA_GE (&entry->top->savept_lsa, savept_lsa))
          && entry->top->state != LOB_TRANSIENT_DELETED)
        {
          need_to_delete = true;
        }
    }

      /* remove from the locator tree */
      if (need_to_delete)
    {
#if defined (SERVER_MODE)
      if (at_commit && entry->top->state == LOB_PERMANENT_DELETED)
        {
          vacuum_notify_es_deleted (thread_p, entry->top->locator);
        }
      else
        {
          /* The file is created and rolled-back and it is not visible to anyone. Delete it directly without
           * notifying vacuum. */
          (void) es_delete_file (entry->top->locator);
        }
#else /* !SERVER_MODE */
      /* SA_MODE */
      (void) es_delete_file (entry->top->locator);
#endif /* !SERVER_MODE */
      RB_REMOVE (lob_rb_root, &tdes->lob_locator_root, entry);
      lob_locator_free (entry);
    }
    }

  /* at the end of transaction, free the locator list */
  if (savept_lsa == NULL)
    {
      for (entry = RB_MIN (lob_rb_root, &tdes->lob_locator_root); entry != NULL; entry = next)
    {
      next = RB_NEXT (lob_rb_root, &tdes->lob_locator_root, entry);
      RB_REMOVE (lob_rb_root, &tdes->lob_locator_root, entry);
      lob_locator_free (entry);
    }
      assert (RB_EMPTY (&tdes->lob_locator_root));
    }
}

static int
lob_locator_cmp (const lob_locator_entry *e1, const lob_locator_entry *e2)
{
  if (e1->key_hash != e2->key_hash)
    {
      return e1->key_hash - e2->key_hash;
    }
  else
    {
      return e1->m_key.compare (e2->m_key);
    }
}

/*
 * Below macro generates lob rb tree functions (see base/rb_tree.h)
 * Note: semi-colon is intentionally added to be more beautiful
 */
RB_GENERATE_STATIC (lob_rb_root, lob_locator_entry, head, lob_locator_cmp);

void
lob_rb_root::init ()
{
  RB_INIT (this);
}