polardbxengine/plugin/polarx_rpc/executor/handler_api.cc

543 lines
16 KiB
C++

#include "../global_defines.h"
#ifndef MYSQL8
#define MYSQL_SERVER
#endif
#include "sql/mysqld.h"
#include "sql/binlog.h"
#include "sql/handler.h"
#include "sql/field.h"
#include "sql/key.h"
#include "sql/transaction.h"
#include "sql/sql_class.h"
#include "sql/sql_base.h"
#include "sql/sql_lex.h"
#include "handler_api.h"
#include "log.h"
namespace rpc_executor {
namespace handler {
int check_table_init(ExecTable *exec_table) {
int ret = HA_EXEC_SUCCESS;
if (!exec_table) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("exec_table is null");
} else if (!exec_table->is_init()) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("exec_table not init");
}
return ret;
}
int check_key_init(ExecKeyMeta *exec_key) {
int ret = HA_EXEC_SUCCESS;
if (!exec_key) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("exec_key is null");
} else if (!exec_key->is_init()) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("exec_key not init");
}
return ret;
}
int check_meta_init(ExecTable *exec_table, ExecKeyMeta *exec_key) {
int ret = HA_EXEC_SUCCESS;
if ((ret = check_table_init(exec_table))) {
// table init error, should check in handler_open_table
} else if ((ret = check_key_init(exec_key))) {
// key init error, should check in handler_open_table
}
return ret;
}
} // namespace handler
THD *handler_create_thd(bool enable_binlog) {
THD *thd;
if (enable_binlog && !binlog_enabled()) {
fprintf(stderr,
" InnoDB_Memcached: MySQL server"
" binlog not enabled\n");
return (NULL);
}
thd = new (std::nothrow) THD;
if (!thd) {
return (NULL);
}
thd->get_protocol_classic()->init_net((Vio *)0);
thd->set_new_thread_id();
thd->thread_stack = reinterpret_cast<char *>(&thd);
thd->store_globals();
if (enable_binlog) {
thd->binlog_setup_trx_data();
/* set binlog_format to "ROW" */
thd->set_current_stmt_binlog_format_row();
}
return (thd);
}
void handler_close_thd(THD *thd) {
/* destructor will not free it, because net.vio is 0. */
thd->get_protocol_classic()->end_net();
thd->release_resources();
delete (thd);
}
void handler_thd_attach(THD *thd,
THD **original_thd) {
if (original_thd) {
*original_thd = current_thd;
}
thd->store_globals();
}
void handler_set_thd_source(THD *thd,
const char *host_or_ip,
const char *host,
const char *ip,
uint16_t port,
const char *user) {
thd->peer_port = port;
thd->m_main_security_ctx.set_host_or_ip_ptr(host_or_ip, strlen(host_or_ip));
thd->m_main_security_ctx.set_host_ptr(host, strlen(host));
thd->m_main_security_ctx.set_ip_ptr(ip, strlen(ip));
thd->m_main_security_ctx.set_user_ptr(user, strlen(user));
}
void handler_begin_read(THD *thd) {
THD *current_stack = thd;
thd->thread_stack = reinterpret_cast<char *>(&current_stack);
thd->store_globals();
THD_STAGE_INFO(thd, stage_starting);
thd->set_time();
thd->lex->sql_command = SQLCOM_SELECT;
}
void handler_end(THD *thd) {
thd->reset_query();
thd->set_command(COM_SLEEP);
thd->proc_info = 0;
thd->lex->sql_command = SQLCOM_END;
// thd->restore_globals();
}
int handler_open_table(THD *thd,
const char *db_name,
const char *table_name,
int lock_type,
ExecTable *&exec_table) {
int ret = HA_EXEC_SUCCESS;
std::unique_ptr<ExecTable> new_exec_table(new (std::nothrow) ExecTable());
if (!new_exec_table) {
ret = HA_ERR_OUT_OF_MEM;
log_exec_error("create new ExecTable failed, ret: %d", ret);
} else {
THD_STAGE_INFO(thd, stage_opening_tables);
TABLE_LIST &tables = new_exec_table->tables;
Open_table_context table_ctx(thd, 0);
thr_lock_type lock_mode = (lock_type <= HDL_READ) ? TL_READ : TL_WRITE;
#ifdef MYSQL8
tables = TABLE_LIST(db_name, strlen(db_name), table_name,
strlen(table_name), table_name, lock_mode);
#else
tables.init_one_table(db_name, strlen(db_name), table_name,
strlen(table_name), table_name, lock_mode);
#endif
/* For flush, we need to request exclusive mdl lock. */
if (lock_type == HDL_FLUSH) {
MDL_REQUEST_INIT(&tables.mdl_request, MDL_key::TABLE, db_name, table_name,
MDL_EXCLUSIVE, MDL_TRANSACTION);
} else {
MDL_REQUEST_INIT(&tables.mdl_request, MDL_key::TABLE, db_name, table_name,
(lock_mode > TL_READ) ? MDL_SHARED_WRITE : MDL_SHARED_READ,
MDL_TRANSACTION);
}
if ((ret = open_table(thd, &tables, &table_ctx))) {
log_exec_error("call open_table failed, "
"ret: %d, name: %s.%s, lock_type: %d",
ret, db_name, table_name, lock_type);
} else if ((ret = new_exec_table->init(tables.table))) {
log_exec_error("Executor table init failed, "
"ret: %d,name: %s.%s, lock_type: %d",
ret, db_name, table_name, lock_type);
} else {
#ifdef MYSQL8
// set the snapshot seq if needed
if (thd->variables.innodb_snapshot_gcn != MYSQL_GCN_NULL)
tables.table->snapshot.set_gcn(thd->variables.innodb_snapshot_gcn);
else if (thd->variables.innodb_current_snapshot_gcn) {
uint64_t gcn;
if (!ha_acquire_gcn(&gcn)) ++gcn;
tables.table->snapshot.set_gcn(gcn);
}
#endif
// In any other case this function fails,
// new_exec_table will be released by unique_ptr
exec_table = new_exec_table.release();
}
}
return ret;
}
#ifdef MYSQL8
long long thd_test_options(const MYSQL_THD thd, long long test_options) {
return thd->variables.option_bits & test_options;
}
#endif
int handler_close_table(THD *thd,
ExecTable *&exec_table,
int mode) {
int ret = HA_EXEC_SUCCESS;
TABLE *my_table = exec_table->table();
thr_lock_type lock_mode;
lock_mode = (mode & HDL_READ) ? TL_READ : TL_WRITE;
if (lock_mode == TL_WRITE) {
my_table->file->ha_release_auto_increment();
}
ret = trans_commit_stmt(thd);
THD_STAGE_INFO(thd, stage_closing_tables);
close_thread_tables(thd);
if (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
// auto commit and no begin
ret = trans_commit(thd);
thd->mdl_context.release_transactional_locks();
}
thd->lock = 0;
delete exec_table;
exec_table = nullptr;
return ret;
}
int handler_set_key_read_only(ExecTable *exec_table) {
assert(HA_EXEC_SUCCESS == handler::check_table_init(exec_table));
return exec_table->table()->file->extra(HA_EXTRA_KEYREAD);
}
int handler_set_no_key_read_only(ExecTable *exec_table) {
assert(HA_EXEC_SUCCESS == handler::check_table_init(exec_table));
return exec_table->table()->file->extra(HA_EXTRA_NO_KEYREAD);
}
// --------------------------- CRUD API ---------------------------------------
int handler_index_read_impl(THD *thd,
ExecTable *exec_table,
ExecKeyMeta *exec_key,
uchar *search_buf,
key_part_map part_map,
ha_rkey_function read_flags,
bool &found) {
TABLE *my_table = exec_table->table();
uint32_t idx_to_use = exec_key->index();
int ret = HA_EXEC_SUCCESS;
// handler get routine
::handler *handle = my_table->file;
if ((ret = handle->ha_index_init(idx_to_use, true /* sorted */))) {
log_exec_error("handler index init failed, ret: %d, idx_to_use: %d",
ret, idx_to_use);
} else {
if (0 == part_map) {
// TODO: See what kind of get we should offer and if we can optimize so
// many if else
ret = handle->ha_index_first(my_table->record[0]);
} else {
ret = handle->ha_index_read_map(my_table->record[0],
search_buf, part_map, read_flags);
}
if (ret == HA_ERR_KEY_NOT_FOUND || ret == HA_ERR_END_OF_FILE) {
ret = HA_EXEC_SUCCESS;
} else if (ret) {
log_exec_error("handler index read failed, ret: %d, flags: %d",
ret, read_flags);
} else {
found = true;
}
}
return ret;
}
int handler_get(THD *thd,
ExecTable *exec_table,
ExecKeyMeta *exec_key,
const SearchKey &search_key,
bool &found) {
// exec_table and exec_key must be generated from handler_open_table and
// should have been checked there.
assert(HA_EXEC_SUCCESS == handler::check_meta_init(exec_table, exec_key));
int ret = HA_EXEC_SUCCESS;
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("THD is NULL");
} else {
ret = handler_index_read_impl(thd, exec_table, exec_key,
search_key.key_buffer(),
search_key.used_part_map(),
HA_READ_KEY_EXACT, found);
}
return ret;
}
int handler_index_first(THD *thd,
ExecTable *exec_table,
ExecKeyMeta *exec_key,
bool &found) {
// exec_table and exec_key must be generated from handler_open_table and
// should have been checked there.
assert(HA_EXEC_SUCCESS == handler::check_meta_init(exec_table, exec_key));
TABLE *my_table = exec_table->table();
uint32_t idx_to_use = exec_key->index();
int ret = HA_EXEC_SUCCESS;
// handler get routine
::handler *handle = my_table->file;
if ((ret = handle->ha_index_init(idx_to_use, true /* sorted */))) {
log_exec_error("handler index init failed, ret: %d, idx_to_use: %d",
ret, idx_to_use);
} else if ((ret = handle->ha_index_first(my_table->record[0]))) {
if (ret == HA_ERR_KEY_NOT_FOUND || ret == HA_ERR_END_OF_FILE) {
ret = HA_EXEC_SUCCESS;
found = false;
} else if (ret) {
log_exec_error("handler index read failed, ret: %d", ret);
}
} else {
found = true;
}
return ret;
}
int handler_index_next(THD *thd,
ExecTable *exec_table,
bool &found) {
assert(HA_EXEC_SUCCESS == handler::check_table_init(exec_table));
int ret = HA_EXEC_SUCCESS;
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("THD is NULL");
} else {
TABLE *my_table = exec_table->table();
if ((ret = my_table->file->ha_index_next(my_table->record[0]))) {
if (HA_ERR_END_OF_FILE == ret || HA_ERR_KEY_NOT_FOUND == ret) {
ret = HA_EXEC_SUCCESS;
found = false;
}
// else this is an error
} else {
found = true;
}
}
return ret;
}
int handler_next_same(THD *thd,
ExecTable *exec_table,
const SearchKey &search_key,
bool &found) {
assert(HA_EXEC_SUCCESS == handler::check_table_init(exec_table));
int ret = HA_EXEC_SUCCESS;
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("THD is NULL");
} else {
TABLE *my_table = exec_table->table();
if ((ret = my_table->file->ha_index_next_same(my_table->record[0],
search_key.key_buffer(),
search_key.used_length()))) {
if (HA_ERR_END_OF_FILE == ret || HA_ERR_KEY_NOT_FOUND == ret) {
ret = HA_EXEC_SUCCESS;
found = false;
}
// else this is an error
} else {
found = true;
}
}
return ret;
}
int handler_index_end(THD *thd, ExecTable *exec_table) {
int ret = HA_EXEC_SUCCESS;
TABLE *my_table = exec_table->table();
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
} else {
::handler *handle = my_table->file;
if ((ret = handle->ha_index_or_rnd_end())) {
log_exec_error("handler end failed, ret: %d", ret);
}
}
return HA_EXEC_SUCCESS;
}
int handler_seek(THD *thd,
ExecTable *exec_table,
ExecKeyMeta *exec_key,
const RangeSearchKey &range_key,
bool &found) {
// exec_table and exec_key must be generated from handler_open_table and
// should have been checked there.
assert(HA_EXEC_SUCCESS == handler::check_meta_init(exec_table, exec_key));
int ret = HA_EXEC_SUCCESS;
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("THD is NULL");
} else {
TABLE *my_table = exec_table->table();
::handler *handle = my_table->file;
uint32_t idx_to_use = exec_key->index();
if ((ret = handle->ha_index_init(idx_to_use, false))) {
log_exec_error("handler index init failed, ret: %d, idx_to_use: %d",
ret, idx_to_use);
} else {
#ifdef MYSQL8
ret = handle->ha_read_range_first(range_key.begin_range(),
range_key.end_range(),
false, false);
#else
ret = handle->read_range_first(range_key.begin_range(),
range_key.end_range(),
false, false);
#endif
if (ret == HA_ERR_KEY_NOT_FOUND || ret == HA_ERR_END_OF_FILE) {
ret = HA_EXEC_SUCCESS;
} else if (ret) {
log_exec_error("handler range first failed, ret: %d, idx_to_use: %d",
ret, idx_to_use);
} else {
found = true;
}
}
}
return ret;
}
int handler_range_next(THD *thd, ExecTable *exec_table, bool &found) {
assert(HA_EXEC_SUCCESS == handler::check_table_init(exec_table));
int ret = HA_EXEC_SUCCESS;
if (!thd) {
ret = HA_ERR_INTERNAL_ERROR;
log_exec_error("THD is NULL");
} else {
TABLE *my_table = exec_table->table();
::handler *handle = my_table->file;
#ifdef MYSQL8
ret = handle->ha_read_range_next();
#else
ret = handle->read_range_next();
#endif
if (ret) {
if (HA_ERR_END_OF_FILE == ret || HA_ERR_KEY_NOT_FOUND == ret) {
ret = HA_EXEC_SUCCESS;
found = false;
}
// else this is an error
} else {
found = true;
}
}
return ret;
}
/*
int handler_insert_rec(TABLE *my_table, field_arg_t *store_args) {
uchar *insert_buf;
KEY *key_info = &(my_table->key_info[0]);
handler *handle = my_table->file;
empty_record(my_table);
assert(my_table->reginfo.lock_type > TL_READ &&
my_table->reginfo.lock_type <= TL_WRITE_ONLY);
insert_buf = my_table->record[0];
memset(insert_buf, 0, my_table->s->null_bytes);
assert(store_args->num_arg == key_info->user_defined_key_parts);
for (unsigned int i = 0; i < key_info->user_defined_key_parts; i++) {
Field *fld;
fld = my_table->field[i];
if (store_args->len[i]) {
fld->store(store_args->value[i], store_args->len[i], &my_charset_bin);
fld->set_notnull();
} else {
fld->set_null();
}
}
return (handle->ha_write_row((uchar *)my_table->record[0]));
}
int handler_update_rec(TABLE *my_table, field_arg_t *store_args) {
uchar *buf = my_table->record[0];
handler *handle = my_table->file;
KEY *key_info = &my_table->key_info[0];
store_record(my_table, record[1]);
for (unsigned int i = 0; i < key_info->user_defined_key_parts; i++) {
Field *fld;
fld = my_table->field[i];
fld->store(store_args->value[i], store_args->len[i], &my_charset_bin);
fld->set_notnull();
}
return (handle->ha_update_row(my_table->record[1], buf));
}
int handler_delete_rec(TABLE *my_table) {
return (my_table->file->ha_delete_row(my_table->record[0]));
}
*/
MYSQL_LOCK *handler_lock_table(THD *thd,
ExecTable *exec_table,
thr_lock_type lock_mode) {
TABLE *my_table = exec_table->table();
my_table->reginfo.lock_type = lock_mode;
thd->lock = mysql_lock_tables(thd, &my_table, 1, 0);
// Set this to make Item_field::set_field work
my_table->pos_in_table_list = nullptr;
return thd->lock;
}
} // namespace executor