提交 683851a0 编写于 作者: H hustjieke

feat: rm files after rebase leftover #1217

files deleted:
        storage/tianmu/core/rc_attr_typeinfo.h
        storage/tianmu/handler/tianmu_handler.cpp
        storage/tianmu/handler/tianmu_handler_com.cpp
        storage/tianmu/types/rc_data_types.cpp
        storage/tianmu/types/rc_num.cpp
        storage/tianmu/types/rc_num.h
        storage/tianmu/types/rc_value_object.cpp
上级 7d8a0327
/* 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_
/* 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
/* 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;
/* 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
/* 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
/* 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_
/* 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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册