From 1f9ef5f3db410d84b6d4912739c1c3ed6109373f Mon Sep 17 00:00:00 2001
From: hustjieke <gaoriyao1@gmail.com>
Date: Mon, 24 Oct 2022 12:08:43 +0000
Subject: [PATCH] fix(tianmu): fix format using clang-format #792

---
 storage/tianmu/core/engine.cpp                |    2 +-
 storage/tianmu/core/rc_attr_typeinfo.h        |  131 ++
 storage/tianmu/core/sorter3.cpp               |    2 +-
 storage/tianmu/handler/tianmu_handler.cpp     | 1742 +++++++++++++++++
 storage/tianmu/handler/tianmu_handler_com.cpp |  761 +++++++
 storage/tianmu/types/rc_data_types.cpp        |  140 ++
 storage/tianmu/types/rc_num.cpp               |  605 ++++++
 storage/tianmu/types/rc_num.h                 |  118 ++
 storage/tianmu/types/rc_value_object.cpp      |  165 ++
 9 files changed, 3664 insertions(+), 2 deletions(-)
 create mode 100644 storage/tianmu/core/rc_attr_typeinfo.h
 create mode 100644 storage/tianmu/handler/tianmu_handler.cpp
 create mode 100644 storage/tianmu/handler/tianmu_handler_com.cpp
 create mode 100644 storage/tianmu/types/rc_data_types.cpp
 create mode 100644 storage/tianmu/types/rc_num.cpp
 create mode 100644 storage/tianmu/types/rc_num.h
 create mode 100644 storage/tianmu/types/rc_value_object.cpp

diff --git a/storage/tianmu/core/engine.cpp b/storage/tianmu/core/engine.cpp
index 2d547d287..ab20bb40c 100644
--- a/storage/tianmu/core/engine.cpp
+++ b/storage/tianmu/core/engine.cpp
@@ -1857,7 +1857,7 @@ common::TianmuError Engine::RunLoader(THD *thd, sql_exchange *ex, TABLE_LIST *ta
   }
 
   // if (thd->transaction.stmt.cannot_safely_rollback())
-  //     thd->transaction.all.mark_modified_non_trans_table();
+  //    thd->transaction.all.mark_modified_non_trans_table();
 
   if (transactional_table) {
     if (tianmu_e == common::ErrorCode::SUCCESS)
diff --git a/storage/tianmu/core/rc_attr_typeinfo.h b/storage/tianmu/core/rc_attr_typeinfo.h
new file mode 100644
index 000000000..b96913559
--- /dev/null
+++ b/storage/tianmu/core/rc_attr_typeinfo.h
@@ -0,0 +1,131 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+#ifndef TIANMU_CORE_RC_ATTR_TYPEINFO_H_
+#define TIANMU_CORE_RC_ATTR_TYPEINFO_H_
+#pragma once
+
+#include <bitset>
+
+#include "common/common_definitions.h"
+
+namespace Tianmu {
+namespace types {
+class RCDataType;
+}  // namespace types
+namespace core {
+class ATI {
+ public:
+  static int TextSize(common::CT attrt, uint precision, int scale, DTCollation col = DTCollation());
+
+  static bool IsInteger32Type(common::CT attr_type) {
+    return attr_type == common::CT::INT || attr_type == common::CT::BYTEINT || attr_type == common::CT::SMALLINT ||
+           attr_type == common::CT::MEDIUMINT;
+  }
+
+  static bool IsIntegerType(common::CT attr_type) {
+    return IsInteger32Type(attr_type) || attr_type == common::CT::BIGINT;
+  }
+  static bool IsFixedNumericType(common::CT attr_type) {
+    return IsInteger32Type(attr_type) || attr_type == common::CT::BIGINT || attr_type == common::CT::NUM;
+  }
+
+  static bool IsRealType(common::CT attr_type) {
+    return attr_type == common::CT::FLOAT || attr_type == common::CT::REAL;
+  }
+  static bool IsNumericType(common::CT attr_type) {
+    return IsInteger32Type(attr_type) || attr_type == common::CT::BIGINT || attr_type == common::CT::NUM ||
+           attr_type == common::CT::FLOAT || attr_type == common::CT::REAL;
+  }
+
+  static bool IsBinType(common::CT attr_type) {
+    return attr_type == common::CT::BYTE || attr_type == common::CT::VARBYTE || attr_type == common::CT::BIN;
+  }
+
+  static bool IsTxtType(common::CT attr_type) {
+    return attr_type == common::CT::STRING || attr_type == common::CT::VARCHAR || attr_type == common::CT::LONGTEXT;
+  }
+
+  static bool IsCharType(common::CT attr_type) { return attr_type == common::CT::STRING; }
+  static bool IsStringType(common::CT attr_type) {
+    return attr_type == common::CT::STRING || attr_type == common::CT::VARCHAR || attr_type == common::CT::LONGTEXT ||
+           IsBinType(attr_type);
+  }
+
+  static bool IsDateTimeType(common::CT attr_type) {
+    return attr_type == common::CT::DATE || attr_type == common::CT::TIME || attr_type == common::CT::YEAR ||
+           attr_type == common::CT::DATETIME || attr_type == common::CT::TIMESTAMP;
+  }
+
+  static bool IsDateTimeNType(common::CT attr_type) {
+    return attr_type == common::CT::TIME_N || attr_type == common::CT::DATETIME_N ||
+           attr_type == common::CT::TIMESTAMP_N;
+  }
+};
+
+class AttributeTypeInfo {
+ public:
+  enum class enumATI {
+    NOT_NULL = 0,
+    AUTO_INC = 1,
+    BLOOM_FILTER = 2,
+  };
+
+  AttributeTypeInfo(common::CT attrt, bool notnull, uint precision = 0, ushort scale = 0, bool auto_inc = false,
+                    DTCollation collation = DTCollation(), common::PackFmt fmt = common::PackFmt::DEFAULT,
+                    bool filter = false)
+      : attrt(attrt), fmt(fmt), precision(precision), scale(scale), collation(collation) {
+    flag[static_cast<int>(enumATI::NOT_NULL)] = notnull;
+    flag[static_cast<int>(enumATI::BLOOM_FILTER)] = filter;
+    flag[static_cast<int>(enumATI::AUTO_INC)] = auto_inc;
+
+    // lookup only applies to string type
+    if (attrt != common::CT::STRING && attrt != common::CT::VARCHAR && Lookup())
+      fmt = common::PackFmt::DEFAULT;
+  }
+  common::CT Type() const { return attrt; }
+  common::PackType GetPackType() const {
+    return ATI::IsDateTimeType(attrt) || ATI::IsNumericType(attrt) || Lookup() ? common::PackType::INT
+                                                                               : common::PackType::STR;
+  }
+  uint Precision() const { return precision; }
+  ushort Scale() const { return scale; }
+  uint CharLen() const { return precision / collation.collation->mbmaxlen; }
+  bool NotNull() const { return flag[static_cast<int>(enumATI::NOT_NULL)]; }
+  bool AutoInc() const { return flag[static_cast<int>(enumATI::AUTO_INC)]; }
+  void SetCollation(const DTCollation &collation) { this->collation = collation; }
+  void SetCollation(CHARSET_INFO *charset_info) { this->collation.set(charset_info); }
+  DTCollation GetCollation() const { return collation; }
+  CHARSET_INFO *CharsetInfo() const { return const_cast<CHARSET_INFO *>(this->collation.collation); }
+  const types::RCDataType &ValuePrototype() const;
+  common::PackFmt Fmt() const { return fmt; }
+  bool Lookup() const { return fmt == common::PackFmt::LOOKUP; }
+  unsigned char Flag() const { return flag.to_ulong(); }
+  void SetFlag(unsigned char v) { flag = std::bitset<std::numeric_limits<unsigned char>::digits>(v); }
+
+ private:
+  common::CT attrt;
+  common::PackFmt fmt;
+  uint precision;
+  int scale;
+  DTCollation collation;
+
+  std::bitset<std::numeric_limits<unsigned char>::digits> flag;
+};
+}  // namespace core
+}  // namespace Tianmu
+
+#endif  // TIANMU_CORE_RC_ATTR_TYPEINFO_H_
diff --git a/storage/tianmu/core/sorter3.cpp b/storage/tianmu/core/sorter3.cpp
index 7500394db..91308d4c2 100644
--- a/storage/tianmu/core/sorter3.cpp
+++ b/storage/tianmu/core/sorter3.cpp
@@ -518,4 +518,4 @@ bool SorterLimit::PutValue(Sorter3 *s) {
   return true;
 }
 }  // namespace core
