#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(&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(¤t_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 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