polardbxengine/plugin/polarx_rpc/executor/meta.h

465 lines
12 KiB
C++

#pragma once
#include <memory>
#include <vector>
#include <list>
#include "../global_defines.h"
#ifndef MYSQL8
#define MYSQL_SERVER
#endif
#include "sql/table.h"
#include "sql/field.h"
#include "sql/item.h"
#include "sql/my_decimal.h"
#include "sql/item_func.h"
#include "sql/item_cmpfunc.h"
#include "sql/item_sum.h"
#include "../coders/protocol_fwd.h"
#include "error.h"
#include "parse.h"
#include "log.h"
namespace rpc_executor {
using KeyPartMap = ::key_part_map;
using KeyRange = ::key_range;
using KeyFlag = enum ha_rkey_function;
using ExprItem = ::Item;
using ExprSendItem = ::Send_field;
using ProjectInfo = std::vector<std::pair<std::string, ExprItem*>>;
using AggrInfo = std::pair<std::string, Item_sum*>;
constexpr KeyPartMap EMPTY_MAP = 0;
class SearchKey;
class FieldRefItem : public ::Item_int {
public:
FieldRefItem(FieldIndex field_index)
: Item_int(static_cast<ulonglong>(field_index)),
field_index_(field_index) {}
uint32_t field_index() { return field_index_; }
private:
uint32_t field_index_;
};
class ExecKeyMeta {
public:
ExecKeyMeta() : init_(false), key_info_(nullptr), key_idx_(-1) {}
int init(KEY *key_info, uint32_t key_idx) {
int ret = HA_EXEC_SUCCESS;
if (!key_info || key_idx >= MAX_INDEXES) {
ret = HA_ERR_TABLE_CORRUPT;
log_exec_error("ExecKeyMeta init failed, ret: %d, key_ptr: %p, key_idx: %u",
ret, key_info, key_idx);
} else {
key_info_ = key_info;
key_idx_ = key_idx;
init_ = true;
}
return ret;
}
bool is_init() {
return init_;
}
const char *name() {
return key_info_->name;
}
uint32_t parts() {
return key_info_->user_defined_key_parts;
}
uint32_t length() {
return key_info_->key_length;
}
bool is_unique() {
return key_info_->flags & HA_NOSAME;
}
uint32_t index() {
return key_idx_;
}
KEY *key() {
return key_info_;
}
private:
bool init_;
KEY *key_info_;
uint32_t key_idx_;
};
/** structure holds the search field(s) */
class SearchKey {
public:
int init(ExecKeyMeta *exec_key,
const Polarx::ExecPlan::GetExpr &mysqlx_key) {
int ret = HA_EXEC_SUCCESS;
used_part_map_ = EMPTY_MAP;
used_length_ = 0;
using_full_key_ = false;
impossible_where_ = false;
ExecKeyMap expr;
if ((ret = expr.init(mysqlx_key))) {
log_exec_error("ExecKeyMap init failed, ret: %d", ret);
} else if ((ret = store_to_buffer(exec_key, expr))) {
log_exec_error("store key to table->record[0] failed, "
"ret: %d, part_map: %lu",
ret, used_part_map_);
}
return ret;
}
int store_to_buffer(ExecKeyMeta *exec_key,
const ExecKeyMap &expr);
uchar *key_buffer() const {
return key_buffer_.get();
}
uint64_t used_length() const {
return used_length_;
}
KeyPartMap used_part_map() const {
return used_part_map_;
}
bool using_full_key() const {
return using_full_key_;
}
bool is_impossible_where() const {
return impossible_where_;
}
private:
std::unique_ptr<uchar[]> key_buffer_;
KeyPartMap used_part_map_;
uint64_t used_length_;
bool using_full_key_;
bool impossible_where_;
};
struct RangeInfo {
const Polarx::ExecPlan::GetExpr *begin_key_;
const Polarx::ExecPlan::GetExpr *end_key_;
bool include_begin_;
bool include_end_;
};
class RangeSearchKey {
public:
int init(ExecKeyMeta *exec_key, const RangeInfo &range_info) {
int ret = HA_EXEC_SUCCESS;
// If range_info.begin_key_ is nullptr then it means scan from the first
// record of table. We do nothing and left begin_range_ nullptr. See
// handler::read_range_first
if (range_info.begin_key_) {
begin_range_.reset(new (std::nothrow) KeyRange());
if (!begin_range_) {
ret = HA_ERR_OUT_OF_MEM;
} else if ((ret = begin_key_.init(exec_key, *(range_info.begin_key_)))) {
log_exec_error("SearchKey of begin key init failed, "
"ret: %d, parts: %lu",
ret, begin_key_.used_part_map());
} else {
begin_range_->key = begin_key_.key_buffer();
begin_range_->length = begin_key_.used_length();
begin_range_->keypart_map = begin_key_.used_part_map();
begin_range_->flag = range_info.include_begin_ ? HA_READ_KEY_OR_NEXT :
HA_READ_AFTER_KEY;
}
}
// If range_info.end_key_ is nullptr then it means scan to the last
// record of table. We do nothing and left end_range_ nullptr. See
// handler::read_range_next
if (!ret && range_info.end_key_) {
end_range_.reset(new (std::nothrow) KeyRange());
if (!end_range_) {
ret = HA_ERR_OUT_OF_MEM;
} else if ((ret = end_key_.init(exec_key, *(range_info.end_key_)))) {
log_exec_error("SearchKey of end key init failed, "
"ret: %d, parts: %lu",
ret, end_key_.used_part_map());
} else {
end_range_->key = end_key_.key_buffer();
end_range_->length = end_key_.used_length();
end_range_->keypart_map = end_key_.used_part_map();
end_range_->flag = range_info.include_end_ ? HA_READ_AFTER_KEY :
HA_READ_BEFORE_KEY;
}
}
return ret;
}
const SearchKey &begin_key() const { return begin_key_; }
const SearchKey &end_key() const { return end_key_; }
KeyRange *begin_range() const { return begin_range_.get(); }
KeyRange *end_range() const { return end_range_.get(); }
private:
SearchKey begin_key_;
std::unique_ptr<KeyRange> begin_range_;
SearchKey end_key_;
std::unique_ptr<KeyRange> end_range_;
};
class ExecTable {
public:
ExecTable() : init_(false), table_(nullptr) {}
int init(TABLE *table) {
int ret = HA_EXEC_SUCCESS;
if (!table || !(table->file) || !(table->s)) {
ret = HA_ERR_TABLE_CORRUPT;
log_exec_error("ExecTable init failed, ret: %d, "
"table: %p, file: %p, share: %p",
ret, table, nullptr == table ? nullptr : table->file,
nullptr == table ? nullptr : table->s);
} else {
// table->use_all_columns();
table_ = table;
for (uint32_t i = 0; i < table->s->fields; ++i) {
Field *this_field = table->field[i];
name_field_map_.emplace(this_field->field_name, this_field);
}
for (uint32_t i = 0; i < table->s->keys ; ++i) {
KEY *key_info = &(table->key_info[i]);
std::unique_ptr<ExecKeyMeta> new_exec_key(new (std::nothrow) ExecKeyMeta());
if (!new_exec_key) {
ret = HA_ERR_OUT_OF_MEM;
log_exec_error("ExecTable init failed, new ExecKeyMeta failed, ret: %d",
ret);
} else if ((ret = new_exec_key->init(key_info, i))) {
log_exec_error("ExecTable init failed, ret: %d", ret);
} else {
name_key_map_.emplace(key_info->name, std::move(new_exec_key));
}
}
init_ = true;
}
return ret;
}
bool is_init() {
return init_;
}
int get_field(uint32_t index, Field *&field) {
int ret = HA_EXEC_SUCCESS;
if (index > table_->s->fields) {
ret = HA_ERR_TOO_MANY_FIELDS;
} else {
field = table_->field[index];
}
return ret;
}
int get_field(const std::string &name, Field *&field) {
int ret = HA_EXEC_SUCCESS;
auto iter = name_field_map_.find(name);
if (name_field_map_.end() == iter) {
ret = HA_ERR_INTERNAL_ERROR;
} else {
field = iter->second;
}
return ret;
}
int get_key(const std::string &name, ExecKeyMeta *&exec_key) {
int ret = HA_EXEC_SUCCESS;
auto iter = name_key_map_.find(name);
if (name_key_map_.end() == iter) {
ret = HA_ERR_INTERNAL_ERROR;
} else {
exec_key = iter->second.get();
}
return ret;
}
uint32_t field_count() {
return table_->s->fields;
}
Field **fields() {
return table_->field;
}
TABLE *table() {
return table_;
}
private:
bool init_;
TABLE *table_;
std::unordered_map<std::string, Field *> name_field_map_;
std::unordered_map<std::string, std::unique_ptr<ExecKeyMeta>> name_key_map_;
public:
TABLE_LIST tables;
};
class InternalDataSet {
public:
~InternalDataSet() {
project_exprs_ = nullptr;
item_free_list_.clear();
#ifdef MYSQL8
if (THR_MALLOC && (*THR_MALLOC)) {
(*THR_MALLOC)->ClearForReuse();
}
#else
MEM_ROOT **item_mem_root = my_thread_get_THR_MALLOC();
if (item_mem_root && (*item_mem_root)) {
free_root(*item_mem_root, MY_MARK_BLOCKS_FREE);
}
#endif
}
int init(int64_t batch_size, ExecTable *table) {
int ret = HA_EXEC_SUCCESS;
batch_size_ = batch_size;
found_ = false;
no_next_row_ = false;
table_ = table;
has_project_ = false;
project_exprs_ = nullptr;
is_aggr_ = false;
init_ = true;
return ret;
}
bool is_init() {
return init_;
}
void defer_delete_item(ExprItem *item) {
item_free_list_.emplace_back(item);
}
ExecTable *table() {
return table_;
}
void set_found() {
found_ = true;
}
void reset_found () {
found_ = false;
}
bool found() {
return found_;
}
void set_no_next_row() {
no_next_row_ = true;
}
bool no_next_row() {
return no_next_row_;
}
int get_field(uint32_t index, Field *&field) {
return table_->get_field(index, field);
}
int get_field(const std::string *name, Field *&field) {
return table_->get_field(*name, field);
}
// -------------------------------------------------------------------------
// Project functions
// -------------------------------------------------------------------------
int do_project() {
// Hope to optimize VERY SIMPLE queries like point select.
//
// CASE1: If no ProjectNode has applied, a do_project call will
// only set my_table->read_set (See @project_all). In this case, we should
// use get_all_fields to send metadata(See @executor::Protocol::write_metadata)
// and row(See @executor::Protocol::write_row).
//
// CASE 2: If some ProjectNode has applied, then project_exprs_ should have
// been ready and project_field should have been called to set specific bit
// in my_table->read_set. In this case, we should use get_project_exprs to
// send metadata and row.
//
// CASE 1 and CASE 2 is distinguished by has_project_x
return has_project_ ? HA_EXEC_SUCCESS : project_all();
}
int project_all() {
TABLE *my_table = table_->table();
my_table->read_set = &(my_table->s->all_set);
return HA_EXEC_SUCCESS;
}
void get_all_fields(Field **&fields, uint32_t &field_count) {
fields = table_->fields();
field_count = table_->field_count();
}
int project_field(Field *field) {
#ifdef MYSQL8
table_->table()->mark_column_used(field, MARK_COLUMNS_READ);
#else
table_->table()->mark_column_used(current_thd, field, MARK_COLUMNS_READ);
#endif
return HA_EXEC_SUCCESS;
}
int project(ProjectInfo *project_expr) {
int ret = HA_EXEC_SUCCESS;
project_exprs_ = project_expr;
has_project_ = true;
return ret;
}
ProjectInfo &get_project_exprs() {
return *project_exprs_;
}
bool has_project() { return has_project_; }
bool is_aggr() { return is_aggr_; }
void set_aggr(std::string &aggr_name, Item_sum *aggr_expr) {
is_aggr_ = true;
aggr_info_ = make_pair(aggr_name, aggr_expr);
}
AggrInfo &get_aggr_info() {
return aggr_info_;
}
private:
bool init_ = false;
int64_t batch_size_;
bool found_;
bool no_next_row_;
ExecTable *table_;
ProjectInfo *project_exprs_;
// TODO: use a more memory-friendly DS.
std::list<std::unique_ptr<ExprItem>> item_free_list_;
bool has_project_;
bool is_aggr_ = false;
AggrInfo aggr_info_;
};
} // namespace executor