-}  // namespace Tianmu
\ No newline at end of file
+}  // namespace Tianmu
diff --git a/storage/tianmu/handler/tianmu_handler.cpp b/storage/tianmu/handler/tianmu_handler.cpp
new file mode 100644
index 000000000..5065a1848
--- /dev/null
+++ b/storage/tianmu/handler/tianmu_handler.cpp
@@ -0,0 +1,1742 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+
+#include "tianmu_handler.h"
+
+#include <iostream>
+
+#include "mysql/plugin.h"
+
+#include "common/assert.h"
+#include "common/exception.h"
+#include "core/compilation_tools.h"
+#include "core/compiled_query.h"
+#include "core/temp_table.h"
+#include "core/tools.h"
+#include "core/transaction.h"
+#include "core/value.h"
+#include "system/configuration.h"
+#include "util/fs.h"
+
+#define MYSQL_SERVER 1
+
+namespace Tianmu {
+namespace dbhandler {
+
+const Alter_inplace_info::HA_ALTER_FLAGS TianmuHandler::TIANMU_SUPPORTED_ALTER_ADD_DROP_ORDER =
+    Alter_inplace_info::ADD_COLUMN | Alter_inplace_info::DROP_COLUMN | Alter_inplace_info::ALTER_STORED_COLUMN_ORDER;
+const Alter_inplace_info::HA_ALTER_FLAGS TianmuHandler::TIANMU_SUPPORTED_ALTER_COLUMN_NAME =
+    Alter_inplace_info::ALTER_COLUMN_DEFAULT | Alter_inplace_info::ALTER_COLUMN_NAME;
+/////////////////////////////////////////////////////////////////////
+//
+// NOTICE: ALL EXCEPTIONS SHOULD BE CAUGHT in the handler API!!!
+//         MySQL doesn't use exception.
+//
+/////////////////////////////////////////////////////////////////////
+
+/*
+ Example of simple lock controls. The "share" it creates is structure we will
+ pass to each rcbase handler. Do you have to have one of these? Well, you have
+ pieces that are used for locking, and they are needed to function.
+ */
+
+my_bool rcbase_query_caching_of_table_permitted(THD *thd, [[maybe_unused]] char *full_name,
+                                                [[maybe_unused]] uint full_name_len,
+                                                [[maybe_unused]] ulonglong *unused) {
+  if (!thd_test_options(thd, (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+    return ((my_bool)TRUE);
+  return ((my_bool)FALSE);
+}
+
+static core::Value GetValueFromField(Field *f) {
+  core::Value v;
+
+  if (f->is_null())
+    return v;
+
+  switch (f->type()) {
+    case MYSQL_TYPE_TINY:
+    case MYSQL_TYPE_SHORT:
+    case MYSQL_TYPE_LONG:
+    case MYSQL_TYPE_INT24:
+    case MYSQL_TYPE_LONGLONG:
+      v.SetInt(f->val_int());
+      break;
+    case MYSQL_TYPE_DECIMAL:
+    case MYSQL_TYPE_FLOAT:
+    case MYSQL_TYPE_DOUBLE:
+      v.SetDouble(f->val_real());
+      break;
+    case MYSQL_TYPE_NEWDECIMAL: {
+      auto dec_f = dynamic_cast<Field_new_decimal *>(f);
+      v.SetInt(std::lround(dec_f->val_real() * types::PowOfTen(dec_f->dec)));
+      break;
+    }
+    case MYSQL_TYPE_TIMESTAMP: {
+      MYSQL_TIME my_time;
+      std::memset(&my_time, 0, sizeof(my_time));
+      f->get_time(&my_time);
+      // convert to UTC
+      if (!common::IsTimeStampZero(my_time)) {
+        my_bool myb;
+        my_time_t secs_utc = current_txn_->Thd()->variables.time_zone->TIME_to_gmt_sec(&my_time, &myb);
+        common::GMTSec2GMTTime(&my_time, secs_utc);
+      }
+      types::DT dt = {};
+      dt.year = my_time.year;
+      dt.month = my_time.month;
+      dt.day = my_time.day;
+      dt.hour = my_time.hour;
+      dt.minute = my_time.minute;
+      dt.second = my_time.second;
+      v.SetInt(dt.val);
+      break;
+    }
+    case MYSQL_TYPE_TIME:
+    case MYSQL_TYPE_TIME2:
+    case MYSQL_TYPE_DATE:
+    case MYSQL_TYPE_DATETIME:
+    case MYSQL_TYPE_NEWDATE:
+    case MYSQL_TYPE_TIMESTAMP2:
+    case MYSQL_TYPE_DATETIME2: {
+      MYSQL_TIME my_time;
+      std::memset(&my_time, 0, sizeof(my_time));
+      f->get_time(&my_time);
+      types::DT dt = {};
+      dt.year = my_time.year;
+      dt.month = my_time.month;
+      dt.day = my_time.day;
+      dt.hour = my_time.hour;
+      dt.minute = my_time.minute;
+      dt.second = my_time.second;
+      v.SetInt(dt.val);
+      break;
+    }
+    case MYSQL_TYPE_YEAR:  // what the hell?
+    {
+      types::DT dt = {};
+      dt.year = f->val_int();
+      v.SetInt(dt.val);
+      break;
+    }
+    case MYSQL_TYPE_VARCHAR:
+    case MYSQL_TYPE_TINY_BLOB:
+    case MYSQL_TYPE_MEDIUM_BLOB:
+    case MYSQL_TYPE_LONG_BLOB:
+    case MYSQL_TYPE_BLOB:
+    case MYSQL_TYPE_VAR_STRING:
+    case MYSQL_TYPE_STRING: {
+      String str;
+      f->val_str(&str);
+      v.SetString(const_cast<char *>(str.ptr()), str.length());
+      break;
+    }
+    case MYSQL_TYPE_SET:
+    case MYSQL_TYPE_ENUM:
+    case MYSQL_TYPE_GEOMETRY:
+    case MYSQL_TYPE_NULL:
+    case MYSQL_TYPE_BIT:
+    default:
+      throw common::Exception("unsupported mysql type " + std::to_string(f->type()));
+      break;
+  }
+  return v;
+}
+
+TianmuHandler::TianmuHandler(handlerton *hton, TABLE_SHARE *table_arg) : handler(hton, table_arg) {
+  ref_length = sizeof(uint64_t);
+}
+
+const char **TianmuHandler::bas_ext() const {
+  static const char *ha_rcbase_exts[] = {common::TIANMU_EXT, 0};
+  return ha_rcbase_exts;
+}
+
+namespace {
+std::vector<bool> GetAttrsUseIndicator(TABLE *table) {
+  int col_id = 0;
+  std::vector<bool> attr_uses;
+  for (Field **field = table->field; *field; ++field, ++col_id) {
+    if (bitmap_is_set(table->read_set, col_id) || bitmap_is_set(table->write_set, col_id))
+      attr_uses.push_back(true);
+    else
+      attr_uses.push_back(false);
+  }
+  return attr_uses;
+}
+}  // namespace
+
+static bool is_delay_insert(THD *thd) {
+  return tianmu_sysvar_insert_delayed &&
+         (thd_sql_command(thd) == SQLCOM_INSERT || thd_sql_command(thd) == SQLCOM_INSERT_SELECT) &&
+         thd->lex->duplicates != DUP_UPDATE;
+}
+
+/*
+ The idea with handler::store_lock() is the following:
+
+ The statement decided which locks we should need for the table
+ for updates/deletes/inserts we get WRITE locks, for SELECT... we get
+ read locks.
+
+ Before adding the lock into the table lock handler (see thr_lock.c)
+ mysqld calls store lock with the requested locks.  Store lock can now
+ modify a write lock to a read lock (or some other lock), ignore the
+ lock (if we don't want to use MySQL table locks at all) or add locks
+ for many tables (like we do when we are using a MERGE handler).
+
+ Berkeley DB for example  changes all WRITE locks to TL_WRITE_ALLOW_WRITE
+ (which signals that we are doing WRITES, but we are still allowing other
+ reader's and writer's.
+
+ When releasing locks, store_lock() are also called. In this case one
+ usually doesn't have to do anything.
+
+ In some exceptional cases MySQL may send a request for a TL_IGNORE;
+ This means that we are requesting the same lock as last time and this
+ should also be ignored. (This may happen when someone does a flush
+ table when we have opened a part of the tables, in which case mysqld
+ closes and reopens the tables and tries to get the same locks at last
+ time).  In the future we will probably try to remove this.
+
+ Called from lock.cc by get_lock_data().
+ */
+THR_LOCK_DATA **TianmuHandler::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) {
+  if (lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) {
+    if (is_delay_insert(thd))
+      lock_type = TL_READ;
+    else
+      lock_type = TL_WRITE_CONCURRENT_INSERT;
+  }
+
+  if (lock_type != TL_IGNORE && m_lock.type == TL_UNLOCK)
+    m_lock.type = lock_type;
+  *to++ = &m_lock;
+  return to;
+}
+
+/*
+ First you should go read the section "locking functions for mysql" in
+ lock.cc to understand this.
+ This create a lock on the table. If you are implementing a storage engine
+ that can handle transacations look at ha_berkely.cc to see how you will
+ want to goo about doing this. Otherwise you should consider calling flock()
+ here.
+
+ Called from lock.cc by lock_external() and unlock_external(). Also called
+ from sql_table.cc by copy_data_between_tables().
+ */
+int TianmuHandler::external_lock(THD *thd, int lock_type) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+
+  // static const char *ss[] = { "READ LOCK", "WRITE LOCK", "UNLOCK", };
+  // rclog << lock << "external lock table " << m_table_name << " type: " <<
+  // ss[lock_type] << " command: " << thd->lex->sql_command << unlock;
+
+  if (thd->lex->sql_command == SQLCOM_LOCK_TABLES)
+    DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+
+  if (is_delay_insert(thd) && table_share->tmp_table == NO_TMP_TABLE && lock_type == F_WRLCK) {
+    DBUG_RETURN(0);
+  }
+
+  try {
+    if (lock_type == F_UNLCK) {
+      if (thd->lex->sql_command == SQLCOM_UNLOCK_TABLES)
+        current_txn_->ExplicitUnlockTables();
+
+      if (thd->killed)
+        ha_rcengine_->Rollback(thd, true);
+      if (current_txn_) {
+        current_txn_->RemoveTable(share);
+        if (current_txn_->Empty()) {
+          ha_rcengine_->ClearTx(thd);
+        }
+      }
+    } else {
+      auto tx = ha_rcengine_->GetTx(thd);
+      if (thd->lex->sql_command == SQLCOM_LOCK_TABLES)
+        tx->ExplicitLockTables();
+
+      if (lock_type == F_RDLCK) {
+        tx->AddTableRD(share);
+      } else {
+        tx->AddTableWR(share);
+        trans_register_ha(thd, false, rcbase_hton, NULL);
+        if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+          trans_register_ha(thd, true, rcbase_hton, NULL);
+      }
+    }
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "Tianmu internal error", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s.", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  // destroy the tx on failure
+  if (ret != 0)
+    ha_rcengine_->ClearTx(thd);
+
+  DBUG_RETURN(ret);
+}
+
+namespace {
+inline bool has_dup_key(std::shared_ptr<index::RCTableIndex> &indextab, TABLE *table, size_t &row) {
+  common::ErrorCode ret;
+  std::vector<std::string_view> records;
+  KEY *key = table->key_info + table->s->primary_key;
+
+  for (uint i = 0; i < key->actual_key_parts; i++) {
+    uint col = key->key_part[i].field->field_index;
+    Field *f = table->field[col];
+    switch (f->type()) {
+      case MYSQL_TYPE_TINY:
+      case MYSQL_TYPE_SHORT:
+      case MYSQL_TYPE_LONG:
+      case MYSQL_TYPE_INT24:
+      case MYSQL_TYPE_LONGLONG: {
+        int64_t v = f->val_int();
+        records.emplace_back((const char *)&v, sizeof(int64_t));
+        break;
+      }
+      case MYSQL_TYPE_DECIMAL:
+      case MYSQL_TYPE_FLOAT:
+      case MYSQL_TYPE_DOUBLE: {
+        double v = f->val_real();
+        records.emplace_back((const char *)&v, sizeof(double));
+        break;
+      }
+      case MYSQL_TYPE_NEWDECIMAL: {
+        auto dec_f = dynamic_cast<Field_new_decimal *>(f);
+        double v = std::lround(dec_f->val_real() * types::PowOfTen(dec_f->dec));
+        records.emplace_back((const char *)&v, sizeof(double));
+        break;
+      }
+      case MYSQL_TYPE_TIME:
+      case MYSQL_TYPE_TIME2:
+      case MYSQL_TYPE_DATE:
+      case MYSQL_TYPE_DATETIME:
+      case MYSQL_TYPE_NEWDATE:
+      case MYSQL_TYPE_TIMESTAMP2:
+      case MYSQL_TYPE_DATETIME2: {
+        MYSQL_TIME my_time;
+        std::memset(&my_time, 0, sizeof(my_time));
+        f->get_time(&my_time);
+        types::DT dt(my_time);
+        records.emplace_back((const char *)&dt.val, sizeof(int64_t));
+        break;
+      }
+
+      case MYSQL_TYPE_TIMESTAMP: {
+        MYSQL_TIME my_time;
+        std::memset(&my_time, 0, sizeof(my_time));
+        f->get_time(&my_time);
+        auto saved = my_time.second_part;
+        // convert to UTC
+        if (!common::IsTimeStampZero(my_time)) {
+          my_bool myb;
+          my_time_t secs_utc = current_thd->variables.time_zone->TIME_to_gmt_sec(&my_time, &myb);
+          common::GMTSec2GMTTime(&my_time, secs_utc);
+        }
+        my_time.second_part = saved;
+        types::DT dt(my_time);
+        records.emplace_back((const char *)&dt.val, sizeof(int64_t));
+        break;
+      }
+      case MYSQL_TYPE_YEAR:  // what the hell?
+      {
+        types::DT dt = {};
+        dt.year = f->val_int();
+        records.emplace_back((const char *)&dt.val, sizeof(int64_t));
+        break;
+      }
+      case MYSQL_TYPE_VARCHAR:
+      case MYSQL_TYPE_TINY_BLOB:
+      case MYSQL_TYPE_MEDIUM_BLOB:
+      case MYSQL_TYPE_LONG_BLOB:
+      case MYSQL_TYPE_BLOB:
+      case MYSQL_TYPE_VAR_STRING:
+      case MYSQL_TYPE_STRING: {
+        String str;
+        f->val_str(&str);
+        records.emplace_back(str.ptr(), str.length());
+        break;
+      }
+      case MYSQL_TYPE_SET:
+      case MYSQL_TYPE_ENUM:
+      case MYSQL_TYPE_GEOMETRY:
+      case MYSQL_TYPE_NULL:
+      case MYSQL_TYPE_BIT:
+      default:
+        throw common::Exception("unsupported mysql type " + std::to_string(f->type()));
+        break;
+    }
+  }
+
+  ret = indextab->GetRowByKey(current_txn_, records, row);
+  return (ret == common::ErrorCode::SUCCESS);
+}
+}  // namespace
+
+/*
+ write_row() inserts a row. No extra() hint is given currently if a bulk load
+ is happeneding. buf() is a byte array of data. You can use the field
+ information to extract the data from the native byte array type.
+ Example of this would be:
+ for (Field **field=table->field ; *field ; field++)
+ {
+ ...
+ }
+
+ See ha_tina.cc for an example of extracting all of the data as strings.
+ ha_berekly.cc has an example of how to store it intact by "packing" it
+ for ha_berkeley's own native storage type.
+
+ See the note for update_row() on auto_increments and timestamps. This
+ case also applied to write_row().
+
+ Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
+ sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
+ */
+int TianmuHandler::write_row([[maybe_unused]] uchar *buf) {
+  int ret = 1;
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  try {
+    if (ha_thd()->lex->duplicates == DUP_UPDATE) {
+      if (auto indextab = ha_rcengine_->GetTableIndex(m_table_name)) {
+        if (size_t row; has_dup_key(indextab, table, row)) {
+          m_dupkey_pos = row;
+          DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
+        }
+      }
+    }
+    ret = ha_rcengine_->InsertRow(m_table_name, current_txn_, table, share);
+  } catch (common::OutOfMemoryException &e) {
+    DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT);
+  } catch (common::DatabaseException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in Engine::InsertRow: %s.", e.what());
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+  } catch (common::FormatException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in Engine::InsertRow: %s Row: %ld, field %u.", e.what(),
+               e.m_row_no, e.m_field_no);
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+  } catch (common::FileException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in Engine::InsertRow: %s.", e.what());
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+  } catch (common::Exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in Engine::InsertRow: %s.", e.what());
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in Engine::InsertRow: %s.", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  DBUG_RETURN(ret);
+}
+
+/*
+ Yes, update_row() does what you expect, it updates a row. old_data will have
+ the previous row record in it, while new_data will have the newest data in
+ it.
+ Keep in mind that the server can do updates based on ordering if an ORDER BY
+ clause was used. Consecutive ordering is not guarenteed.
+ Currently new_data will not have an updated auto_increament record, or
+ and updated timestamp field. You can do these for example by doing these:
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+ table->timestamp_field->set_time();
+ if (table->next_number_field && record == table->record[0])
+ update_auto_increment();
+
+ Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
+ */
+int TianmuHandler::update_row(const uchar *old_data, uchar *new_data) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = HA_ERR_INTERNAL_ERROR;
+  auto org_bitmap = dbug_tmp_use_all_columns(table, table->write_set);
+
+  std::shared_ptr<void> defer(nullptr,
+                              [org_bitmap, this](...) { dbug_tmp_restore_column_map(table->write_set, org_bitmap); });
+
+  try {
+    auto tab = current_txn_->GetTableByPath(m_table_name);
+
+    for (uint i = 0; i < table->s->fields; i++) {
+      if (!bitmap_is_set(table->write_set, i)) {
+        continue;
+      }
+      auto field = table->field[i];
+      if (field->real_maybe_null()) {
+        if (field->is_null_in_record(old_data) && field->is_null_in_record(new_data)) {
+          continue;
+        }
+
+        if (field->is_null_in_record(new_data)) {
+          core::Value null;
+          tab->UpdateItem(current_position, i, null);
+          continue;
+        }
+      }
+      auto o_ptr = field->ptr - table->record[0] + old_data;
+      auto n_ptr = field->ptr - table->record[0] + new_data;
+      if (field->is_null_in_record(old_data) || std::memcmp(o_ptr, n_ptr, field->pack_length()) != 0) {
+        my_bitmap_map *org_bitmap2 = dbug_tmp_use_all_columns(table, table->read_set);
+        std::shared_ptr<void> defer(
+            nullptr, [org_bitmap2, this](...) { dbug_tmp_restore_column_map(table->read_set, org_bitmap2); });
+        core::Value v = GetValueFromField(field);
+        tab->UpdateItem(current_position, i, v);
+      }
+    }
+    ha_rcengine_->IncTianmuStatUpdate();
+    DBUG_RETURN(0);
+  } catch (common::DatabaseException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Update exception: %s.", e.what());
+  } catch (common::FileException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Update exception: %s.", e.what());
+  } catch (common::DupKeyException &e) {
+    ret = HA_ERR_FOUND_DUPP_KEY;
+    TIANMU_LOG(LogCtl_Level::ERROR, "Update exception: %s.", e.what());
+  } catch (common::Exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Update exception: %s.", e.what());
+  } catch (std::exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Update exception: %s.", e.what());
+  } catch (...) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  DBUG_RETURN(ret);
+}
+
+/*
+ This will delete a row. buf will contain a copy of the row to be deleted.
+ The server will call this right after the current row has been called (from
+ either a previous rnd_nexT() or index call).
+ If you keep a pointer to the last row or can access a primary key it will
+ make doing the deletion quite a bit easier.
+ Keep in mind that the server does no guarentee consecutive deletions. ORDER BY
+ clauses can be used.
+
+ Called in sql_acl.cc and sql_udf.cc to manage internal table information.
+ Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select it is
+ used for removing duplicates while in insert it is used for REPLACE calls.
+ */
+int TianmuHandler::delete_row([[maybe_unused]] const uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = HA_ERR_INTERNAL_ERROR;
+  auto org_bitmap = dbug_tmp_use_all_columns(table, table->write_set);
+
+  std::shared_ptr<void> defer(nullptr,
+                              [org_bitmap, this](...) { dbug_tmp_restore_column_map(table->write_set, org_bitmap); });
+
+  try {
+    auto tab = current_txn_->GetTableByPath(m_table_name);
+
+    for (uint i = 0; i < table->s->fields; i++) {
+      tab->DeleteItem(current_position, i);
+    }
+    DBUG_RETURN(0);
+  } catch (common::DatabaseException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Delete exception: %s.", e.what());
+  } catch (common::FileException &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Delete exception: %s.", e.what());
+  } catch (common::Exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Delete exception: %s.", e.what());
+  } catch (std::exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "Delete exception: %s.", e.what());
+  } catch (...) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  DBUG_RETURN(ret);
+}
+/*
+ Used to delete all rows in a table. Both for cases of truncate and
+ for cases where the optimizer realizes that all rows will be
+ removed as a result of a SQL statement.
+
+ Called from item_sum.cc by Item_func_group_concat::clear(),
+ Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
+ Called from sql_delete.cc by mysql_delete().
+ Called from sql_select.cc by JOIN::rein*it.
+ Called from sql_union.cc by st_select_lex_unit::exec().
+ */
+int TianmuHandler::delete_all_rows() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = 1;
+  try {
+    ha_rcengine_->TruncateTable(m_table_name, ha_thd());
+    ret = 0;
+  } catch (std::exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  DBUG_RETURN(ret);
+}
+
+int TianmuHandler::rename_table(const char *from, const char *to) {
+  try {
+    ha_rcengine_->RenameTable(current_txn_, from, to, ha_thd());
+    return 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  return 1;
+}
+
+void TianmuHandler::update_create_info([[maybe_unused]] HA_CREATE_INFO *create_info) {}
+
+/*
+ ::info() is used to return information to the optimizer.
+ see my_base.h for the complete description
+
+ Currently this table handler doesn't implement most of the fields
+ really needed. SHOW also makes use of this data
+ Another note, you will probably want to have the following in your
+ code:
+ if (records < 2)
+ records = 2;
+ The reason is that the server will optimize for cases of only a single
+ record. If in a table scan you don't know the number of records
+ it will probably be better to set records to two so you can return
+ as many records as you need.
+ Along with records a few more variables you may wish to set are:
+ records
+ deleted
+ data_file_length
+ index_file_length
+ delete_length
+ check_time
+ Take a look at the public variables in handler.h for more information.
+
+ Called in:
+ filesort.cc
+ ha_heap.cc
+ item_sum.cc
+ opt_sum.cc
+ sql_delete.cc
+ sql_delete.cc
+ sql_derived.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_show.cc
+ sql_show.cc
+ sql_show.cc
+ sql_show.cc
+ sql_table.cc
+ sql_union.cc
+ sql_update.cc
+
+ */
+int TianmuHandler::info(uint flag) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = 1;
+  try {
+    std::scoped_lock guard(global_mutex_);
+    if (flag & HA_STATUS_VARIABLE) {
+      std::shared_ptr<core::RCTable> tab;
+      if (current_txn_ != nullptr) {
+        tab = current_txn_->GetTableByPath(m_table_name);
+      } else
+        tab = ha_rcengine_->GetTableRD(m_table_name);
+      stats.records = (ha_rows)(tab->NumOfValues() - tab->NumOfDeleted());
+      stats.data_file_length = 0;
+      stats.mean_rec_length = 0;
+      if (stats.records > 0) {
+        std::vector<core::AttrInfo> attr_info(tab->GetAttributesInfo());
+        uint no_attrs = tab->NumOfAttrs();
+        for (uint j = 0; j < no_attrs; j++) stats.data_file_length += attr_info[j].comp_size;  // compressed size
+        stats.mean_rec_length = ulong(stats.data_file_length / stats.records);
+      }
+    }
+
+    if (flag & HA_STATUS_CONST)
+      stats.create_time = share->GetCreateTime();
+
+    if (flag & HA_STATUS_TIME)
+      stats.update_time = share->GetUpdateTime();
+
+    if (flag & HA_STATUS_ERRKEY) {
+      errkey = 0;  // TODO: for now only support one pk index
+      my_store_ptr(dup_ref, ref_length, m_dupkey_pos);
+    }
+
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+/* this should return 0 for concurrent insert to work */
+my_bool tianmu_check_status([[maybe_unused]] void *param) { return 0; }
+
+/*
+ Used for opening tables. The name will be the name of the file.
+ A table is opened when it needs to be opened. For instance
+ when a request comes in for a select on the table (tables are not
+ open and closed for each request, they are cached).
+
+ Called from handler.cc by handler::ha_open(). The server opens all tables by
+ calling ha_open() which then calls the handler specific open().
+ */
+int TianmuHandler::open(const char *name, [[maybe_unused]] int mode, [[maybe_unused]] uint test_if_locked) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  m_table_name = name;
+  int ret = 1;
+
+  try {
+    // TODO:
+    // Probably we don't need to search from the map each time.
+    // Keeping the share together with mysql handler cache makes
+    // more sense that would mean once a table is opened the TableShare
+    // would be kept.
+    if (!(share = ha_rcengine_->GetTableShare(table_share)))
+      DBUG_RETURN(ret);
+
+    thr_lock_data_init(&share->thr_lock, &m_lock, NULL);
+    share->thr_lock.check_status = tianmu_check_status;
+    // have primary key, use table index
+    if (table->s->primary_key != MAX_INDEXES)
+      ha_rcengine_->AddTableIndex(name, table, ha_thd());
+    ha_rcengine_->AddMemTable(table, share);
+    ret = 0;
+  } catch (common::Exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "Error from Tianmu engine", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "A tianmu exception is caught: %s", e.what());
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  info(HA_STATUS_CONST);
+
+  DBUG_RETURN(ret);
+}
+
+int TianmuHandler::free_share() {
+  share.reset();
+  return 0;
+}
+
+/*
+ Closes a table. We call the free_share() function to free any resources
+ that we have allocated in the "shared" structure.
+
+ Called from sql_base.cc, sql_select.cc, and table.cc.
+ In sql_select.cc it is only used to close up temporary tables or during
+ the process where a temporary table is converted over to being a
+ myisam table.
+ For sql_base.cc look at close_data_tables().
+ */
+int TianmuHandler::close() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  DBUG_RETURN(free_share());
+}
+
+int TianmuHandler::fill_row_by_id([[maybe_unused]] uchar *buf, uint64_t rowid) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_KEY_NOT_FOUND;
+  try {
+    auto tab = current_txn_->GetTableByPath(m_table_name);
+    if (tab) {
+      my_bitmap_map *org_bitmap = dbug_tmp_use_all_columns(table, table->write_set);
+      std::shared_ptr<void> defer(
+          nullptr, [org_bitmap, this](...) { dbug_tmp_restore_column_map(table->write_set, org_bitmap); });
+
+      tab->FillRowByRowid(table, rowid);
+      current_position = rowid;
+      rc = 0;
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+  DBUG_RETURN(rc);
+}
+
+int TianmuHandler::index_init(uint index, [[maybe_unused]] bool sorted) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  active_index = index;
+  DBUG_RETURN(0);
+}
+
+int TianmuHandler::index_end() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  active_index = MAX_KEY;
+  DBUG_RETURN(0);
+}
+
+/*
+ Positions an index cursor to the index specified in the handle. Fetches the
+ row if available. If the key value is null, begin at the first key of the
+ index.
+ */
+int TianmuHandler::index_read([[maybe_unused]] uchar *buf, [[maybe_unused]] const uchar *key,
+                              [[maybe_unused]] uint key_len __attribute__((unused)),
+                              enum ha_rkey_function find_flag __attribute__((unused))) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_KEY_NOT_FOUND;
+  try {
+    auto index = ha_rcengine_->GetTableIndex(m_table_name);
+    if (index && (active_index == table_share->primary_key)) {
+      std::vector<std::string_view> keys;
+      key_convert(key, key_len, index->KeyCols(), keys);
+      // support equality fullkey lookup over primary key, using full tuple
+      if (find_flag == HA_READ_KEY_EXACT) {
+        uint64_t rowid;
+        if (index->GetRowByKey(current_txn_, keys, rowid) == common::ErrorCode::SUCCESS) {
+          rc = fill_row_by_id(buf, rowid);
+        }
+      } else if (find_flag == HA_READ_AFTER_KEY || find_flag == HA_READ_KEY_OR_NEXT) {
+        auto iter = current_txn_->KVTrans().KeyIter();
+        common::Operator op = (find_flag == HA_READ_AFTER_KEY) ? common::Operator::O_MORE : common::Operator::O_MORE_EQ;
+        iter->ScanToKey(index, keys, op);
+        uint64_t rowid;
+        iter->GetRowid(rowid);
+        rc = fill_row_by_id(buf, rowid);
+
+      } else {
+        // not support HA_READ_PREFIX_LAST_OR_PREV HA_READ_PREFIX_LAST
+        rc = HA_ERR_WRONG_COMMAND;
+        TIANMU_LOG(LogCtl_Level::ERROR, "Error: index_read not support prefix search");
+      }
+
+    } else {
+      // other index not support
+      rc = HA_ERR_WRONG_INDEX;
+      TIANMU_LOG(LogCtl_Level::ERROR, "Error: index_read only support primary key");
+    }
+
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+
+  DBUG_RETURN(rc);
+}
+
+/*
+ Used to read forward through the index.
+ */
+int TianmuHandler::index_next([[maybe_unused]] uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_END_OF_FILE;
+  try {
+    auto iter = current_txn_->KVTrans().KeyIter();
+    ++(*iter);
+    if (iter->IsValid()) {
+      uint64_t rowid;
+      iter->GetRowid(rowid);
+      rc = fill_row_by_id(buf, rowid);
+    } else {
+      rc = HA_ERR_KEY_NOT_FOUND;
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+  DBUG_RETURN(rc);
+}
+
+/*
+ Used to read backwards through the index.
+ */
+int TianmuHandler::index_prev([[maybe_unused]] uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_END_OF_FILE;
+  try {
+    auto iter = current_txn_->KVTrans().KeyIter();
+    --(*iter);
+    if (iter->IsValid()) {
+      uint64_t rowid;
+      iter->GetRowid(rowid);
+      rc = fill_row_by_id(buf, rowid);
+    } else {
+      rc = HA_ERR_KEY_NOT_FOUND;
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+  DBUG_RETURN(rc);
+}
+
+/*
+ index_first() asks for the first key in the index.
+
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
+ and sql_select.cc.
+ */
+int TianmuHandler::index_first([[maybe_unused]] uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_END_OF_FILE;
+  try {
+    auto index = ha_rcengine_->GetTableIndex(m_table_name);
+    if (index && current_txn_) {
+      uint64_t rowid;
+      auto iter = current_txn_->KVTrans().KeyIter();
+      iter->ScanToEdge(index, true);
+      if (iter->IsValid()) {
+        iter->GetRowid(rowid);
+        rc = fill_row_by_id(buf, rowid);
+      } else {
+        rc = HA_ERR_KEY_NOT_FOUND;
+      }
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+  DBUG_RETURN(rc);
+}
+
+/*
+ index_last() asks for the last key in the index.
+
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
+ and sql_select.cc.
+ */
+int TianmuHandler::index_last([[maybe_unused]] uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int rc = HA_ERR_END_OF_FILE;
+  try {
+    auto index = ha_rcengine_->GetTableIndex(m_table_name);
+    if (index && current_txn_) {
+      uint64_t rowid;
+      auto iter = current_txn_->KVTrans().KeyIter();
+      iter->ScanToEdge(index, false);
+      if (iter->IsValid()) {
+        iter->GetRowid(rowid);
+        rc = fill_row_by_id(buf, rowid);
+      } else {
+        rc = HA_ERR_KEY_NOT_FOUND;
+      }
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  }
+  DBUG_RETURN(rc);
+}
+
+/*
+ rnd_init() is called when the system wants the storage engine to do a table
+ scan.
+ See the example in the introduction at the top of this file to see when
+ rnd_init() is called.
+
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
+ sql_table.cc, and sql_update.cc.
+ */
+int TianmuHandler::rnd_init(bool scan) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+  try {
+    if (m_query && !m_result && table_ptr->NumOfObj() != 0) {
+      m_cq->Result(m_tmp_table);  // it is ALWAYS -2 though....
+      m_result = true;
+
+      try {
+        core::FunctionExecutor fe(std::bind(&core::Query::LockPackInfoForUse, std::ref(m_query)),
+                                  std::bind(&core::Query::UnlockPackInfoFromUse, std::ref(m_query)));
+
+        core::TempTable *push_down_result = m_query->Preexecute(*m_cq, NULL, false);
+        if (!push_down_result || push_down_result->NumOfTables() != 1)
+          throw common::InternalException("core::Query execution returned no result object");
+
+        core::Filter *filter(push_down_result->GetMultiIndexP()->GetFilter(0));
+        if (!filter)
+          filter_ptr.reset(
+              new core::Filter(push_down_result->GetMultiIndexP()->OrigSize(0), table_ptr->Getpackpower()));
+        else
+          filter_ptr.reset(new core::Filter(*filter));
+
+        table_ptr = push_down_result->GetTableP(0);
+        table_new_iter = ((core::RCTable *)table_ptr)->Begin(GetAttrsUseIndicator(table), *filter);
+        table_new_iter_end = ((core::RCTable *)table_ptr)->End();
+      } catch (common::Exception const &e) {
+        rc_control_ << system::lock << "Error in push-down execution, push-down execution aborted: " << e.what()
+                    << system::unlock;
+        TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught in push-down execution: %s", e.what());
+      }
+      m_query.reset();
+      m_cq.reset();
+    } else {
+      if (scan && filter_ptr.get()) {
+        table_new_iter = ((core::RCTable *)table_ptr)->Begin(GetAttrsUseIndicator(table), *filter_ptr);
+        table_new_iter_end = ((core::RCTable *)table_ptr)->End();
+      } else {
+        std::shared_ptr<core::RCTable> rctp;
+        ha_rcengine_->GetTableIterator(m_table_name, table_new_iter, table_new_iter_end, rctp,
+                                       GetAttrsUseIndicator(table), table->in_use);
+        table_ptr = rctp.get();
+        filter_ptr.reset();
+      }
+    }
+    ret = 0;
+    blob_buffers.resize(0);
+    if (table_ptr != NULL)
+      blob_buffers.resize(table_ptr->NumOfDisplaybleAttrs());
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+int TianmuHandler::rnd_end() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  reset();
+  DBUG_RETURN(0);
+}
+
+/*
+ This is called for each row of the table scan. When you run out of records
+ you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
+ The Field structure for the table is the key to getting data into buf
+ in a manner that will allow the server to understand it.
+
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
+ sql_table.cc, and sql_update.cc.
+ */
+int TianmuHandler::rnd_next(uchar *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = HA_ERR_END_OF_FILE;
+  try {
+    table->status = 0;
+    if (fill_row(buf) == HA_ERR_END_OF_FILE) {
+      table->status = STATUS_NOT_FOUND;
+      DBUG_RETURN(ret);
+    }
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+/*
+ position() is called after each call to rnd_next() if the data needs
+ to be ordered. You can do something like the following to store
+ the position:
+ my_store_ptr(ref, ref_length, current_position);
+
+ The server uses ref to store data. ref_length in the above case is
+ the size needed to store current_position. ref is just a byte array
+ that the server will maintain. If you are using offsets to mark rows, then
+ current_position should be the offset. If it is a primary key like in
+ BDB, then it needs to be a primary key.
+
+ Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
+ */
+void TianmuHandler::position([[maybe_unused]] const uchar *record) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  my_store_ptr(ref, ref_length, current_position);
+
+  DBUG_VOID_RETURN;
+}
+
+/*
+ This is like rnd_next, but you are given a position to use
+ to determine the row. The position will be of the type that you stored in
+ ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key
+ or position you saved when position() was called.
+ Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc.
+ */
+int TianmuHandler::rnd_pos(uchar *buf, uchar *pos) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = HA_ERR_END_OF_FILE;
+  try {
+    uint64_t position = my_get_ptr(pos, ref_length);
+
+    filter_ptr = std::make_unique<core::Filter>(position + 1, share->PackSizeShift());
+    filter_ptr->Reset();
+    filter_ptr->Set(position);
+
+    auto tab_ptr = ha_rcengine_->GetTx(table->in_use)->GetTableByPath(m_table_name);
+    table_new_iter = tab_ptr->Begin(GetAttrsUseIndicator(table), *filter_ptr);
+    table_new_iter_end = tab_ptr->End();
+    table_ptr = tab_ptr.get();
+
+    table_new_iter.MoveToRow(position);
+    table->status = 0;
+    blob_buffers.resize(table->s->fields);
+    if (fill_row(buf) == HA_ERR_END_OF_FILE) {
+      table->status = STATUS_NOT_FOUND;
+      DBUG_RETURN(ret);
+    }
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+/*
+ extra() is called whenever the server wishes to send a hint to
+ the storage engine. The myisam engine implements the most hints.
+ ha_innodb.cc has the most exhaustive list of these hints.
+ */
+int TianmuHandler::extra(enum ha_extra_function operation) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  /* This preemptive delete might cause problems here.
+   * Other place where it can be put is TianmuHandler::external_lock().
+   */
+  if (operation == HA_EXTRA_NO_CACHE) {
+    m_cq.reset();
+    m_query.reset();
+  }
+  DBUG_RETURN(0);
+}
+
+int TianmuHandler::start_stmt(THD *thd, thr_lock_type lock_type) {
+  try {
+    if (lock_type == TL_WRITE_CONCURRENT_INSERT || lock_type == TL_WRITE_DEFAULT || lock_type == TL_WRITE) {
+      trans_register_ha(thd, false, rcbase_hton, NULL);
+      if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
+        trans_register_ha(thd, true, rcbase_hton, NULL);
+      }
+      current_txn_ = ha_rcengine_->GetTx(thd);
+      current_txn_->AddTableWRIfNeeded(share);
+    }
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  return 0;
+}
+
+/*
+ Ask rcbase handler about permission to cache table during query registration.
+ If current thread is in non-autocommit, we don't permit any mysql query
+ caching.
+ */
+my_bool TianmuHandler::register_query_cache_table(THD *thd, char *table_key, size_t key_length,
+                                                  qc_engine_callback *call_back,
+                                                  [[maybe_unused]] ulonglong *engine_data) {
+  *call_back = rcbase_query_caching_of_table_permitted;
+  return rcbase_query_caching_of_table_permitted(thd, table_key, key_length, 0);
+}
+
+/*
+ Used to delete a table. By the time delete_table() has been called all
+ opened references to this table will have been closed (and your globally
+ shared references released. The variable name will just be the name of
+ the table. You will need to remove any files you have created at this point.
+
+ If you do not implement this, the default delete_table() is called from
+ handler.cc and it will delete all files with the file extentions returned
+ by bas_ext().
+
+ Called from handler.cc by delete_table and  ha_create_table(). Only used
+ during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
+ the storage engine.
+ */
+int TianmuHandler::delete_table(const char *name) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = 1;
+  try {
+    ha_rcengine_->DeleteTable(name, ha_thd());
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+/*
+ Given a starting key, and an ending key estimate the number of rows that
+ will exist between the two. end_key may be empty which in case determine
+ if start_key matches any rows.
+
+ Called from opt_range.cc by check_quick_keys().
+ */
+ha_rows TianmuHandler::records_in_range([[maybe_unused]] uint inx, [[maybe_unused]] key_range *min_key,
+                                        [[maybe_unused]] key_range *max_key) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  DBUG_RETURN(stats.records);  // low number to force index usage
+}
+
+/*
+ create() is called to create a database. The variable name will have the name
+ of the table. When create() is called you do not need to worry about opening
+ the table. Also, the FRM file will have already been created so adjusting
+ create_info will not do you any good. You can overwrite the frm file at this
+ point if you wish to change the table definition, but there are no methods
+ currently provided for doing that.
+
+ Called from handle.cc by ha_create_table().
+ */
+int TianmuHandler::create(const char *name, TABLE *table_arg, [[maybe_unused]] HA_CREATE_INFO *create_info) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  try {
+    // fix issue 487: bug for create table #mysql50#q.q should return failure and actually return success
+    const size_t table_name_len = strlen(name);
+    if (name[table_name_len - 1] == '/') {
+      TIANMU_LOG(LogCtl_Level::ERROR, "Table name is empty");
+      DBUG_RETURN(ER_WRONG_TABLE_NAME);
+    }
+
+    ha_rcengine_->CreateTable(name, table_arg);
+    DBUG_RETURN(0);
+  } catch (common::AutoIncException &e) {
+    my_message(ER_WRONG_AUTO_KEY, e.what(), MYF(0));
+  } catch (common::UnsupportedDataTypeException &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+  } catch (fs::filesystem_error &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "filesystem_error on table creation '%s'", e.what());
+    fs::remove_all(std::string(name) + common::TIANMU_EXT);
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(1);
+}
+
+int TianmuHandler::truncate() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  int ret = 1;
+  try {
+    ha_rcengine_->TruncateTable(m_table_name, ha_thd());
+    ret = 0;
+  } catch (std::exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  DBUG_RETURN(ret);
+}
+
+uint TianmuHandler::max_supported_key_part_length(HA_CREATE_INFO *create_info) const {
+  if (tianmu_sysvar_large_prefix)
+    return (Tianmu::common::TIANMU_MAX_INDEX_COL_LEN_LARGE);
+  else
+    return (Tianmu::common::TIANMU_MAX_INDEX_COL_LEN_SMALL);
+}
+
+int TianmuHandler::fill_row(uchar *buf) {
+  if (table_new_iter == table_new_iter_end)
+    return HA_ERR_END_OF_FILE;
+
+  my_bitmap_map *org_bitmap = dbug_tmp_use_all_columns(table, table->write_set);
+
+  std::shared_ptr<char[]> buffer;
+
+  // we should pack the row into `buf` but seems it just use record[0] blindly.
+  // So this is a workaround to handle the case that `buf` is not record[0].
+  if (buf != table->record[0]) {
+    buffer.reset(new char[table->s->reclength]);
+    std::memcpy(buffer.get(), table->record[0], table->s->reclength);
+  }
+
+  for (uint col_id = 0; col_id < table->s->fields; col_id++)
+    core::Engine::ConvertToField(table->field[col_id], *(table_new_iter.GetData(col_id)), &blob_buffers[col_id]);
+
+  if (buf != table->record[0]) {
+    std::memcpy(buf, table->record[0], table->s->reclength);
+    std::memcpy(table->record[0], buffer.get(), table->s->reclength);
+  }
+
+  current_position = table_new_iter.GetCurrentRowId();
+  table_new_iter++;
+
+  dbug_tmp_restore_column_map(table->write_set, org_bitmap);
+
+  return 0;
+}
+
+char *TianmuHandler::update_table_comment(const char *comment) {
+  char *ret = const_cast<char *>(comment);
+  try {
+    uint length = (uint)std::strlen(comment);
+    char *str = NULL;
+    uint extra_len = 0;
+
+    if (length > 64000 - 3) {
+      return ((char *)comment);  // string too long
+    }
+
+    //  get size & ratio
+    int64_t sum_c = 0, sum_u = 0;
+    std::vector<core::AttrInfo> attr_info = ha_rcengine_->GetTableAttributesInfo(m_table_name, table_share);
+    for (uint j = 0; j < attr_info.size(); j++) {
+      sum_c += attr_info[j].comp_size;
+      sum_u += attr_info[j].uncomp_size;
+    }
+    char buf[256];
+    double ratio = (sum_c > 0 ? double(sum_u) / double(sum_c) : 0);
+    int count = std::sprintf(buf, "Overall compression ratio: %.3f, Raw size=%ld MB", ratio, sum_u >> 20);
+    extra_len += count;
+
+    str = (char *)my_malloc(PSI_NOT_INSTRUMENTED, length + extra_len + 3, MYF(0));
+    if (str) {
+      char *pos = str + length;
+      if (length) {
+        std::memcpy(str, comment, length);
+        *pos++ = ';';
+        *pos++ = ' ';
+      }
+
+      std::memcpy(pos, buf, extra_len);
+      pos[extra_len] = 0;
+    }
+    ret = str;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  return ret;
+}
+
+bool TianmuHandler::explain_message(const Item *a_cond, String *buf) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  if (current_txn_->Explain()) {
+    cond_push(a_cond);
+    std::string str = current_txn_->GetExplainMsg();
+    buf->append(str.c_str(), str.length());
+  }
+  DBUG_RETURN(TRUE);
+}
+
+int TianmuHandler::set_cond_iter() {
+  int ret = 1;
+  if (m_query && !m_result && table_ptr->NumOfObj() != 0) {
+    m_cq->Result(m_tmp_table);  // it is ALWAYS -2 though....
+    m_result = true;
+
+    try {
+      core::FunctionExecutor fe(std::bind(&core::Query::LockPackInfoForUse, std::ref(m_query)),
+                                std::bind(&core::Query::UnlockPackInfoFromUse, std::ref(m_query)));
+
+      core::TempTable *push_down_result = m_query->Preexecute(*m_cq, NULL, false);
+      if (!push_down_result || push_down_result->NumOfTables() != 1)
+        throw common::InternalException("core::Query execution returned no result object");
+
+      core::Filter *filter(push_down_result->GetMultiIndexP()->GetFilter(0));
+      if (!filter)
+        filter_ptr.reset(new core::Filter(push_down_result->GetMultiIndexP()->OrigSize(0), table_ptr->Getpackpower()));
+      else
+        filter_ptr.reset(new core::Filter(*filter));
+
+      table_ptr = push_down_result->GetTableP(0);
+      table_new_iter = ((core::RCTable *)table_ptr)->Begin(GetAttrsUseIndicator(table), *filter_ptr);
+      table_new_iter_end = ((core::RCTable *)table_ptr)->End();
+      ret = 0;
+    } catch (common::Exception const &e) {
+      rc_control_ << system::lock << "Error in push-down execution, push-down execution aborted: " << e.what()
+                  << system::unlock;
+      TIANMU_LOG(LogCtl_Level::ERROR, "Error in push-down execution, push-down execution aborted: %s", e.what());
+    }
+    m_query.reset();
+    m_cq.reset();
+  }
+  return ret;
+}
+
+const Item *TianmuHandler::cond_push(const Item *a_cond) {
+  Item const *ret = a_cond;
+  Item *cond = const_cast<Item *>(a_cond);
+
+  try {
+    if (!m_query) {
+      std::shared_ptr<core::RCTable> rctp;
+      ha_rcengine_->GetTableIterator(m_table_name, table_new_iter, table_new_iter_end, rctp,
+                                     GetAttrsUseIndicator(table), table->in_use);
+      table_ptr = rctp.get();
+      m_query.reset(new core::Query(current_txn_));
+      m_cq.reset(new core::CompiledQuery);
+      m_result = false;
+
+      m_query->AddTable(rctp);
+      core::TabID t_out;
+      m_cq->TableAlias(t_out,
+                       core::TabID(0));  // we apply it to the only table in this query
+      m_cq->TmpTable(m_tmp_table, t_out);
+
+      std::string ext_alias;
+      if (table->pos_in_table_list->referencing_view)
+        ext_alias = std::string(table->pos_in_table_list->referencing_view->table_name);
+      else
+        ext_alias = std::string(table->s->table_name.str);
+      ext_alias += std::string(":") + std::string(table->alias);
+      m_query->table_alias2index_ptr.insert(std::make_pair(ext_alias, std::make_pair(t_out.n, table)));
+
+      int col_no = 0;
+      core::AttrID col, vc;
+      core::TabID tab(m_tmp_table);
+
+      my_bitmap_map *org_bitmap = dbug_tmp_use_all_columns(table, table->read_set);
+      for (Field **field = table->field; *field; field++) {
+        core::AttrID at;
+        if (bitmap_is_set(table->read_set, col_no)) {
+          col.n = col_no++;
+          m_cq->CreateVirtualColumn(vc.n, m_tmp_table, t_out, col);
+          m_cq->AddColumn(at, m_tmp_table, core::CQTerm(vc.n), common::ColOperation::LISTING, (*field)->field_name,
+                          false);
+        }
+      }
+      dbug_tmp_restore_column_map(table->read_set, org_bitmap);
+    }
+
+    if (m_result)
+      return a_cond;  // if m_result there is already a result command in
+                      // compilation
+
+    std::unique_ptr<core::CompiledQuery> tmp_cq(new core::CompiledQuery(*m_cq));
+    core::CondID cond_id;
+    if (!m_query->BuildConditions(cond, cond_id, tmp_cq.get(), m_tmp_table, core::CondType::WHERE_COND, false)) {
+      m_query.reset();
+      return a_cond;
+    }
+    tmp_cq->AddConds(m_tmp_table, cond_id, core::CondType::WHERE_COND);
+    tmp_cq->ApplyConds(m_tmp_table);
+    m_cq.reset(tmp_cq.release());
+    // reset  table_new_iter with push condition
+    set_cond_iter();
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+  return ret;
+}
+
+int TianmuHandler::reset() {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+  try {
+    table_new_iter = core::RCTable::Iterator();
+    table_new_iter_end = core::RCTable::Iterator();
+    table_ptr = NULL;
+    filter_ptr.reset();
+    m_query.reset();
+    m_cq.reset();
+    m_result = false;
+    blob_buffers.resize(0);
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+enum_alter_inplace_result TianmuHandler::check_if_supported_inplace_alter([[maybe_unused]] TABLE *altered_table,
+                                                                          Alter_inplace_info *ha_alter_info) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  if ((ha_alter_info->handler_flags & ~TIANMU_SUPPORTED_ALTER_ADD_DROP_ORDER) &&
+      (ha_alter_info->handler_flags != TIANMU_SUPPORTED_ALTER_COLUMN_NAME)) {
+    // support alter table column type
+    if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_STORED_COLUMN_TYPE)
+      DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+    // support alter table column exceeded length
+    if ((ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH))
+      DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+    // support alter table column default
+    if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_DEFAULT)
+      DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+    DBUG_RETURN(HA_ALTER_ERROR);
+  }
+  DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+}
+
+bool TianmuHandler::inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
+  try {
+    if (!(ha_alter_info->handler_flags & ~TIANMU_SUPPORTED_ALTER_ADD_DROP_ORDER)) {
+      std::vector<Field *> v_old(table_share->field, table_share->field + table_share->fields);
+      std::vector<Field *> v_new(altered_table->s->field, altered_table->s->field + altered_table->s->fields);
+      ha_rcengine_->PrepareAlterTable(m_table_name, v_new, v_old, ha_thd());
+      return false;
+    } else if (ha_alter_info->handler_flags == TIANMU_SUPPORTED_ALTER_COLUMN_NAME) {
+      return false;
+    }
+  } catch (std::exception &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception is caught: %s", e.what());
+  } catch (...) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "Unable to inplace alter table", MYF(0));
+
+  return true;
+}
+
+bool TianmuHandler::commit_inplace_alter_table([[maybe_unused]] TABLE *altered_table, Alter_inplace_info *ha_alter_info,
+                                               bool commit) {
+  if (!commit) {
+    TIANMU_LOG(LogCtl_Level::INFO, "Alter table failed : %s%s", m_table_name.c_str(), " rollback");
+    return true;
+  }
+  if (ha_alter_info->handler_flags == TIANMU_SUPPORTED_ALTER_COLUMN_NAME) {
+    return false;
+  }
+  if ((ha_alter_info->handler_flags & ~TIANMU_SUPPORTED_ALTER_ADD_DROP_ORDER)) {
+    TIANMU_LOG(LogCtl_Level::INFO, "Altered table not support type %lu", ha_alter_info->handler_flags);
+    return true;
+  }
+  fs::path tmp_dir(m_table_name + ".tmp");
+  fs::path tab_dir(m_table_name + common::TIANMU_EXT);
+  fs::path bak_dir(m_table_name + ".backup");
+
+  try {
+    fs::rename(tab_dir, bak_dir);
+    fs::rename(tmp_dir, tab_dir);
+
+    // now we are safe to clean up
+    std::unordered_set<std::string> s;
+    for (auto &it : fs::directory_iterator(tab_dir / common::COLUMN_DIR)) {
+      auto target = fs::read_symlink(it.path()).string();
+      if (!target.empty())
+        s.insert(target);
+    }
+
+    for (auto &it : fs::directory_iterator(bak_dir / common::COLUMN_DIR)) {
+      auto target = fs::read_symlink(it.path()).string();
+      if (target.empty())
+        continue;
+      auto search = s.find(target);
+      if (search == s.end()) {
+        fs::remove_all(target);
+        TIANMU_LOG(LogCtl_Level::INFO, "removing %s", target.c_str());
+      }
+    }
+    fs::remove_all(bak_dir);
+  } catch (fs::filesystem_error &e) {
+    TIANMU_LOG(LogCtl_Level::ERROR, "file system error: %s %s|%s", e.what(), e.path1().string().c_str(),
+               e.path2().string().c_str());
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "Failed to commit alter table", MYF(0));
+    return true;
+  }
+  return false;
+}
+/*
+ key: mysql format, may be union key, need changed to kvstore key format
+
+ */
+void TianmuHandler::key_convert(const uchar *key, uint key_len, std::vector<uint> cols,
+                                std::vector<std::string_view> &keys) {
+  key_restore(table->record[0], (uchar *)key, &table->key_info[active_index], key_len);
+
+  Field **field = table->field;
+  std::vector<std::string> records;
+  for (auto &i : cols) {
+    Field *f = field[i];
+    size_t length;
+    if (f->is_null()) {
+      throw common::Exception("Priamry key part can not be NULL");
+    }
+    if (f->flags & BLOB_FLAG)
+      length = dynamic_cast<Field_blob *>(f)->get_length();
+    else
+      length = f->row_pack_length();
+
+    std::unique_ptr<char[]> buf(new char[length]);
+    char *ptr = buf.get();
+
+    switch (f->type()) {
+      case MYSQL_TYPE_TINY: {
+        int64_t v = f->val_int();
+        if (v > TIANMU_TINYINT_MAX)
+          v = TIANMU_TINYINT_MAX;
+        else if (v < TIANMU_TINYINT_MIN)
+          v = TIANMU_TINYINT_MIN;
+        *(int64_t *)ptr = v;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_SHORT: {
+        int64_t v = f->val_int();
+        if (v > TIANMU_SMALLINT_MAX)
+          v = TIANMU_SMALLINT_MAX;
+        else if (v < TIANMU_SMALLINT_MIN)
+          v = TIANMU_SMALLINT_MIN;
+        *(int64_t *)ptr = v;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_LONG: {
+        int64_t v = f->val_int();
+        if (v > std::numeric_limits<int>::max())
+          v = std::numeric_limits<int>::max();
+        else if (v < TIANMU_INT_MIN)
+          v = TIANMU_INT_MIN;
+        *(int64_t *)ptr = v;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_INT24: {
+        int64_t v = f->val_int();
+        if (v > TIANMU_MEDIUMINT_MAX)
+          v = TIANMU_MEDIUMINT_MAX;
+        else if (v < TIANMU_MEDIUMINT_MIN)
+          v = TIANMU_MEDIUMINT_MIN;
+        *(int64_t *)ptr = v;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_LONGLONG: {
+        int64_t v = f->val_int();
+        if (v > common::TIANMU_BIGINT_MAX)
+          v = common::TIANMU_BIGINT_MAX;
+        else if (v < common::TIANMU_BIGINT_MIN)
+          v = common::TIANMU_BIGINT_MIN;
+        *(int64_t *)ptr = v;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_DECIMAL:
+      case MYSQL_TYPE_FLOAT:
+      case MYSQL_TYPE_DOUBLE: {
+        double v = f->val_real();
+        *(int64_t *)ptr = *reinterpret_cast<int64_t *>(&v);
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_NEWDECIMAL: {
+        auto dec_f = dynamic_cast<Field_new_decimal *>(f);
+        *(int64_t *)ptr = std::lround(dec_f->val_real() * types::PowOfTen(dec_f->dec));
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_TIME:
+      case MYSQL_TYPE_TIME2:
+      case MYSQL_TYPE_DATE:
+      case MYSQL_TYPE_DATETIME:
+      case MYSQL_TYPE_NEWDATE:
+      case MYSQL_TYPE_TIMESTAMP2:
+      case MYSQL_TYPE_DATETIME2: {
+        MYSQL_TIME my_time;
+        std::memset(&my_time, 0, sizeof(my_time));
+        f->get_time(&my_time);
+        types::DT dt(my_time);
+        *(int64_t *)ptr = dt.val;
+        ptr += sizeof(int64_t);
+      } break;
+
+      case MYSQL_TYPE_TIMESTAMP: {
+        MYSQL_TIME my_time;
+        std::memset(&my_time, 0, sizeof(my_time));
+        f->get_time(&my_time);
+        auto saved = my_time.second_part;
+        // convert to UTC
+        if (!common::IsTimeStampZero(my_time)) {
+          my_bool myb;
+          my_time_t secs_utc = current_thd->variables.time_zone->TIME_to_gmt_sec(&my_time, &myb);
+          common::GMTSec2GMTTime(&my_time, secs_utc);
+        }
+        my_time.second_part = saved;
+        types::DT dt(my_time);
+        *(int64_t *)ptr = dt.val;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_YEAR:  // what the hell?
+      {
+        types::DT dt = {};
+        dt.year = f->val_int();
+        *(int64_t *)ptr = dt.val;
+        ptr += sizeof(int64_t);
+      } break;
+      case MYSQL_TYPE_VARCHAR:
+      case MYSQL_TYPE_TINY_BLOB:
+      case MYSQL_TYPE_MEDIUM_BLOB:
+      case MYSQL_TYPE_LONG_BLOB:
+      case MYSQL_TYPE_BLOB:
+      case MYSQL_TYPE_VAR_STRING:
+      case MYSQL_TYPE_STRING: {
+        String str;
+        f->val_str(&str);
+        *(uint32_t *)ptr = str.length();
+        ptr += sizeof(uint32_t);
+        std::memcpy(ptr, str.ptr(), str.length());
+        ptr += str.length();
+      } break;
+      case MYSQL_TYPE_SET:
+      case MYSQL_TYPE_ENUM:
+      case MYSQL_TYPE_GEOMETRY:
+      case MYSQL_TYPE_NULL:
+      case MYSQL_TYPE_BIT:
+      default:
+        throw common::Exception("unsupported mysql type " + std::to_string(f->type()));
+        break;
+    }
+    records.emplace_back((const char *)buf.get(), ptr - buf.get());
+  }
+
+  for (auto &elem : records) {
+    keys.emplace_back(elem.data(), elem.size());
+  }
+}
+
+}  // namespace dbhandler
+}  // namespace Tianmu
diff --git a/storage/tianmu/handler/tianmu_handler_com.cpp b/storage/tianmu/handler/tianmu_handler_com.cpp
new file mode 100644
index 000000000..63d428805
--- /dev/null
+++ b/storage/tianmu/handler/tianmu_handler_com.cpp
@@ -0,0 +1,761 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+#include <arpa/inet.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "binlog.h"
+#include "core/transaction.h"
+#include "handler/tianmu_handler.h"
+#include "mm/initializer.h"
+#include "system/file_out.h"
+
+handlerton *rcbase_hton;
+
+struct st_mysql_sys_var {
+  MYSQL_PLUGIN_VAR_HEADER;
+};
+
+namespace Tianmu {
+namespace dbhandler {
+
+/*
+ If frm_error() is called then we will use this to to find out what file
+ extentions exist for the storage engine. This is also used by the default
+ rename_table and delete_table method in handler.cc.
+ */
+my_bool tianmu_bootstrap = 0;
+
+char *strmov_str(char *dst, const char *src) {
+  while ((*dst++ = *src++))
+    ;
+  return dst - 1;
+}
+
+static int rcbase_done_func([[maybe_unused]] void *p) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  if (ha_rcengine_) {
+    delete ha_rcengine_;
+    ha_rcengine_ = nullptr;
+  }
+  if (ha_kvstore_) {
+    delete ha_kvstore_;
+    ha_kvstore_ = nullptr;
+  }
+
+  DBUG_RETURN(0);
+}
+
+handler *rcbase_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root) {
+  return new (mem_root) TianmuHandler(hton, table);
+}
+
+int rcbase_panic_func([[maybe_unused]] handlerton *hton, enum ha_panic_function flag) {
+  if (tianmu_bootstrap)
+    return 0;
+
+  if (flag == HA_PANIC_CLOSE) {
+    delete ha_rcengine_;
+    ha_rcengine_ = nullptr;
+    delete ha_kvstore_;
+    ha_kvstore_ = nullptr;
+  }
+  return 0;
+}
+
+int rcbase_rollback([[maybe_unused]] handlerton *hton, THD *thd, bool all) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+  try {
+    ha_rcengine_->Rollback(thd, all);
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception error is caught: %s.", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+int rcbase_close_connection(handlerton *hton, THD *thd) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+  try {
+    rcbase_rollback(hton, thd, true);
+    ha_rcengine_->ClearTx(thd);
+    ret = 0;
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception error is caught: %s.", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+int rcbase_commit([[maybe_unused]] handlerton *hton, THD *thd, bool all) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+
+  int ret = 1;
+  std::string error_message;
+
+  if (!(thd->no_errors != 0 || thd->killed || thd->transaction_rollback_request)) {
+    try {
+      ha_rcengine_->CommitTx(thd, all);
+      ret = 0;
+    } catch (std::exception &e) {
+      error_message = std::string("Error: ") + e.what();
+    } catch (...) {
+      error_message = std::string(__func__) + " An unknown system exception error caught.";
+    }
+  }
+
+  if (ret) {
+    try {
+      ha_rcengine_->Rollback(thd, all, true);
+      if (!error_message.empty()) {
+        TIANMU_LOG(LogCtl_Level::ERROR, "%s", error_message.c_str());
+        my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), error_message.c_str(), MYF(0));
+      }
+    } catch (std::exception &e) {
+      if (!error_message.empty()) {
+        TIANMU_LOG(LogCtl_Level::ERROR, "%s", error_message.c_str());
+        my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), error_message.c_str(), MYF(0));
+      }
+      my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR),
+                 (std::string("Failed to rollback transaction. Error: ") + e.what()).c_str(), MYF(0));
+      TIANMU_LOG(LogCtl_Level::ERROR, "Failed to rollback transaction. Error: %s.", e.what());
+    } catch (...) {
+      if (!error_message.empty()) {
+        TIANMU_LOG(LogCtl_Level::ERROR, "%s", error_message.c_str());
+        my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), error_message.c_str(), MYF(0));
+      }
+      my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "Failed to rollback transaction. Unknown error.",
+                 MYF(0));
+      TIANMU_LOG(LogCtl_Level::ERROR, "Failed to rollback transaction. Unknown error.");
+    }
+  }
+
+  DBUG_RETURN(ret);
+}
+
+bool rcbase_show_status([[maybe_unused]] handlerton *hton, THD *thd, stat_print_fn *pprint, enum ha_stat_type stat) {
+  const static char *hton_name = "TIANMU";
+
+  std::ostringstream buf(std::ostringstream::out);
+
+  buf << std::endl;
+
+  if (stat == HA_ENGINE_STATUS) {
+    mm::TraceableObject::Instance()->HeapHistogram(buf);
+    return pprint(thd, hton_name, uint(std::strlen(hton_name)), "Heap Histograms", uint(std::strlen("Heap Histograms")),
+                  buf.str().c_str(), uint(buf.str().length()));
+  }
+  return false;
+}
+
+extern my_bool tianmu_bootstrap;
+
+static int init_variables() {
+  opt_binlog_order_commits = false;
+  return 0;
+}
+
+int rcbase_init_func(void *p) {
+  DBUG_ENTER(__PRETTY_FUNCTION__);
+  rcbase_hton = (handlerton *)p;
+
+  if (init_variables()) {
+    DBUG_RETURN(1);
+  }
+
+  rcbase_hton->state = SHOW_OPTION_YES;
+  rcbase_hton->db_type = DB_TYPE_TIANMU;
+  rcbase_hton->create = rcbase_create_handler;
+  rcbase_hton->flags = HTON_NO_FLAGS;
+  rcbase_hton->panic = rcbase_panic_func;
+  rcbase_hton->close_connection = rcbase_close_connection;
+  rcbase_hton->commit = rcbase_commit;
+  rcbase_hton->rollback = rcbase_rollback;
+  rcbase_hton->show_status = rcbase_show_status;
+
+  // When mysqld runs as bootstrap mode, we do not need to initialize
+  // memmanager.
+  if (tianmu_bootstrap)
+    DBUG_RETURN(0);
+
+  int ret = 1;
+  ha_rcengine_ = NULL;
+
+  try {
+    std::string log_file = mysql_home_ptr;
+    log_setup(log_file + "/log/tianmu.log");
+    rc_control_.addOutput(new system::FileOut(log_file + "/log/trace.log"));
+    rc_querylog_.addOutput(new system::FileOut(log_file + "/log/query.log"));
+    struct hostent *hent = NULL;
+    hent = gethostbyname(glob_hostname);
+    if (hent)
+      strmov_str(global_hostIP_, inet_ntoa(*(struct in_addr *)(hent->h_addr_list[0])));
+    my_snprintf(global_serverinfo_, sizeof(global_serverinfo_), "\tServerIp:%s\tServerHostName:%s\tServerPort:%d",
+                global_hostIP_, glob_hostname, mysqld_port);
+    // startup tianmu engine.
+    ha_rcengine_ = new core::Engine();
+    ret = ha_rcengine_->Init(total_ha);
+    {
+      TIANMU_LOG(LogCtl_Level::INFO,
+                 "\n"
+                 "------------------------------------------------------------"
+                 "----------------------------------"
+                 "-------------\n"
+                 "    ######  ########  #######  ##     ## ######## ######   "
+                 "######   \n"
+                 "   ##    ##    ##    ##     ## ####   ## ##       ##    ## "
+                 "##    ## \n"
+                 "   ##          ##    ##     ## ## ##  ## ##       ##    ## "
+                 "##    ## \n"
+                 "    ######     ##    ##     ## ##  ## ## ######   ##    ## "
+                 "######## \n"
+                 "         ##    ##    ##     ## ##   #### ##       ##    ## "
+                 "##    ## \n"
+                 "   ##    ##    ##    ##     ## ##    ### ##       ##    ## "
+                 "##    ## \n"
+                 "    ######     ##     #######  ##     ## ######## ######   "
+                 "######   \n"
+                 "------------------------------------------------------------"
+                 "----------------------------------"
+                 "-------------\n");
+    }
+
+  } catch (std::exception &e) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), e.what(), MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An exception error is caught: %s.", e.what());
+  } catch (...) {
+    my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), "An unknown system exception error caught.", MYF(0));
+    TIANMU_LOG(LogCtl_Level::ERROR, "An unknown system exception error caught.");
+  }
+
+  DBUG_RETURN(ret);
+}
+
+struct st_mysql_storage_engine tianmu_storage_engine = {MYSQL_HANDLERTON_INTERFACE_VERSION};
+
+int get_DelayedBufferUsage_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *var, char *buff) {
+  var->type = SHOW_CHAR;
+  var->value = buff;
+  std::string str = ha_rcengine_->DelayedBufferStat();
+  std::memcpy(buff, str.c_str(), str.length() + 1);
+  return 0;
+}
+
+int get_RowStoreUsage_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *var, char *buff) {
+  var->type = SHOW_CHAR;
+  var->value = buff;
+  std::string str = ha_rcengine_->RowStoreStat();
+  std::memcpy(buff, str.c_str(), str.length() + 1);
+  return 0;
+}
+
+int get_InsertPerMinute_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetIPM();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_QueryPerMinute_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetQPM();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_LoadPerMinute_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetLPM();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_Freeable_StatusVar([[maybe_unused]] MYSQL_THD thd, struct st_mysql_show_var *outvar, char *tmp) {
+  *((int64_t *)tmp) = mm::TraceableObject::GetFreeableSize();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONGLONG;
+  return 0;
+}
+
+int get_UnFreeable_StatusVar([[maybe_unused]] MYSQL_THD thd, struct st_mysql_show_var *outvar, char *tmp) {
+  *((int64_t *)tmp) = mm::TraceableObject::GetUnFreeableSize();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONGLONG;
+  return 0;
+}
+
+int get_MemoryScale_StatusVar([[maybe_unused]] MYSQL_THD thd, struct st_mysql_show_var *outvar, char *tmp) {
+  *((int64_t *)tmp) = mm::TraceableObject::MemorySettingsScale();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONGLONG;
+  return 0;
+}
+
+int get_InsertTotal_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetIT();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_QueryTotal_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetQT();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_LoadTotal_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetLT();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_LoadDupTotal_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetLDT();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_LoadDupPerMinute_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetLDPM();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_UpdateTotal_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetUT();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+int get_UpdatePerMinute_StatusVar([[maybe_unused]] MYSQL_THD thd, SHOW_VAR *outvar, char *tmp) {
+  *((int64_t *)tmp) = ha_rcengine_->GetUPM();
+  outvar->value = tmp;
+  outvar->type = SHOW_LONG;
+  return 0;
+}
+
+char masteslave_info[8192] = {0};
+
+SHOW_VAR tianmu_masterslave_dump[] = {{"info", masteslave_info, SHOW_CHAR, SHOW_SCOPE_UNDEF},
+                                      {NullS, NullS, SHOW_LONG, SHOW_SCOPE_UNDEF}};
+
+//  showtype
+//  =====================
+//  SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG,
+//  SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR,
+//  SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE
+
+int tianmu_throw_error_func([[maybe_unused]] MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var,
+                            [[maybe_unused]] void *save, struct st_mysql_value *value) {
+  int buffer_length = 512;
+  char buff[512] = {0};
+
+  DEBUG_ASSERT(value->value_type(value) == MYSQL_VALUE_TYPE_STRING);
+
+  my_message(static_cast<int>(common::ErrorCode::UNKNOWN_ERROR), value->val_str(value, buff, &buffer_length), MYF(0));
+  return -1;
+}
+
+static void update_func_str([[maybe_unused]] THD *thd, struct st_mysql_sys_var *var, void *tgt, const void *save) {
+  char *old = *(char **)tgt;
+  *(char **)tgt = *(char **)save;
+  if (var->flags & PLUGIN_VAR_MEMALLOC) {
+    *(char **)tgt = my_strdup(PSI_NOT_INSTRUMENTED, *(char **)save, MYF(0));
+    my_free(old);
+  }
+}
+
+void refresh_sys_table_func([[maybe_unused]] MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var, void *tgt,
+                            const void *save) {
+  *(my_bool *)tgt = *(my_bool *)save ? TRUE : FALSE;
+}
+
+void debug_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);
+void trace_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);
+void controlquerylog_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);
+void start_async_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);
+extern void async_join_update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);
+
+#define STATUS_FUNCTION(name, showtype, member)                                                             \
+  int get_##name##_StatusVar([[maybe_unused]] MYSQL_THD thd, struct st_mysql_show_var *outvar, char *tmp) { \
+    *((int64_t *)tmp) = ha_rcengine_->cache.member();                                                       \
+    outvar->value = tmp;                                                                                    \
+    outvar->type = showtype;                                                                                \
+    return 0;                                                                                               \
+  }
+
+#define MM_STATUS_FUNCTION(name, showtype, member)                                                          \
+  int get_##name##_StatusVar([[maybe_unused]] MYSQL_THD thd, struct st_mysql_show_var *outvar, char *tmp) { \
+    *((int64_t *)tmp) = mm::TraceableObject::Instance()->member();                                          \
+    outvar->value = tmp;                                                                                    \
+    outvar->type = showtype;                                                                                \
+    return 0;                                                                                               \
+  }
+
+#define STATUS_MEMBER(name, label) \
+  { "Tianmu_" #label, (char *)get_##name##_StatusVar, SHOW_FUNC, SHOW_SCOPE_GLOBAL }
+
+STATUS_FUNCTION(gdchits, SHOW_LONGLONG, getCacheHits)
+STATUS_FUNCTION(gdcmisses, SHOW_LONGLONG, getCacheMisses)
+STATUS_FUNCTION(gdcreleased, SHOW_LONGLONG, getReleased)
+STATUS_FUNCTION(gdcreadwait, SHOW_LONGLONG, getReadWait)
+STATUS_FUNCTION(gdcfalsewakeup, SHOW_LONGLONG, getFalseWakeup)
+STATUS_FUNCTION(gdcreadwaitinprogress, SHOW_LONGLONG, getReadWaitInProgress)
+STATUS_FUNCTION(gdcpackloads, SHOW_LONGLONG, getPackLoads)
+STATUS_FUNCTION(gdcloaderrors, SHOW_LONGLONG, getLoadErrors)
+STATUS_FUNCTION(gdcredecompress, SHOW_LONGLONG, getReDecompress)
+
+MM_STATUS_FUNCTION(mmallocblocks, SHOW_LONGLONG, getAllocBlocks)
+MM_STATUS_FUNCTION(mmallocobjs, SHOW_LONGLONG, getAllocObjs)
+MM_STATUS_FUNCTION(mmallocsize, SHOW_LONGLONG, getAllocSize)
+MM_STATUS_FUNCTION(mmallocpack, SHOW_LONGLONG, getAllocPack)
+MM_STATUS_FUNCTION(mmalloctemp, SHOW_LONGLONG, getAllocTemp)
+MM_STATUS_FUNCTION(mmalloctempsize, SHOW_LONGLONG, getAllocTempSize)
+MM_STATUS_FUNCTION(mmallocpacksize, SHOW_LONGLONG, getAllocPackSize)
+MM_STATUS_FUNCTION(mmfreeblocks, SHOW_LONGLONG, getFreeBlocks)
+MM_STATUS_FUNCTION(mmfreepacks, SHOW_LONGLONG, getFreePacks)
+MM_STATUS_FUNCTION(mmfreetemp, SHOW_LONGLONG, getFreeTemp)
+MM_STATUS_FUNCTION(mmfreepacksize, SHOW_LONGLONG, getFreePackSize)
+MM_STATUS_FUNCTION(mmfreetempsize, SHOW_LONGLONG, getFreeTempSize)
+MM_STATUS_FUNCTION(mmfreesize, SHOW_LONGLONG, getFreeSize)
+MM_STATUS_FUNCTION(mmrelease1, SHOW_LONGLONG, getReleaseCount1)
+MM_STATUS_FUNCTION(mmrelease2, SHOW_LONGLONG, getReleaseCount2)
+MM_STATUS_FUNCTION(mmrelease3, SHOW_LONGLONG, getReleaseCount3)
+MM_STATUS_FUNCTION(mmrelease4, SHOW_LONGLONG, getReleaseCount4)
+MM_STATUS_FUNCTION(mmreloaded, SHOW_LONGLONG, getReloaded)
+MM_STATUS_FUNCTION(mmreleasecount, SHOW_LONGLONG, getReleaseCount)
+MM_STATUS_FUNCTION(mmreleasetotal, SHOW_LONGLONG, getReleaseTotal)
+
+static struct st_mysql_show_var statusvars[] = {
+    STATUS_MEMBER(gdchits, gdc_hits),
+    STATUS_MEMBER(gdcmisses, gdc_misses),
+    STATUS_MEMBER(gdcreleased, gdc_released),
+    STATUS_MEMBER(gdcreadwait, gdc_readwait),
+    STATUS_MEMBER(gdcfalsewakeup, gdc_false_wakeup),
+    STATUS_MEMBER(gdcreadwaitinprogress, gdc_read_wait_in_progress),
+    STATUS_MEMBER(gdcpackloads, gdc_pack_loads),
+    STATUS_MEMBER(gdcloaderrors, gdc_load_errors),
+    STATUS_MEMBER(gdcredecompress, gdc_redecompress),
+    STATUS_MEMBER(mmrelease1, mm_release1),
+    STATUS_MEMBER(mmrelease2, mm_release2),
+    STATUS_MEMBER(mmrelease3, mm_release3),
+    STATUS_MEMBER(mmrelease4, mm_release4),
+    STATUS_MEMBER(mmallocblocks, mm_alloc_blocs),
+    STATUS_MEMBER(mmallocobjs, mm_alloc_objs),
+    STATUS_MEMBER(mmallocsize, mm_alloc_size),
+    STATUS_MEMBER(mmallocpack, mm_alloc_packs),
+    STATUS_MEMBER(mmalloctemp, mm_alloc_temp),
+    STATUS_MEMBER(mmalloctempsize, mm_alloc_temp_size),
+    STATUS_MEMBER(mmallocpacksize, mm_alloc_pack_size),
+    STATUS_MEMBER(mmfreeblocks, mm_free_blocks),
+    STATUS_MEMBER(mmfreepacks, mm_free_packs),
+    STATUS_MEMBER(mmfreetemp, mm_free_temp),
+    STATUS_MEMBER(mmfreepacksize, mm_free_pack_size),
+    STATUS_MEMBER(mmfreetempsize, mm_free_temp_size),
+    STATUS_MEMBER(mmfreesize, mm_free_size),
+    STATUS_MEMBER(mmreloaded, mm_reloaded),
+    STATUS_MEMBER(mmreleasecount, mm_release_count),
+    STATUS_MEMBER(mmreleasetotal, mm_release_total),
+    STATUS_MEMBER(DelayedBufferUsage, delay_buffer_usage),
+    STATUS_MEMBER(RowStoreUsage, row_store_usage),
+    STATUS_MEMBER(Freeable, mm_freeable),
+    STATUS_MEMBER(InsertPerMinute, insert_per_minute),
+    STATUS_MEMBER(LoadPerMinute, load_per_minute),
+    STATUS_MEMBER(QueryPerMinute, query_per_minute),
+    STATUS_MEMBER(MemoryScale, mm_scale),
+    STATUS_MEMBER(UnFreeable, mm_unfreeable),
+    STATUS_MEMBER(InsertTotal, insert_total),
+    STATUS_MEMBER(LoadTotal, load_total),
+    STATUS_MEMBER(QueryTotal, query_total),
+    STATUS_MEMBER(LoadDupPerMinute, load_dup_per_minute),
+    STATUS_MEMBER(LoadDupTotal, load_dup_total),
+    STATUS_MEMBER(UpdatePerMinute, update_per_minute),
+    STATUS_MEMBER(UpdateTotal, update_total),
+    {0, 0, SHOW_UNDEF, SHOW_SCOPE_UNDEF},
+};
+
+static MYSQL_SYSVAR_BOOL(refresh_sys_tianmu, tianmu_sysvar_refresh_sys_table, PLUGIN_VAR_BOOL, "-", NULL,
+                         refresh_sys_table_func, TRUE);
+static MYSQL_THDVAR_STR(trigger_error, PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL, "-", tianmu_throw_error_func,
+                        update_func_str, NULL);
+
+static MYSQL_SYSVAR_INT(ini_allowmysqlquerypath, tianmu_sysvar_allowmysqlquerypath, PLUGIN_VAR_READONLY, "-", NULL,
+                        NULL, 0, 0, 1, 0);
+static MYSQL_SYSVAR_STR(ini_cachefolder, tianmu_sysvar_cachefolder, PLUGIN_VAR_READONLY, "-", NULL, NULL, "cache");
+static MYSQL_SYSVAR_INT(ini_knlevel, tianmu_sysvar_knlevel, PLUGIN_VAR_READONLY, "-", NULL, NULL, 99, 0, 99, 0);
+static MYSQL_SYSVAR_BOOL(ini_pushdown, tianmu_sysvar_pushdown, PLUGIN_VAR_READONLY, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_INT(ini_servermainheapsize, tianmu_sysvar_servermainheapsize, PLUGIN_VAR_READONLY, "-", NULL, NULL,
+                        0, 0, 1000000, 0);
+static MYSQL_SYSVAR_BOOL(ini_usemysqlimportexportdefaults, tianmu_sysvar_usemysqlimportexportdefaults,
+                         PLUGIN_VAR_READONLY, "-", NULL, NULL, FALSE);
+static MYSQL_SYSVAR_INT(ini_threadpoolsize, tianmu_sysvar_threadpoolsize, PLUGIN_VAR_READONLY, "-", NULL, NULL, 1, 0,
+                        1000000, 0);
+static MYSQL_SYSVAR_INT(ini_cachesizethreshold, tianmu_sysvar_cachesizethreshold, PLUGIN_VAR_INT, "-", NULL, NULL, 4, 0,
+                        1024, 0);
+static MYSQL_SYSVAR_INT(ini_cachereleasethreshold, tianmu_sysvar_cachereleasethreshold, PLUGIN_VAR_INT, "-", NULL, NULL,
+                        100, 0, 100000, 0);
+static MYSQL_SYSVAR_BOOL(insert_delayed, tianmu_sysvar_insert_delayed, PLUGIN_VAR_READONLY, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_INT(insert_cntthreshold, tianmu_sysvar_insert_cntthreshold, PLUGIN_VAR_READONLY, "-", NULL, NULL, 2,
+                        0, 1000, 0);
+static MYSQL_SYSVAR_INT(insert_numthreshold, tianmu_sysvar_insert_numthreshold, PLUGIN_VAR_READONLY, "-", NULL, NULL,
+                        10000, 0, 100000, 0);
+static MYSQL_SYSVAR_INT(insert_wait_ms, tianmu_sysvar_insert_wait_ms, PLUGIN_VAR_READONLY, "-", NULL, NULL, 100, 10,
+                        10000, 0);
+static MYSQL_SYSVAR_INT(insert_wait_time, tianmu_sysvar_insert_wait_time, PLUGIN_VAR_INT, "-", NULL, NULL, 1000, 0,
+                        600000, 0);
+static MYSQL_SYSVAR_INT(insert_max_buffered, tianmu_sysvar_insert_max_buffered, PLUGIN_VAR_READONLY, "-", NULL, NULL,
+                        65536, 0, 10000000, 0);
+static MYSQL_SYSVAR_BOOL(compensation_start, tianmu_sysvar_compensation_start, PLUGIN_VAR_BOOL, "-", NULL, NULL, FALSE);
+static MYSQL_SYSVAR_STR(hugefiledir, tianmu_sysvar_hugefiledir, PLUGIN_VAR_READONLY, "-", NULL, NULL, "");
+static MYSQL_SYSVAR_INT(cachinglevel, tianmu_sysvar_cachinglevel, PLUGIN_VAR_READONLY, "-", NULL, NULL, 1, 0, 512, 0);
+static MYSQL_SYSVAR_STR(mm_policy, tianmu_sysvar_mm_policy, PLUGIN_VAR_READONLY, "-", NULL, NULL, "");
+static MYSQL_SYSVAR_INT(mm_hardlimit, tianmu_sysvar_mm_hardlimit, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0, 1, 0);
+static MYSQL_SYSVAR_STR(mm_releasepolicy, tianmu_sysvar_mm_releasepolicy, PLUGIN_VAR_READONLY, "-", NULL, NULL, "2q");
+static MYSQL_SYSVAR_INT(mm_largetempratio, tianmu_sysvar_mm_largetempratio, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0,
+                        99, 0);
+static MYSQL_SYSVAR_INT(mm_largetemppool_threshold, tianmu_sysvar_mm_large_threshold, PLUGIN_VAR_INT,
+                        "size threshold in MB for using large temp thread pool", NULL, NULL, 16, 0, 10240, 0);
+static MYSQL_SYSVAR_INT(sync_buffers, tianmu_sysvar_sync_buffers, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0, 1, 0);
+
+static MYSQL_SYSVAR_INT(query_threads, tianmu_sysvar_query_threads, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0, 100, 0);
+static MYSQL_SYSVAR_INT(load_threads, tianmu_sysvar_load_threads, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0, 100, 0);
+static MYSQL_SYSVAR_INT(bg_load_threads, tianmu_sysvar_bg_load_threads, PLUGIN_VAR_READONLY, "-", NULL, NULL, 0, 0, 100,
+                        0);
+static MYSQL_SYSVAR_INT(insert_buffer_size, tianmu_sysvar_insert_buffer_size, PLUGIN_VAR_READONLY, "-", NULL, NULL, 512,
+                        512, 10000, 0);
+
+static MYSQL_THDVAR_INT(session_debug_level, PLUGIN_VAR_INT, "session debug level", NULL, debug_update, 3, 0, 5, 0);
+static MYSQL_THDVAR_INT(control_trace, PLUGIN_VAR_OPCMDARG, "ini controltrace", NULL, trace_update, 0, 0, 100, 0);
+static MYSQL_SYSVAR_INT(global_debug_level, tianmu_sysvar_global_debug_level, PLUGIN_VAR_INT, "global debug level",
+                        NULL, NULL, 4, 0, 5, 0);
+
+static MYSQL_SYSVAR_INT(distinct_cache_size, tianmu_sysvar_distcache_size, PLUGIN_VAR_INT,
+                        "Upper byte limit for GroupDistinctCache buffer", NULL, NULL, 64, 64, 256, 0);
+static MYSQL_SYSVAR_BOOL(filterevaluation_speedup, tianmu_sysvar_filterevaluation_speedup, PLUGIN_VAR_BOOL, "-", NULL,
+                         NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(groupby_speedup, tianmu_sysvar_groupby_speedup, PLUGIN_VAR_BOOL, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(orderby_speedup, tianmu_sysvar_orderby_speedup, PLUGIN_VAR_BOOL, "-", NULL, NULL, FALSE);
+static MYSQL_SYSVAR_INT(join_parallel, tianmu_sysvar_join_parallel, PLUGIN_VAR_INT,
+                        "join matching parallel: 0-Disabled, 1-Auto, N-specify count", NULL, NULL, 1, 0, 1000, 0);
+static MYSQL_SYSVAR_INT(join_splitrows, tianmu_sysvar_join_splitrows, PLUGIN_VAR_INT,
+                        "join split rows:0-Disabled, 1-Auto, N-specify count", NULL, NULL, 0, 0, 1000, 0);
+static MYSQL_SYSVAR_BOOL(minmax_speedup, tianmu_sysvar_minmax_speedup, PLUGIN_VAR_BOOL, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_UINT(index_cache_size, tianmu_sysvar_index_cache_size, PLUGIN_VAR_READONLY,
+                         "Index cache size in MB", NULL, NULL, 0, 0, 65536, 0);
+static MYSQL_SYSVAR_BOOL(index_search, tianmu_sysvar_index_search, PLUGIN_VAR_BOOL, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(enable_rowstore, tianmu_sysvar_enable_rowstore, PLUGIN_VAR_BOOL, "-", NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(parallel_filloutput, tianmu_sysvar_parallel_filloutput, PLUGIN_VAR_BOOL, "-", NULL, NULL,
+                         TRUE);
+static MYSQL_SYSVAR_BOOL(parallel_mapjoin, tianmu_sysvar_parallel_mapjoin, PLUGIN_VAR_BOOL, "-", NULL, NULL, FALSE);
+
+static MYSQL_SYSVAR_INT(max_execution_time, tianmu_sysvar_max_execution_time, PLUGIN_VAR_INT,
+                        "max query execution time in seconds", NULL, NULL, 0, 0, 10000, 0);
+static MYSQL_SYSVAR_INT(ini_controlquerylog, tianmu_sysvar_controlquerylog, PLUGIN_VAR_INT, "global controlquerylog",
+                        NULL, controlquerylog_update, 1, 0, 100, 0);
+
+static const char *dist_policy_names[] = {"round-robin", "random", "space", 0};
+static TYPELIB policy_typelib_t = {array_elements(dist_policy_names) - 1, "dist_policy_names", dist_policy_names, 0};
+static MYSQL_SYSVAR_ENUM(data_distribution_policy, tianmu_sysvar_dist_policy, PLUGIN_VAR_RQCMDARG,
+                         "Specifies the policy to distribute column data among multiple data "
+                         "directories."
+                         "Possible values are round-robin(default), random, and space",
+                         NULL, NULL, 2, &policy_typelib_t);
+
+static MYSQL_SYSVAR_INT(disk_usage_threshold, tianmu_sysvar_disk_usage_threshold, PLUGIN_VAR_INT,
+                        "Specifies the disk usage threshold for data diretories.", NULL, NULL, 85, 10, 99, 0);
+
+static MYSQL_SYSVAR_UINT(lookup_max_size, tianmu_sysvar_lookup_max_size, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+                         "Lookup dictionary max size", NULL, NULL, 100000, 1000, 1000000, 0);
+
+static MYSQL_SYSVAR_BOOL(qps_log, tianmu_sysvar_qps_log, PLUGIN_VAR_BOOL, "-", NULL, NULL, TRUE);
+
+static MYSQL_SYSVAR_BOOL(force_hashjoin, tianmu_sysvar_force_hashjoin, PLUGIN_VAR_BOOL, "-", NULL, NULL, FALSE);
+static MYSQL_SYSVAR_INT(start_async, tianmu_sysvar_start_async, PLUGIN_VAR_INT,
+                        "Enable async, specifies async threads x/100 * cpus", NULL, start_async_update, 0, 0, 100, 0);
+static MYSQL_SYSVAR_STR(async_join, tianmu_sysvar_async_join, PLUGIN_VAR_STR,
+                        "Set async join params: packStep;traverseCount;matchCount", NULL, async_join_update, "1;0;0;0");
+static MYSQL_SYSVAR_BOOL(join_disable_switch_side, tianmu_sysvar_join_disable_switch_side, PLUGIN_VAR_BOOL, "-", NULL,
+                         NULL, FALSE);
+static MYSQL_SYSVAR_BOOL(enable_histogram_cmap_bloom, tianmu_sysvar_enable_histogram_cmap_bloom, PLUGIN_VAR_BOOL, "-",
+                         NULL, NULL, FALSE);
+static MYSQL_SYSVAR_BOOL(large_prefix, tianmu_sysvar_large_prefix, PLUGIN_VAR_RQCMDARG,
+                         "Support large index prefix length of 3072 bytes. If off, the maximum "
+                         "index prefix length is 767.",
+                         NULL, NULL, TRUE);
+static MYSQL_SYSVAR_UINT(result_sender_rows, tianmu_sysvar_result_sender_rows, PLUGIN_VAR_UNSIGNED,
+                         "The number of rows to load at a time when processing "
+                         "queries like select xxx from yyya",
+                         NULL, NULL, 65536, 1024, 131072, 0);
+
+void debug_update(MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var, void *var_ptr, const void *save) {
+  if (ha_rcengine_) {
+    auto cur_conn = ha_rcengine_->GetTx(thd);
+    // set debug_level for connection level
+    cur_conn->SetDebugLevel(*((int *)save));
+  }
+  *((int *)var_ptr) = *((int *)save);
+}
+
+void trace_update(MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var, void *var_ptr, const void *save) {
+  *((int *)var_ptr) = *((int *)save);
+  // get global mysql_sysvar_control_trace
+  tianmu_sysvar_controltrace = THDVAR(nullptr, control_trace);
+  if (ha_rcengine_) {
+    core::Transaction *cur_conn = ha_rcengine_->GetTx(thd);
+    cur_conn->SetSessionTrace(*((int *)save));
+    ConfigureRCControl();
+  }
+}
+
+void controlquerylog_update([[maybe_unused]] MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var,
+                            void *var_ptr, const void *save) {
+  *((int *)var_ptr) = *((int *)save);
+  int control = *((int *)var_ptr);
+  if (ha_rcengine_) {
+    control ? rc_querylog_.setOn() : rc_querylog_.setOff();
+  }
+}
+
+void start_async_update([[maybe_unused]] MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var, void *var_ptr,
+                        const void *save) {
+  int percent = *((int *)save);
+  *((int *)var_ptr) = percent;
+  if (ha_rcengine_) {
+    ha_rcengine_->ResetTaskExecutor(percent);
+  }
+}
+
+void resolve_async_join_settings(const std::string &settings) {
+  std::vector<std::string> splits_vec;
+  boost::split(splits_vec, settings, boost::is_any_of(";"));
+  if (splits_vec.size() >= 4) {
+    try {
+      tianmu_sysvar_async_join_setting.pack_per_step = boost::lexical_cast<int>(splits_vec[0]);
+      tianmu_sysvar_async_join_setting.rows_per_step = boost::lexical_cast<int>(splits_vec[1]);
+      tianmu_sysvar_async_join_setting.traverse_slices = boost::lexical_cast<int>(splits_vec[2]);
+      tianmu_sysvar_async_join_setting.match_slices = boost::lexical_cast<int>(splits_vec[3]);
+    } catch (...) {
+      TIANMU_LOG(LogCtl_Level::ERROR, "Failed resolve async join settings");
+    }
+  }
+}
+
+void async_join_update([[maybe_unused]] MYSQL_THD thd, [[maybe_unused]] struct st_mysql_sys_var *var,
+                       [[maybe_unused]] void *var_ptr, const void *save) {
+  const char *str = *static_cast<const char *const *>(save);
+  std::string settings(str);
+  resolve_async_join_settings(settings);
+}
+
+static struct st_mysql_sys_var *tianmu_showvars[] = {MYSQL_SYSVAR(bg_load_threads),
+                                                     MYSQL_SYSVAR(cachinglevel),
+                                                     MYSQL_SYSVAR(compensation_start),
+                                                     MYSQL_SYSVAR(control_trace),
+                                                     MYSQL_SYSVAR(data_distribution_policy),
+                                                     MYSQL_SYSVAR(disk_usage_threshold),
+                                                     MYSQL_SYSVAR(distinct_cache_size),
+                                                     MYSQL_SYSVAR(filterevaluation_speedup),
+                                                     MYSQL_SYSVAR(global_debug_level),
+                                                     MYSQL_SYSVAR(groupby_speedup),
+                                                     MYSQL_SYSVAR(hugefiledir),
+                                                     MYSQL_SYSVAR(index_cache_size),
+                                                     MYSQL_SYSVAR(index_search),
+                                                     MYSQL_SYSVAR(enable_rowstore),
+                                                     MYSQL_SYSVAR(ini_allowmysqlquerypath),
+                                                     MYSQL_SYSVAR(ini_cachefolder),
+                                                     MYSQL_SYSVAR(ini_cachereleasethreshold),
+                                                     MYSQL_SYSVAR(ini_cachesizethreshold),
+                                                     MYSQL_SYSVAR(ini_controlquerylog),
+                                                     MYSQL_SYSVAR(ini_knlevel),
+                                                     MYSQL_SYSVAR(ini_pushdown),
+                                                     MYSQL_SYSVAR(ini_servermainheapsize),
+                                                     MYSQL_SYSVAR(ini_threadpoolsize),
+                                                     MYSQL_SYSVAR(ini_usemysqlimportexportdefaults),
+                                                     MYSQL_SYSVAR(insert_buffer_size),
+                                                     MYSQL_SYSVAR(insert_cntthreshold),
+                                                     MYSQL_SYSVAR(insert_delayed),
+                                                     MYSQL_SYSVAR(insert_max_buffered),
+                                                     MYSQL_SYSVAR(insert_numthreshold),
+                                                     MYSQL_SYSVAR(insert_wait_ms),
+                                                     MYSQL_SYSVAR(insert_wait_time),
+                                                     MYSQL_SYSVAR(join_disable_switch_side),
+                                                     MYSQL_SYSVAR(enable_histogram_cmap_bloom),
+                                                     MYSQL_SYSVAR(join_parallel),
+                                                     MYSQL_SYSVAR(join_splitrows),
+                                                     MYSQL_SYSVAR(large_prefix),
+                                                     MYSQL_SYSVAR(load_threads),
+                                                     MYSQL_SYSVAR(lookup_max_size),
+                                                     MYSQL_SYSVAR(max_execution_time),
+                                                     MYSQL_SYSVAR(minmax_speedup),
+                                                     MYSQL_SYSVAR(mm_hardlimit),
+                                                     MYSQL_SYSVAR(mm_largetempratio),
+                                                     MYSQL_SYSVAR(mm_largetemppool_threshold),
+                                                     MYSQL_SYSVAR(mm_policy),
+                                                     MYSQL_SYSVAR(mm_releasepolicy),
+                                                     MYSQL_SYSVAR(orderby_speedup),
+                                                     MYSQL_SYSVAR(parallel_filloutput),
+                                                     MYSQL_SYSVAR(parallel_mapjoin),
+                                                     MYSQL_SYSVAR(qps_log),
+                                                     MYSQL_SYSVAR(query_threads),
+                                                     MYSQL_SYSVAR(refresh_sys_tianmu),
+                                                     MYSQL_SYSVAR(session_debug_level),
+                                                     MYSQL_SYSVAR(sync_buffers),
+                                                     MYSQL_SYSVAR(trigger_error),
+                                                     MYSQL_SYSVAR(async_join),
+                                                     MYSQL_SYSVAR(force_hashjoin),
+                                                     MYSQL_SYSVAR(start_async),
+                                                     MYSQL_SYSVAR(result_sender_rows),
+                                                     NULL};
+}  // namespace dbhandler
+}  // namespace Tianmu
+
+mysql_declare_plugin(tianmu){
+    MYSQL_STORAGE_ENGINE_PLUGIN,
+    &Tianmu::dbhandler::tianmu_storage_engine,
+    "TIANMU",
+    "StoneAtom Group Holding Limited",
+    "Tianmu storage engine",
+    PLUGIN_LICENSE_GPL,
+    Tianmu::dbhandler::rcbase_init_func, /* Plugin Init */
+    Tianmu::dbhandler::rcbase_done_func, /* Plugin Deinit */
+    0x0001 /* 0.1 */,
+    Tianmu::dbhandler::statusvars,      /* status variables  */
+    Tianmu::dbhandler::tianmu_showvars, /* system variables  */
+    NULL,                               /* config options    */
+    0                                   /* flags for plugin */
+} mysql_declare_plugin_end;
diff --git a/storage/tianmu/types/rc_data_types.cpp b/storage/tianmu/types/rc_data_types.cpp
new file mode 100644
index 000000000..b558cc819
--- /dev/null
+++ b/storage/tianmu/types/rc_data_types.cpp
@@ -0,0 +1,140 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+
+#include "rc_data_types.h"
+
+#include "core/rc_attr.h"
+#include "core/rc_attr_typeinfo.h"
+
+namespace Tianmu {
+namespace types {
+
+RCDataType::~RCDataType() {}
+
+bool RCDataType::AreComperable(const RCDataType &rcdt) const { return RCDataType::AreComperable(*this, rcdt); }
+
+bool RCDataType::AreComperable(const RCDataType &rcdt1, const RCDataType &rcdt2) {
+  common::CT att1 = rcdt1.Type();
+  common::CT att2 = rcdt2.Type();
+  return AreComparable(att1, att2);
+}
+
+bool RCDataType::compare(const RCDataType &rcdt1, const RCDataType &rcdt2, common::Operator op, char like_esc) {
+  // DEBUG_ASSERT(RCDataType::AreComperable(rcdt1, rcdt2));
+  if (op == common::Operator::O_LIKE || op == common::Operator::O_NOT_LIKE) {
+    if (rcdt1.IsNull() || rcdt2.IsNull())
+      return false;
+    BString x, y;
+    BString *rcbs1 = dynamic_cast<BString *>(const_cast<RCDataType *>(&rcdt1));
+    if (!rcbs1) {
+      x = rcdt1.ToBString();
+      rcbs1 = &x;
+    }
+    BString *rcbs2 = dynamic_cast<BString *>(const_cast<RCDataType *>(&rcdt2));
+    if (!rcbs2) {
+      y = rcdt2.ToBString();
+      rcbs2 = &y;
+    }
+    bool res = rcbs1->Like(*rcbs2, like_esc);
+    if (op == common::Operator::O_LIKE)
+      return res;
+    else
+      return !res;
+  } else if (!rcdt1.IsNull() && !rcdt2.IsNull() &&
+             (((op == common::Operator::O_EQ) && rcdt1 == rcdt2) ||
+              (op == common::Operator::O_NOT_EQ && rcdt1 != rcdt2) ||
+              (op == common::Operator::O_LESS && rcdt1 < rcdt2) ||
+              (op == common::Operator::O_LESS_EQ && (rcdt1 < rcdt2 || rcdt1 == rcdt2)) ||
+              (op == common::Operator::O_MORE && (!(rcdt1 < rcdt2) && rcdt1 != rcdt2)) ||
+              (op == common::Operator::O_MORE_EQ && (!(rcdt1 < rcdt2) || rcdt1 == rcdt2))))
+    return true;
+  return false;
+}
+
+bool RCDataType::compare(const RCDataType &rcdt, common::Operator op, char like_esc) const {
+  return RCDataType::compare(*this, rcdt, op, like_esc);
+}
+
+bool AreComparable(common::CT attr1_t, common::CT attr2_t) {
+  if (attr1_t == attr2_t)
+    return true;
+  if ((core::ATI::IsDateTimeType(attr1_t)) && (core::ATI::IsDateTimeType(attr2_t)))
+    return true;
+  if ((core::ATI::IsTxtType(attr2_t) && attr1_t == common::CT::VARBYTE) ||
+      (core::ATI::IsTxtType(attr1_t) && attr2_t == common::CT::VARBYTE))
+    return true;
+  if ((((attr1_t == common::CT::TIME) || (attr1_t == common::CT::DATE)) && attr2_t != common::CT::DATETIME) ||
+      (((attr2_t == common::CT::TIME) || (attr2_t == common::CT::DATE)) && attr1_t != common::CT::DATETIME) ||
+      (core::ATI::IsBinType(attr1_t) && !core::ATI::IsBinType(attr2_t)) ||
+      (core::ATI::IsBinType(attr2_t) && !core::ATI::IsBinType(attr1_t)) ||
+      (core::ATI::IsTxtType(attr1_t) && !core::ATI::IsTxtType(attr2_t)) ||
+      (core::ATI::IsTxtType(attr2_t) && !core::ATI::IsTxtType(attr1_t)))
+    return false;
+  return true;
+}
+
+bool RCDataType::ToDecimal(const RCDataType &in, int scale, RCNum &out) {
+  if (RCNum *rcn = dynamic_cast<RCNum *>(const_cast<RCDataType *>(&in))) {
+    if (rcn->IsDecimal(scale)) {
+      out = rcn->ToDecimal(scale);
+      return true;
+    }
+  } else if (BString *rcs = dynamic_cast<BString *>(const_cast<RCDataType *>(&in))) {
+    if (RCNum::Parse(*rcs, out) == common::ErrorCode::SUCCESS)
+      return true;
+  }
+  return false;
+}
+
+bool RCDataType::ToInt(const RCDataType &in, RCNum &out) {
+  if (RCNum *rcn = dynamic_cast<RCNum *>(const_cast<RCDataType *>(&in))) {
+    if (rcn->IsInt()) {
+      out = rcn->ToInt();
+      return true;
+    }
+  } else if (BString *rcs = dynamic_cast<BString *>(const_cast<RCDataType *>(&in))) {
+    if (RCNum::Parse(*rcs, out) == common::ErrorCode::SUCCESS)
+      return true;
+  }
+  return false;
+}
+
+bool RCDataType::ToReal(const RCDataType &in, RCNum &out) {
+  if (RCNum *rcn = dynamic_cast<RCNum *>(const_cast<RCDataType *>(&in))) {
+    if (rcn->IsReal()) {
+      out = rcn->ToReal();
+      return true;
+    }
+  } else if (BString *rcs = dynamic_cast<BString *>(const_cast<RCDataType *>(&in))) {
+    if (RCNum::ParseReal(*rcs, out, common::CT::UNK) == common::ErrorCode::SUCCESS)
+      return true;
+  }
+  return false;
+}
+
+ValueTypeEnum RCDataType::GetValueType(common::CT attr_type) {
+  if (core::ATI::IsNumericType(attr_type))
+    return ValueTypeEnum::NUMERIC_TYPE;
+  else if (core::ATI::IsDateTimeType(attr_type))
+    return ValueTypeEnum::DATE_TIME_TYPE;
+  else if (core::ATI::IsStringType(attr_type))
+    return ValueTypeEnum::STRING_TYPE;
+  return ValueTypeEnum::NULL_TYPE;
+}
+
+}  // namespace types
+}  // namespace Tianmu
diff --git a/storage/tianmu/types/rc_num.cpp b/storage/tianmu/types/rc_num.cpp
new file mode 100644
index 000000000..6b05ebd6a
--- /dev/null
+++ b/storage/tianmu/types/rc_num.cpp
@@ -0,0 +1,605 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+
+#include "rc_num.h"
+
+#include <cmath>
+
+#include "common/assert.h"
+#include "core/tools.h"
+#include "system/txt_utils.h"
+#include "types/rc_data_types.h"
+#include "types/value_parser4txt.h"
+
+namespace Tianmu {
+namespace types {
+
+RCNum::RCNum(common::CT attrt) : value_(0), scale_(0), is_double_(false), is_dot_(false), attr_type_(attrt) {}
+
+RCNum::RCNum(int64_t value_, short scale, bool is_double_, common::CT attrt) {
+  Assign(value_, scale, is_double_, attrt);
+}
+
+RCNum::RCNum(double value_)
+    : value_(*(int64_t *)&value_), scale_(0), is_double_(true), is_dot_(false), attr_type_(common::CT::REAL) {
+  null = (value_ == NULL_VALUE_D ? true : false);
+}
+
+RCNum::RCNum(const RCNum &rcn)
+    : ValueBasic<RCNum>(rcn),
+      value_(rcn.value_),
+      scale_(rcn.scale_),
+      is_double_(rcn.is_double_),
+      is_dot_(rcn.is_dot_),
+      attr_type_(rcn.attr_type_) {
+  null = rcn.null;
+}
+
+RCNum::~RCNum() {}
+
+RCNum &RCNum::Assign(int64_t value_, short scale, bool is_double_, common::CT attrt) {
+  this->value_ = value_;
+  this->scale_ = scale;
+  this->is_double_ = is_double_;
+  this->attr_type_ = attrt;
+
+  if (scale != -1 && !is_double_) {
+    if (scale != 0 || attrt == common::CT::UNK) {
+      is_dot_ = true;
+      this->attr_type_ = common::CT::NUM;
+    }
+  }
+  if (scale <= -1 && !is_double_)
+    scale_ = 0;
+  if (is_double_) {
+    if (!(this->attr_type_ == common::CT::REAL || this->attr_type_ == common::CT::FLOAT))
+      this->attr_type_ = common::CT::REAL;
+    this->is_dot_ = false;
+    scale_ = 0;
+    null = (value_ == *(int64_t *)&NULL_VALUE_D ? true : false);
+  } else
+    null = (value_ == common::NULL_VALUE_64 ? true : false);
+  return *this;
+}
+
+RCNum &RCNum::Assign(double value_) {
+  this->value_ = *(int64_t *)&value_;
+  this->scale_ = 0;
+  this->is_double_ = true;
+  this->is_dot_ = false;
+  this->attr_type_ = common::CT::REAL;
+  common::double_int_t v(value_);
+  null = (v.i == common::NULL_VALUE_64 ? true : false);
+  return *this;
+}
+
+common::ErrorCode RCNum::Parse(const BString &rcs, RCNum &rcn, common::CT at) {
+  return ValueParserForText::Parse(rcs, rcn, at);
+}
+
+common::ErrorCode RCNum::ParseReal(const BString &rcbs, RCNum &rcn, common::CT at) {
+  return ValueParserForText::ParseReal(rcbs, rcn, at);
+}
+
+common::ErrorCode RCNum::ParseNum(const BString &rcs, RCNum &rcn, short scale) {
+  return ValueParserForText::ParseNum(rcs, rcn, scale);
+}
+
+RCNum &RCNum::operator=(const RCNum &rcn) {
+  value_ = rcn.value_;
+  is_double_ = rcn.is_double_;
+  scale_ = rcn.scale_;
+  null = rcn.null;
+  attr_type_ = rcn.attr_type_;
+  return *this;
+}
+
+RCNum &RCNum::operator=(const RCDataType &rcdt) {
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    *this = (RCNum &)rcdt;
+  else {
+    RCNum rcn1;
+    if (common::IsError(RCNum::Parse(rcdt.ToBString(), rcn1, this->attr_type_))) {
+      *this = rcn1;
+    } else {
+      TIANMU_ERROR("Unsupported assign operation!");
+      null = true;
+    }
+  }
+  return *this;
+}
+
+common::CT RCNum::Type() const { return attr_type_; }
+
+bool RCNum::IsDecimal(ushort scale) const {
+  if (core::ATI::IsIntegerType(this->attr_type_)) {
+    return GetDecIntLen() <= (MAX_DEC_PRECISION - scale);
+  } else if (attr_type_ == common::CT::NUM) {
+    if (this->GetDecFractLen() <= scale)
+      return true;
+    if (scale_ > scale)
+      return value_ % (int64_t)Uint64PowOfTen(scale_ - scale) == 0;
+    return true;
+  } else {
+    double f = GetFractPart();
+    return f == 0.0 || (fabs(f) >= (1.0 / (double)Uint64PowOfTen(scale)));
+  }
+  return false;
+}
+
+bool RCNum::IsInt() const {
+  if (!is_double_) {
+    if ((value_ % (int64_t)Uint64PowOfTen(scale_)) != 0) {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+RCNum RCNum::ToDecimal(int scale) const {
+  int64_t tmpv = 0;
+  short tmpp = 0;
+  int sign = 1;
+
+  if (is_double_) {
+    double intpart(0);
+    double fracpart(modf(*(double *)&value_, &intpart));
+
+    if (intpart < 0 || fracpart < 0) {
+      sign = -1;
+      intpart = fabs(intpart);
+      fracpart = fabs(fracpart);
+    }
+
+    if (scale == -1) {
+      if (intpart >= Uint64PowOfTen(MAX_DEC_PRECISION)) {
+        if (sign != 1)
+          scale = 0;
+      } else {
+        int l = 0;
+        if (intpart != 0)
+          l = (int)floor(log10(intpart)) + 1;
+        scale = MAX_DEC_PRECISION - l;
+      }
+    }
+
+    if (intpart >= Uint64PowOfTen(MAX_DEC_PRECISION)) {
+      tmpv = (Uint64PowOfTen(MAX_DEC_PRECISION) - 1);
+    } else
+      tmpv = (int64_t)intpart * Uint64PowOfTen(scale) + std::llround(fracpart * Uint64PowOfTen(scale));
+
+    tmpp = scale;
+  } else {
+    tmpv = this->value_;
+    tmpp = this->scale_;
+    if (scale != -1) {
+      if (tmpp > scale)
+        tmpv /= (int64_t)Uint64PowOfTen(tmpp - scale);
+      else
+        tmpv *= (int64_t)Uint64PowOfTen(scale - tmpp);
+      tmpp = scale;
+    }
+  }
+  return RCNum(tmpv * sign, tmpp);
+}
+
+RCNum RCNum::ToReal() const {
+  if (core::ATI::IsRealType(attr_type_)) {
+    return RCNum(*(double *)&value_);
+  }
+  return RCNum((double)((double)(this->value_ / PowOfTen(scale_))));
+}
+
+RCNum RCNum::ToInt() const { return GetIntPart(); }
+
+static char *Text(int64_t value_, char buf[], int scale) {
+  bool sign = true;
+  if (value_ < 0) {
+    sign = false;
+    value_ *= -1;
+  }
+  longlong2str(value_, buf, 10);
+  int l = (int)std::strlen(buf);
+  std::memset(buf + l + 1, ' ', 21 - l);
+  int pos = 21;
+  int i = 0;
+  for (i = l; i >= 0; i--) {
+    if (scale != 0 && pos + scale == 20) {
+      buf[pos--] = '.';
+      i++;
+    } else {
+      buf[pos--] = buf[i];
+      buf[i] = ' ';
+    }
+  }
+
+  if (scale >= l) {
+    buf[20 - scale] = '.';
+    buf[20 - scale - 1] = '0';
+    i = 20 - scale + 1;
+    while (buf[i] == ' ') buf[i++] = '0';
+  }
+  pos = 0;
+  while (buf[pos] == ' ') pos++;
+  if (!sign)
+    buf[--pos] = '-';
+  return buf + pos;
+}
+
+BString RCNum::ToBString() const {
+  if (!IsNull()) {
+    static int const SIZE(24);
+    char buf[SIZE];
+    if (core::ATI::IsRealType(attr_type_)) {
+      gcvt(*(double *)&value_, 15, buf);
+      size_t s = std::strlen(buf);
+      if (s && buf[s - 1] == '.')
+        buf[s - 1] = 0;
+    } else if (core::ATI::IsIntegerType(attr_type_))
+      std::sprintf(buf, "%ld", value_);
+    else {
+      return BString(Text(value_, buf, scale_), 0, true);
+    }
+    return BString(buf, std::strlen(buf), true);
+  }
+  return BString();
+}
+
+RCNum::operator double() const {
+  return (core::ATI::IsRealType(Type()) || Type() == common::CT::FLOAT) ? *(double *)&value_
+                                                                        : GetIntPart() + GetFractPart();
+}
+
+bool RCNum::operator==(const RCDataType &rcdt) const {
+  if (null || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) == 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) == 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (rcdt == this->ToBString());
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+bool RCNum::operator!=(const RCDataType &rcdt) const {
+  if (null || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) != 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) != 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (rcdt != this->ToBString());
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+bool RCNum::operator<(const RCDataType &rcdt) const {
+  if (IsNull() || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) < 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) < 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (this->ToBString() < rcdt);
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+bool RCNum::operator>(const RCDataType &rcdt) const {
+  if (IsNull() || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) > 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) > 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (this->ToBString() > rcdt);
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+bool RCNum::operator<=(const RCDataType &rcdt) const {
+  if (IsNull() || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) <= 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) <= 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (this->ToBString() <= rcdt);
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+bool RCNum::operator>=(const RCDataType &rcdt) const {
+  if (null || rcdt.IsNull())
+    return false;
+  if (rcdt.GetValueType() == ValueTypeEnum::NUMERIC_TYPE)
+    return (compare((RCNum &)rcdt) >= 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return (compare((RCDateTime &)rcdt) >= 0);
+  if (rcdt.GetValueType() == ValueTypeEnum::STRING_TYPE)
+    return (this->ToBString() >= rcdt);
+  TIANMU_ERROR("Bad cast inside RCNum");
+  return false;
+}
+
+RCNum &RCNum::operator-=(const RCNum &rcn) {
+  DEBUG_ASSERT(!null);
+  if (rcn.IsNull() || rcn.IsNull())
+    return *this;
+  if (IsReal() || rcn.IsReal()) {
+    if (IsReal() && rcn.IsReal())
+      *(double *)&value_ -= *(double *)&rcn.value_;
+    else {
+      if (IsReal())
+        *this -= rcn.ToReal();
+      else
+        *this -= rcn.ToDecimal();
+    }
+  } else {
+    if (scale_ < rcn.scale_) {
+      value_ = ((int64_t)(value_ * PowOfTen(rcn.scale_ - scale_)) - rcn.value_);
+      scale_ = rcn.scale_;
+    } else {
+      value_ -= (int64_t)(rcn.value_ * PowOfTen(scale_ - rcn.scale_));
+    }
+  }
+  return *this;
+}
+
+RCNum &RCNum::operator+=(const RCNum &rcn) {
+  DEBUG_ASSERT(!null);
+  if (rcn.IsNull() || rcn.IsNull())
+    return *this;
+  if (IsReal() || rcn.IsReal()) {
+    if (IsReal() && rcn.IsReal())
+      *(double *)&value_ -= *(double *)&rcn.value_;
+    else {
+      if (IsReal())
+        *this += rcn.ToReal();
+      else
+        *this += rcn.ToDecimal();
+    }
+  } else {
+    if (scale_ < rcn.scale_) {
+      value_ = ((int64_t)(value_ * PowOfTen(rcn.scale_ - scale_)) + rcn.value_);
+      scale_ = rcn.scale_;
+    } else {
+      value_ += (int64_t)(rcn.value_ * PowOfTen(scale_ - rcn.scale_));
+    }
+  }
+  return *this;
+}
+
+RCNum &RCNum::operator*=(const RCNum &rcn) {
+  DEBUG_ASSERT(!null);
+  if (rcn.IsNull() || rcn.IsNull())
+    return *this;
+  if (IsReal() || rcn.IsReal()) {
+    if (IsReal() && rcn.IsReal())
+      *(double *)&value_ -= *(double *)&rcn.value_;
+    else {
+      if (IsReal())
+        *this /= rcn.ToReal();
+      else
+        *this /= rcn.ToDecimal();
+    }
+  } else {
+    value_ *= rcn.value_;
+    scale_ += rcn.scale_;
+  }
+  return *this;
+}
+
+void fcvt(char *buf_, double val_, int digits_, int *dec_, int *sign_) {
+  static int const fmtlen = 10;
+  char format[fmtlen + 1];
+  std::snprintf(format, fmtlen + 1, "%%.%df", digits_);
+  std::snprintf(buf_, digits_, format, val_);
+  int len(std::strlen(buf_));
+  (*sign_) = (buf_[0] == '-') ? 1 : 0;
+  (*sign_) && std::memmove(buf_, buf_ + 1, len);
+  char *pbuf(buf_);
+  ::strsep(&pbuf, ".,");
+  if (pbuf) {
+    (*dec_) = pbuf - buf_ - 1;
+    std::memmove(pbuf - 1, pbuf, std::strlen(pbuf) + 1);
+  }
+  return;
+}
+
+RCNum &RCNum::operator/=(const RCNum &rcn) {
+  DEBUG_ASSERT(!null);
+  if (rcn.IsNull() || rcn.IsNull())
+    return *this;
+  if (IsReal() || rcn.IsReal()) {
+    if (IsReal() && rcn.IsReal())
+      *(double *)&value_ -= *(double *)&rcn.value_;
+    else {
+      if (IsReal())
+        *this /= rcn.ToReal();
+      else
+        *this /= rcn.ToDecimal();
+    }
+  } else {
+    double tmv = ((double)(value_ / rcn.value_) / PowOfTen(scale_ - rcn.scale_));
+    int decimal = 0;
+    int sign;
+    static int const MAX_NUM_DIGITS = 21 + 1;
+    char buf[MAX_NUM_DIGITS - 1];
+    fcvt(buf, tmv, 18, &decimal, &sign);
+    buf[18] = 0;
+    ptrdiff_t lz = std::strlen(buf) - 1;
+    while (lz >= 0 && buf[lz--] == '0')
+      ;
+    buf[lz + 2] = 0;
+    value_ = std::strtoll(buf, NULL, 10) * (sign == 1 ? -1 : 1);
+    scale_ = (short)((lz + 2) - decimal);
+  }
+  return *this;
+}
+
+RCNum RCNum::operator-(const RCNum &rcn) const {
+  RCNum res(*this);
+  return res -= rcn;
+}
+
+RCNum RCNum::operator+(const RCNum &rcn) const {
+  RCNum res(*this);
+  return res += rcn;
+}
+
+RCNum RCNum::operator*(const RCNum &rcn) const {
+  RCNum res(*this);
+  return res *= rcn;
+}
+
+RCNum RCNum::operator/(const RCNum &rcn) const {
+  RCNum res(*this);
+  return res /= rcn;
+}
+
+uint RCNum::GetHashCode() const { return uint(GetIntPart() * 1040021); }
+
+int RCNum::compare(const RCNum &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  if (IsReal() || rcn.IsReal()) {
+    if (IsReal() && rcn.IsReal())
+      return (*(double *)&value_ > *(double *)&rcn.value_ ? 1
+                                                          : (*(double *)&value_ == *(double *)&rcn.value_ ? 0 : -1));
+    else {
+      if (IsReal())
+        return (*this > rcn.ToReal() ? 1 : (*this == rcn.ToReal() ? 0 : -1));
+      else
+        return (this->ToReal() > rcn ? 1 : (this->ToReal() == rcn ? 0 : -1));
+    }
+  } else {
+    if (scale_ != rcn.scale_) {
+      if (value_ < 0 && rcn.value_ >= 0)
+        return -1;
+      if (value_ >= 0 && rcn.value_ < 0)
+        return 1;
+      if (scale_ < rcn.scale_) {
+        int64_t power_of_ten = (int64_t)PowOfTen(rcn.scale_ - scale_);
+        int64_t tmpv = (int64_t)(rcn.value_ / power_of_ten);
+        if (value_ > tmpv)
+          return 1;
+        if (value_ < tmpv || rcn.value_ % power_of_ten > 0)
+          return -1;
+        if (rcn.value_ % power_of_ten < 0)
+          return 1;
+        return 0;
+      } else {
+        int64_t power_of_ten = (int64_t)PowOfTen(scale_ - rcn.scale_);
+        int64_t tmpv = (int64_t)(value_ / power_of_ten);
+        if (tmpv < rcn.value_)
+          return -1;
+        if (tmpv > rcn.value_ || value_ % power_of_ten > 0)
+          return 1;
+        if (value_ % power_of_ten < 0)
+          return -1;
+        return 0;
+      }
+    } else
+      return (value_ > rcn.value_ ? 1 : (value_ == rcn.value_ ? 0 : -1));
+  }
+}
+
+int RCNum::compare(const RCDateTime &rcdt) const {
+  int64_t tmp;
+  rcdt.ToInt64(tmp);
+  return int(GetIntPart() - tmp);
+}
+
+double RCNum::GetIntPartAsDouble() const {
+  if (is_double_) {
+    double integer;
+    modf(*(double *)&value_, &integer);
+    return integer;
+  } else {
+    return (double)(value_ / (int64_t)Uint64PowOfTen(scale_));
+  }
+}
+
+double RCNum::GetFractPart() const {
+  if (is_double_) {
+    double fract, integer;
+    fract = modf(*(double *)&value_, &integer);
+    return fract;
+  } else {
+    double tmpv = ((double)value_ / Uint64PowOfTen(scale_));
+    double fract, integer;
+    fract = modf(tmpv, &integer);
+    return fract;
+  }
+}
+
+short RCNum::GetDecStrLen() const {
+  if (IsNull())
+    return 0;
+  if (is_double_)
+    return 18;
+  short res = scale_;
+  int64_t tmpi = value_ / (int64_t)PowOfTen(scale_);
+  while (tmpi != 0) {
+    tmpi /= 10;
+    res++;
+  }
+  return res;
+}
+
+short RCNum::GetDecIntLen() const {
+  if (IsNull())
+    return 0;
+  short res = 0;
+  int64_t tmpi = 0;
+  if (is_double_)
+    tmpi = GetIntPart();
+  else {
+    tmpi = value_ / (int64_t)PowOfTen(scale_);
+  }
+  while (tmpi != 0) {
+    tmpi /= 10;
+    res++;
+  }
+  return res;
+}
+
+short RCNum::GetDecFractLen() const {
+  if (IsNull())
+    return 0;
+  return scale_;
+}
+
+void RCNum::Negate() {
+  if (IsNull())
+    return;
+  if (is_double_)
+    *(double *)&value_ = *(double *)&value_ * -1;
+  else
+    value_ *= -1;
+}
+
+}  // namespace types
+}  // namespace Tianmu
diff --git a/storage/tianmu/types/rc_num.h b/storage/tianmu/types/rc_num.h
new file mode 100644
index 000000000..4a16c94e6
--- /dev/null
+++ b/storage/tianmu/types/rc_num.h
@@ -0,0 +1,118 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+#ifndef TIANMU_TYPES_RC_NUM_H_
+#define TIANMU_TYPES_RC_NUM_H_
+#pragma once
+
+#include "types/rc_data_types.h"
+
+namespace Tianmu {
+namespace types {
+
+class BString;
+
+class RCNum : public ValueBasic<RCNum> {
+  friend class ValueParserForText;
+  friend class Engine;
+
+ public:
+  RCNum(common::CT attrt = common::CT::NUM);
+  RCNum(int64_t value, short scale = -1, bool dbl = false, common::CT attrt = common::CT::UNK);
+  RCNum(double value);
+  RCNum(const RCNum &);
+  ~RCNum();
+
+  RCNum &Assign(int64_t value, short scale = -1, bool dbl = false, common::CT attrt = common::CT::UNK);
+  RCNum &Assign(double value);
+
+  static common::ErrorCode Parse(const BString &rcs, RCNum &rcn, common::CT at = common::CT::UNK);
+  static common::ErrorCode ParseReal(const BString &, RCNum &, common::CT at);
+  static common::ErrorCode ParseNum(const BString &, RCNum &, short scale = -1);
+
+  RCNum &operator=(const RCNum &rcn);
+  RCNum &operator=(const RCDataType &rcdt) override;
+
+  common::CT Type() const override;
+
+  bool operator==(const RCDataType &rcdt) const override;
+  bool operator<(const RCDataType &rcdt) const override;
+  bool operator>(const RCDataType &rcdt) const override;
+  bool operator>=(const RCDataType &rcdt) const override;
+  bool operator<=(const RCDataType &rcdt) const override;
+  bool operator!=(const RCDataType &rcdt) const override;
+
+  RCNum &operator-=(const RCNum &rcn);
+  RCNum &operator+=(const RCNum &rcn);
+  RCNum &operator*=(const RCNum &rcn);
+  RCNum &operator/=(const RCNum &rcn);
+
+  RCNum operator-(const RCNum &rcn) const;
+  RCNum operator+(const RCNum &rcn) const;
+  RCNum operator*(const RCNum &rcn) const;
+  RCNum operator/(const RCNum &rcn) const;
+
+  bool IsDecimal(ushort scale) const;
+  bool IsReal() const { return is_double_; }
+  bool IsInt() const;
+
+  BString ToBString() const override;
+  RCNum ToDecimal(int scale = -1) const;
+  RCNum ToReal() const;
+  RCNum ToInt() const;
+
+  operator int64_t() const { return GetIntPart(); }
+  operator double() const;
+  operator float() const { return (float)(double)*this; }
+
+  short Scale() const { return scale_; }
+  int64_t ValueInt() const { return value_; }
+  char *GetDataBytesPointer() const override { return (char *)&value_; }
+  int64_t GetIntPart() const {
+    return is_double_ ? (int64_t)GetIntPartAsDouble() : value_ / (int64_t)Uint64PowOfTen(scale_);
+  }
+
+  int64_t GetValueInt64() const { return value_; }
+  double GetIntPartAsDouble() const;
+  double GetFractPart() const;
+
+  short GetDecStrLen() const;
+  short GetDecIntLen() const;
+  short GetDecFractLen() const;
+
+  uint GetHashCode() const override;
+  void Negate();
+
+ private:
+  int compare(const RCNum &rcn) const;
+  int compare(const RCDateTime &rcn) const;
+
+ private:
+  static constexpr int MAX_DEC_PRECISION = 18;
+  int64_t value_;
+  ushort scale_;  // means 'scale' actually
+  bool is_double_;
+  bool is_dot_;
+  common::CT attr_type_;
+
+ public:
+  const static ValueTypeEnum value_type = ValueTypeEnum::NUMERIC_TYPE;
+};
+
+}  // namespace types
+}  // namespace Tianmu
+
+#endif  // TIANMU_TYPES_RC_NUM_H_
diff --git a/storage/tianmu/types/rc_value_object.cpp b/storage/tianmu/types/rc_value_object.cpp
new file mode 100644
index 000000000..fa3498c38
--- /dev/null
+++ b/storage/tianmu/types/rc_value_object.cpp
@@ -0,0 +1,165 @@
+/* Copyright (c) 2022 StoneAtom, Inc. All rights reserved.
+   Use is subject to license terms
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335 USA
+*/
+
+#include "types/rc_num.h"
+
+namespace Tianmu {
+namespace types {
+
+RCValueObject::RCValueObject() {}
+
+RCValueObject::RCValueObject(const RCValueObject &rcvo) {
+  if (rcvo.value.get())
+    construct(*rcvo.value);
+}
+
+RCValueObject::RCValueObject(const RCDataType &rcdt) { construct(rcdt); }
+
+RCValueObject::~RCValueObject() {}
+
+RCValueObject &RCValueObject::operator=(const RCValueObject &rcvo) {
+  if (rcvo.value.get())
+    construct(*rcvo.value);
+  else
+    value.reset();
+  return *this;
+}
+
+inline void RCValueObject::construct(const RCDataType &rcdt) { value = rcdt.Clone(); }
+
+bool RCValueObject::compare(const RCValueObject &rcvo1, const RCValueObject &rcvo2, common::Operator op,
+                            char like_esc) {
+  if (rcvo1.IsNull() || rcvo2.IsNull())
+    return false;
+  else
+    return RCDataType::compare(*rcvo1.value, *rcvo2.value, op, like_esc);
+}
+
+bool RCValueObject::compare(const RCValueObject &rcvo, common::Operator op, char like_esc) const {
+  return compare(*this, rcvo, op, like_esc);
+}
+
+bool RCValueObject::operator==(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value == *rcvo.value;
+}
+
+bool RCValueObject::operator<(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value < *rcvo.value;
+}
+
+bool RCValueObject::operator>(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value > *rcvo.value;
+}
+
+bool RCValueObject::operator>=(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value >= *rcvo.value;
+}
+
+bool RCValueObject::operator<=(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value <= *rcvo.value;
+}
+
+bool RCValueObject::operator!=(const RCValueObject &rcvo) const {
+  if (IsNull() || rcvo.IsNull())
+    return false;
+  return *value != *rcvo.value;
+}
+
+bool RCValueObject::operator==(const RCDataType &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  return *value == rcn;
+}
+
+bool RCValueObject::operator<(const RCDataType &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  return *value < rcn;
+}
+
+bool RCValueObject::operator>(const RCDataType &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  return *value > rcn;
+}
+
+bool RCValueObject::operator>=(const RCDataType &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  return *value >= rcn;
+}
+
+bool RCValueObject::operator<=(const RCDataType &rcdt) const {
+  if (IsNull() || rcdt.IsNull())
+    return false;
+  return *value <= rcdt;
+}
+
+bool RCValueObject::operator!=(const RCDataType &rcn) const {
+  if (IsNull() || rcn.IsNull())
+    return false;
+  return *value != rcn;
+}
+
+bool RCValueObject::IsNull() const { return value.get() ? value->IsNull() : true; }
+
+RCDataType &RCValueObject::operator*() const { return value.get() ? *value.get() : RCNum::NullValue(); }
+
+RCValueObject::operator RCNum &() const {
+  if (IsNull())
+    return RCNum::NullValue();
+  if (GetValueType() == ValueTypeEnum::NUMERIC_TYPE || GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return static_cast<RCNum &>(*value);
+
+  TIANMU_ERROR("Bad cast in RCValueObject::RCNum&()");
+  return static_cast<RCNum &>(*value);
+}
+
+RCValueObject::operator RCDateTime &() const {
+  if (IsNull())
+    return RCDateTime::NullValue();
+  if (GetValueType() == ValueTypeEnum::DATE_TIME_TYPE)
+    return static_cast<RCDateTime &>(*value);
+
+  TIANMU_ERROR("Bad cast in RCValueObject::RCDateTime&()");
+  return static_cast<RCDateTime &>(*value);
+}
+
+BString RCValueObject::ToBString() const {
+  if (IsNull())
+    return BString();
+  return value->ToBString();
+}
+
+uint RCValueObject::GetHashCode() const {
+  if (IsNull())
+    return 0;
+  return value->GetHashCode();
+}
+
+}  // namespace types
+}  // namespace Tianmu