/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Copyright(C) 2010 Tetsuro IKEDA Copyright(C) 2010-2013 Kentoku SHIBA Copyright(C) 2011-2017 Kouhei Sutou Copyright(C) 2013 Kenji Maruyama This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #include "mrn_mysql.h" #include "mrn_mysql_compat.h" #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation #endif #include #include #include #include #include #include #include #ifdef MRN_HAVE_BINLOG_H # include #endif #ifdef MRN_HAVE_SQL_OPTIMIZER_H # include #endif #include #include #include #include #include #ifdef WIN32 # include # include # define MRN_TABLE_SHARE_LOCK_SHARE_PROC "?key_TABLE_SHARE_LOCK_share@@3IA" # define MRN_TABLE_SHARE_LOCK_HA_DATA_PROC "?key_TABLE_SHARE_LOCK_ha_data@@3IA" # ifdef _WIN64 # define MRN_BINLOG_FILTER_PROC "?binlog_filter@@3PEAVRpl_filter@@EA" # define MRN_MY_TZ_UTC_PROC "?my_tz_UTC@@3PEAVTime_zone@@EA" # else # define MRN_BINLOG_FILTER_PROC "?binlog_filter@@3PAVRpl_filter@@A" # define MRN_MY_TZ_UTC_PROC "?my_tz_UTC@@3PAVTime_zone@@A" # endif #else # include # include #endif #include "mrn_err.h" #include "mrn_table.hpp" #include #include "ha_mroonga.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MRN_SUPPORT_FOREIGN_KEYS # include #endif #ifdef MRN_SUPPORT_CUSTOM_OPTIONS # include #endif // for debug #define MRN_CLASS_NAME "ha_mroonga" #define MRN_SHORT_TEXT_SIZE (1 << 12) // 4Kbytes #define MRN_TEXT_SIZE (1 << 16) // 64Kbytes #define MRN_LONG_TEXT_SIZE (1 << 31) // 2Gbytes #ifdef MRN_HAVE_TDC_LOCK_TABLE_SHARE # ifdef MRN_TABLE_SHARE_TDC_IS_POINTER # define mrn_open_mutex(share) &((share)->tdc->LOCK_table_share) # else # define mrn_open_mutex(share) &((share)->tdc.LOCK_table_share) # endif # define mrn_open_mutex_lock(share) do { \ TABLE_SHARE *share_ = share; \ if (share_ && share_->tmp_table == NO_TMP_TABLE) { \ mysql_mutex_lock(mrn_open_mutex(share_)); \ } \ } while (0) # define mrn_open_mutex_unlock(share) do { \ TABLE_SHARE *share_ = share; \ if (share_ && share_->tmp_table == NO_TMP_TABLE) { \ mysql_mutex_unlock(mrn_open_mutex(share_)); \ } \ } while (0) #else # ifdef DBUG_OFF # ifndef _WIN32 extern mysql_mutex_t LOCK_open; # endif # endif static mysql_mutex_t *mrn_LOCK_open; # define mrn_open_mutex_lock(share) mysql_mutex_lock(mrn_LOCK_open) # define mrn_open_mutex_unlock(share) mysql_mutex_unlock(mrn_LOCK_open) #endif #if MYSQL_VERSION_ID >= 50600 # define MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK #endif #ifdef MRN_MARIADB_P # if MYSQL_VERSION_ID >= 100200 # define MRN_ORDER_IS_ASC(order) ((order)->direction == ORDER::ORDER_ASC) # else # define MRN_ORDER_IS_ASC(order) ((order)->asc) # endif #else # if MYSQL_VERSION_ID >= 50603 # define MRN_ORDER_IS_ASC(order) ((order)->direction == ORDER::ORDER_ASC) # else # define MRN_ORDER_IS_ASC(order) ((order)->asc) # endif #endif #define MRN_STRINGIFY(macro_or_string) MRN_STRINGIFY_ARG(macro_or_string) #define MRN_STRINGIFY_ARG(contents) #contents #define MRN_PLUGIN_NAME mroonga #define MRN_PLUGIN_NAME_STRING "Mroonga" #define MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "Mroonga" #ifdef MRN_MARIADB_P # define st_mysql_plugin st_maria_plugin # define mrn_declare_plugin(NAME) maria_declare_plugin(NAME) # define mrn_declare_plugin_end maria_declare_plugin_end # define MRN_PLUGIN_LAST_VALUES MRN_VERSION, MariaDB_PLUGIN_MATURITY_STABLE #else # define mrn_declare_plugin(NAME) mysql_declare_plugin(NAME) # define mrn_declare_plugin_end mysql_declare_plugin_end # define MRN_PLUGIN_LAST_VALUES NULL, 0 #endif #if MYSQL_VERSION_ID >= 100007 && defined(MRN_MARIADB_P) # define MRN_THD_GET_AUTOINC(thd, off, inc) thd_get_autoinc(thd, off, inc) # define MRN_GET_ERR_MSG(code) my_get_err_msg(code) #else # define MRN_THD_GET_AUTOINC(thd, off, inc) \ { \ *(off) = thd->variables.auto_increment_offset; \ *(inc) = thd->variables.auto_increment_increment; \ } # define MRN_GET_ERR_MSG(code) ER(code) #endif #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define MRN_LEX_GET_TABLE_LIST(lex) (lex)->select_lex->table_list.first #else # define MRN_LEX_GET_TABLE_LIST(lex) (lex)->first_select_lex()->table_list.first #endif #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define MRN_KEYTYPE_FOREIGN KEYTYPE_FOREIGN #else # define MRN_KEYTYPE_FOREIGN Key::FOREIGN_KEY #endif #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define mrn_calculate_key_len(table, key_index, buffer, keypart_map) \ calculate_key_len(table, key_index, keypart_map) #else # define mrn_calculate_key_len(table, key_index, buffer, keypart_map) \ calculate_key_len(table, key_index, buffer, keypart_map) #endif #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define MRN_TABLE_LIST_GET_DERIVED(table_list) NULL #else # define MRN_TABLE_LIST_GET_DERIVED(table_list) (table_list)->derived #endif #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define MRN_GEOMETRY_FREE(geometry) #else # define MRN_GEOMETRY_FREE(geometry) delete (geometry) #endif Rpl_filter *mrn_binlog_filter; Time_zone *mrn_my_tz_UTC; #ifdef MRN_HAVE_TABLE_DEF_CACHE HASH *mrn_table_def_cache; #endif PSI_memory_key mrn_memory_key; #ifdef MRN_HAVE_PSI_MEMORY_KEY static PSI_memory_info mrn_all_memory_keys[]= { {&mrn_memory_key, "Mroonga", 0} }; #endif static const char *INDEX_COLUMN_NAME = "index"; static const char *MRN_PLUGIN_AUTHOR = "The Mroonga project"; #ifdef __cplusplus extern "C" { #endif #ifdef HAVE_PSI_INTERFACE # ifdef WIN32 # ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE PSI_mutex_key *mrn_table_share_lock_share; # endif PSI_mutex_key *mrn_table_share_lock_ha_data; # endif static PSI_mutex_key mrn_open_tables_mutex_key; static PSI_mutex_key mrn_long_term_share_mutex_key; static PSI_mutex_key mrn_allocated_thds_mutex_key; PSI_mutex_key mrn_share_mutex_key; PSI_mutex_key mrn_long_term_share_auto_inc_mutex_key; static PSI_mutex_key mrn_log_mutex_key; static PSI_mutex_key mrn_query_log_mutex_key; static PSI_mutex_key mrn_db_manager_mutex_key; static PSI_mutex_key mrn_context_pool_mutex_key; static PSI_mutex_key mrn_operations_mutex_key; # if (!defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 80002) # define MRN_MUTEXT_INFO_ENTRY(key, name, flags, volatility) \ {key, name, flags, volatility} # else # define MRN_MUTEXT_INFO_ENTRY(key, name, flags, volatility) \ {key, name, flags} # endif static PSI_mutex_info mrn_mutexes[] = { MRN_MUTEXT_INFO_ENTRY(&mrn_open_tables_mutex_key, "mrn::open_tables", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_long_term_share_mutex_key, "mrn::long_term_share", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_allocated_thds_mutex_key, "mrn::allocated_thds", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_share_mutex_key, "mrn::share", 0, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_long_term_share_auto_inc_mutex_key, "mrn::long_term_share::auto_inc", 0, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_log_mutex_key, "mrn::log", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_query_log_mutex_key, "mrn::query_log", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_db_manager_mutex_key, "mrn::DatabaseManager", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_context_pool_mutex_key, "mrn::ContextPool", PSI_FLAG_GLOBAL, 0), MRN_MUTEXT_INFO_ENTRY(&mrn_operations_mutex_key, "mrn::Operations", PSI_FLAG_GLOBAL, 0) }; #endif /* global variables */ handlerton *mrn_hton_ptr; HASH mrn_open_tables; mysql_mutex_t mrn_open_tables_mutex; HASH mrn_long_term_share; mysql_mutex_t mrn_long_term_share_mutex; HASH mrn_allocated_thds; mysql_mutex_t mrn_allocated_thds_mutex; /* internal variables */ static grn_ctx mrn_ctx; static mysql_mutex_t mrn_log_mutex; static mysql_mutex_t mrn_query_log_mutex; static grn_obj *mrn_db; static grn_ctx mrn_db_manager_ctx; static mysql_mutex_t mrn_db_manager_mutex; mrn::DatabaseManager *mrn_db_manager = NULL; static mysql_mutex_t mrn_context_pool_mutex; mrn::ContextPool *mrn_context_pool = NULL; static mysql_mutex_t mrn_operations_mutex; #ifdef WIN32 static inline double round(double x) { return (floor(x + 0.5)); } #endif static void mrn_init_encoding_map() { mrn::encoding::init(); } static int mrn_change_encoding(grn_ctx *ctx, const CHARSET_INFO *charset) { return mrn::encoding::set(ctx, charset); } #if defined DBUG_TRACE && !defined(_lint) static const char *mrn_inspect_thr_lock_type(enum thr_lock_type lock_type) { const char *inspected = ""; switch (lock_type) { case TL_IGNORE: inspected = "TL_IGNORE"; break; case TL_UNLOCK: inspected = "TL_UNLOCK"; break; case TL_READ_DEFAULT: inspected = "TL_READ_DEFAULT"; break; case TL_READ: inspected = "TL_READ"; break; case TL_READ_WITH_SHARED_LOCKS: inspected = "TL_READ_WITH_SHARED_LOCKS"; break; case TL_READ_HIGH_PRIORITY: inspected = "TL_READ_HIGH_PRIORITY"; break; case TL_READ_NO_INSERT: inspected = "TL_READ_NO_INSERT"; break; case TL_READ_SKIP_LOCKED: inspected = "TL_READ_SKIP_LOCKED"; break; case TL_WRITE_ALLOW_WRITE: inspected = "TL_WRITE_ALLOW_WRITE"; break; #ifdef MRN_HAVE_TL_WRITE_CONCURRENT_DEFAULT case TL_WRITE_CONCURRENT_DEFAULT: inspected = "TL_WRITE_CONCURRENT_DEFAULT"; break; #endif case TL_WRITE_CONCURRENT_INSERT: inspected = "TL_WRITE_CONCURRENT_INSERT"; break; #ifdef MRN_HAVE_TL_WRITE_DELAYED case TL_WRITE_DELAYED: inspected = "TL_WRITE_DELAYED"; break; #endif case TL_WRITE_DEFAULT: inspected = "TL_WRITE_DEFAULT"; break; case TL_WRITE_LOW_PRIORITY: inspected = "TL_WRITE_LOW_PRIORITY"; break; case TL_WRITE: inspected = "TL_WRITE"; break; case TL_WRITE_SKIP_LOCKED: inspected = "TL_WRITE_SKIP_LOCKED"; break; case TL_WRITE_ONLY: inspected = "TL_WRITE_ONLY"; break; } return inspected; } static const char *mrn_inspect_extra_function(enum ha_extra_function operation) { const char *inspected = ""; switch (operation) { case HA_EXTRA_NORMAL: inspected = "HA_EXTRA_NORMAL"; break; case HA_EXTRA_QUICK: inspected = "HA_EXTRA_QUICK"; break; case HA_EXTRA_NOT_USED: inspected = "HA_EXTRA_NOT_USED"; break; case HA_EXTRA_CACHE: inspected = "HA_EXTRA_CACHE"; break; case HA_EXTRA_NO_CACHE: inspected = "HA_EXTRA_NO_CACHE"; break; case HA_EXTRA_NO_READCHECK: inspected = "HA_EXTRA_NO_READCHECK"; break; case HA_EXTRA_READCHECK: inspected = "HA_EXTRA_READCHECK"; break; case HA_EXTRA_KEYREAD: inspected = "HA_EXTRA_KEYREAD"; break; case HA_EXTRA_NO_KEYREAD: inspected = "HA_EXTRA_NO_KEYREAD"; break; case HA_EXTRA_NO_USER_CHANGE: inspected = "HA_EXTRA_NO_USER_CHANGE"; break; case HA_EXTRA_KEY_CACHE: inspected = "HA_EXTRA_KEY_CACHE"; break; case HA_EXTRA_NO_KEY_CACHE: inspected = "HA_EXTRA_NO_KEY_CACHE"; break; case HA_EXTRA_WAIT_LOCK: inspected = "HA_EXTRA_WAIT_LOCK"; break; case HA_EXTRA_NO_WAIT_LOCK: inspected = "HA_EXTRA_NO_WAIT_LOCK"; break; case HA_EXTRA_WRITE_CACHE: inspected = "HA_EXTRA_WRITE_CACHE"; break; case HA_EXTRA_FLUSH_CACHE: inspected = "HA_EXTRA_FLUSH_CACHE"; break; case HA_EXTRA_NO_KEYS: inspected = "HA_EXTRA_NO_KEYS"; break; case HA_EXTRA_KEYREAD_CHANGE_POS: inspected = "HA_EXTRA_KEYREAD_CHANGE_POS"; break; case HA_EXTRA_REMEMBER_POS: inspected = "HA_EXTRA_REMEMBER_POS"; break; case HA_EXTRA_RESTORE_POS: inspected = "HA_EXTRA_RESTORE_POS"; break; case HA_EXTRA_REINIT_CACHE: inspected = "HA_EXTRA_REINIT_CACHE"; break; case HA_EXTRA_FORCE_REOPEN: inspected = "HA_EXTRA_FORCE_REOPEN"; break; case HA_EXTRA_FLUSH: inspected = "HA_EXTRA_FLUSH"; break; case HA_EXTRA_NO_ROWS: inspected = "HA_EXTRA_NO_ROWS"; break; case HA_EXTRA_RESET_STATE: inspected = "HA_EXTRA_RESET_STATE"; break; case HA_EXTRA_IGNORE_DUP_KEY: inspected = "HA_EXTRA_IGNORE_DUP_KEY"; break; case HA_EXTRA_NO_IGNORE_DUP_KEY: inspected = "HA_EXTRA_NO_IGNORE_DUP_KEY"; break; case HA_EXTRA_PREPARE_FOR_DROP: inspected = "HA_EXTRA_PREPARE_FOR_DROP"; break; case HA_EXTRA_PREPARE_FOR_ALTER_TABLE: inspected = "HA_EXTRA_PREPARE_FOR_ALTER_TABLE"; break; case HA_EXTRA_PREPARE_FOR_UPDATE: inspected = "HA_EXTRA_PREPARE_FOR_UPDATE"; break; case HA_EXTRA_PRELOAD_BUFFER_SIZE: inspected = "HA_EXTRA_PRELOAD_BUFFER_SIZE"; break; case HA_EXTRA_CHANGE_KEY_TO_UNIQUE: inspected = "HA_EXTRA_CHANGE_KEY_TO_UNIQUE"; break; case HA_EXTRA_CHANGE_KEY_TO_DUP: inspected = "HA_EXTRA_CHANGE_KEY_TO_DUP"; break; case HA_EXTRA_KEYREAD_PRESERVE_FIELDS: inspected = "HA_EXTRA_KEYREAD_PRESERVE_FIELDS"; break; case HA_EXTRA_MMAP: inspected = "HA_EXTRA_MMAP"; break; case HA_EXTRA_IGNORE_NO_KEY: inspected = "HA_EXTRA_IGNORE_NO_KEY"; break; case HA_EXTRA_NO_IGNORE_NO_KEY: inspected = "HA_EXTRA_NO_IGNORE_NO_KEY"; break; case HA_EXTRA_MARK_AS_LOG_TABLE: inspected = "HA_EXTRA_MARK_AS_LOG_TABLE"; break; case HA_EXTRA_WRITE_CAN_REPLACE: inspected = "HA_EXTRA_WRITE_CAN_REPLACE"; break; case HA_EXTRA_WRITE_CANNOT_REPLACE: inspected = "HA_EXTRA_WRITE_CANNOT_REPLACE"; break; case HA_EXTRA_DELETE_CANNOT_BATCH: inspected = "HA_EXTRA_DELETE_CANNOT_BATCH"; break; case HA_EXTRA_UPDATE_CANNOT_BATCH: inspected = "HA_EXTRA_UPDATE_CANNOT_BATCH"; break; case HA_EXTRA_INSERT_WITH_UPDATE: inspected = "HA_EXTRA_INSERT_WITH_UPDATE"; break; case HA_EXTRA_PREPARE_FOR_RENAME: inspected = "HA_EXTRA_PREPARE_FOR_RENAME"; break; case HA_EXTRA_ADD_CHILDREN_LIST: inspected = "HA_EXTRA_ADD_CHILDREN_LIST"; break; case HA_EXTRA_ATTACH_CHILDREN: inspected = "HA_EXTRA_ATTACH_CHILDREN"; break; case HA_EXTRA_IS_ATTACHED_CHILDREN: inspected = "HA_EXTRA_IS_ATTACHED_CHILDREN"; break; case HA_EXTRA_DETACH_CHILDREN: inspected = "HA_EXTRA_DETACH_CHILDREN"; break; case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: inspected = "HA_EXTRA_STARTING_ORDERED_INDEX_SCAN"; break; case HA_EXTRA_BEGIN_ALTER_COPY: inspected = "HA_EXTRA_BEGIN_ALTER_COPY"; break; case HA_EXTRA_END_ALTER_COPY: inspected = "HA_EXTRA_END_ALTER_COPY"; break; #ifdef MRN_HAVE_HA_EXTRA_EXPORT case HA_EXTRA_EXPORT: inspected = "HA_EXTRA_EXPORT"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_SECONDARY_SORT_ROWID case HA_EXTRA_SECONDARY_SORT_ROWID: inspected = "HA_EXTRA_SECONDARY_SORT_ROWID"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_DETACH_CHILD case HA_EXTRA_DETACH_CHILD: inspected = "HA_EXTRA_DETACH_CHILD"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_PREPARE_FOR_FORCED_CLOSE case HA_EXTRA_PREPARE_FOR_FORCED_CLOSE: inspected = "HA_EXTRA_PREPARE_FOR_FORCED_CLOSE"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_SKIP_SERIALIZABLE_DD_VIEW case HA_EXTRA_SKIP_SERIALIZABLE_DD_VIEW: inspected = "HA_EXTRA_SKIP_SERIALIZABLE_DD_VIEW"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_BEGIN_ALTER_COPY case HA_EXTRA_BEGIN_ALTER_COPY: inspected = "HA_EXTRA_BEGIN_ALTER_COPY"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_END_ALTER_COPY case HA_EXTRA_END_ALTER_COPY: inspected = "HA_EXTRA_END_ALTER_COPY"; break; #endif #ifdef MRN_HAVE_HA_EXTRA_NO_AUTOINC_LOCKING case HA_EXTRA_NO_AUTOINC_LOCKING: inspected = "HA_EXTRA_NO_AUTOINC_LOCKING"; break; #endif } return inspected; } #endif static uchar *mrn_open_tables_get_key(const uchar *record, size_t *length, my_bool not_used __attribute__ ((unused))) { MRN_DBUG_ENTER_FUNCTION(); MRN_SHARE *share = reinterpret_cast(const_cast(record)); *length = share->table_name_length; DBUG_RETURN(reinterpret_cast(share->table_name)); } static uchar *mrn_long_term_share_get_key(const uchar *record, size_t *length, my_bool not_used __attribute__ ((unused))) { MRN_DBUG_ENTER_FUNCTION(); MRN_LONG_TERM_SHARE *long_term_share = reinterpret_cast(const_cast(record)); *length = long_term_share->table_name_length; DBUG_RETURN(reinterpret_cast(long_term_share->table_name)); } /* status */ static long mrn_count_skip = 0; static long mrn_fast_order_limit = 0; /* logging */ static char *mrn_log_file_path = NULL; static FILE *mrn_log_file = NULL; static bool mrn_log_file_opened = false; static grn_log_level mrn_log_level_default = GRN_LOG_DEFAULT_LEVEL; static ulong mrn_log_level = mrn_log_level_default; static char *mrn_query_log_file_path = NULL; char *mrn_default_tokenizer = NULL; char *mrn_default_wrapper_engine = NULL; static int mrn_lock_timeout = grn_get_lock_timeout(); static char *mrn_libgroonga_version = const_cast(grn_get_version()); static char *mrn_version = const_cast(MRN_VERSION); static char *mrn_vector_column_delimiter = NULL; static mrn_bool mrn_libgroonga_support_zlib = false; static mrn_bool mrn_libgroonga_support_lz4 = false; static mrn_bool mrn_libgroonga_support_zstd = false; static mrn_bool mrn_enable_operations_recording = true; #ifdef MRN_SUPPORT_THDVAR_SET static const char *mrn_boolean_mode_sytnax_flag_names[] = { "DEFAULT", "SYNTAX_QUERY", "SYNTAX_SCRIPT", "ALLOW_COLUMN", "ALLOW_UPDATE", "ALLOW_LEADING_NOT", NullS }; static TYPELIB mrn_boolean_mode_syntax_flags_typelib = { array_elements(mrn_boolean_mode_sytnax_flag_names) - 1, "", mrn_boolean_mode_sytnax_flag_names, NULL }; #endif #ifdef MRN_GROONGA_EMBEDDED static mrn_bool mrn_libgroonga_embedded = true; #else static mrn_bool mrn_libgroonga_embedded = false; #endif static mrn::variables::ActionOnError mrn_action_on_fulltext_query_error_default = mrn::variables::ACTION_ON_ERROR_ERROR_AND_LOG; static void mrn_logger_log(grn_ctx *ctx, grn_log_level level, const char *timestamp, const char *title, const char *message, const char *location, void *user_data) { const char level_marks[] = " EACewnid-"; if (mrn_log_file_opened) { mrn::Lock lock(&mrn_log_mutex); fprintf(mrn_log_file, "%s|%c|%08x|%s\n", timestamp, level_marks[level], static_cast((ulong)(pthread_self())), message); fflush(mrn_log_file); } } static grn_logger mrn_logger = { mrn_log_level_default, GRN_LOG_TIME|GRN_LOG_MESSAGE, NULL, mrn_logger_log, NULL, NULL }; static uchar *mrn_allocated_thds_get_key(const uchar *record, size_t *length, my_bool not_used __attribute__ ((unused))) { MRN_DBUG_ENTER_FUNCTION(); *length = sizeof(THD *); DBUG_RETURN(const_cast(record)); } /* system functions */ static struct st_mysql_storage_engine storage_engine_structure = { MYSQL_HANDLERTON_INTERFACE_VERSION }; #if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) # define MRN_STATUS_VARIABLE_ENTRY(name, value, type, scope) \ {name, value, type, scope} #else # define MRN_STATUS_VARIABLE_ENTRY(name, value, type, scope) \ {name, value, type} #endif static struct st_mysql_show_var mrn_status_variables[] = { MRN_STATUS_VARIABLE_ENTRY(MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_count_skip", (char *)&mrn_count_skip, SHOW_LONG, SHOW_SCOPE_GLOBAL), MRN_STATUS_VARIABLE_ENTRY(MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_fast_order_limit", (char *)&mrn_fast_order_limit, SHOW_LONG, SHOW_SCOPE_GLOBAL), MRN_STATUS_VARIABLE_ENTRY(NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL) }; static const char *mrn_log_level_type_names[] = { "NONE", "EMERG", "ALERT", "CRIT", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG", "DUMP", NullS }; static TYPELIB mrn_log_level_typelib = { array_elements(mrn_log_level_type_names) - 1, "mrn_log_level_typelib", mrn_log_level_type_names, NULL }; static void mrn_log_level_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); ulong new_value = *static_cast(save); ulong old_value = mrn_log_level; mrn_log_level = new_value; mrn_logger.max_level = static_cast(mrn_log_level); grn_logger_set(&mrn_ctx, &mrn_logger); grn_ctx *ctx = grn_ctx_open(0); mrn_change_encoding(ctx, system_charset_info); GRN_LOG(ctx, GRN_LOG_NOTICE, "log level changed from '%s' to '%s'", mrn_log_level_type_names[old_value], mrn_log_level_type_names[new_value]); grn_ctx_fin(ctx); DBUG_VOID_RETURN; } static MYSQL_SYSVAR_ENUM(log_level, mrn_log_level, PLUGIN_VAR_RQCMDARG, "logging level", NULL, mrn_log_level_update, static_cast(mrn_log_level), &mrn_log_level_typelib); static void mrn_log_file_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const char *new_value = *((const char **)save); char **old_value_ptr = (char **)var_ptr; grn_ctx *ctx = &mrn_ctx; mrn_change_encoding(ctx, system_charset_info); const char *new_log_file_name; new_log_file_name = *old_value_ptr; if (strcmp(*old_value_ptr, new_value) == 0) { GRN_LOG(ctx, GRN_LOG_NOTICE, "log file isn't changed " "because the requested path isn't different: <%s>", new_value); } else { GRN_LOG(ctx, GRN_LOG_NOTICE, "log file is changed: <%s> -> <%s>", *old_value_ptr, new_value); int log_file_open_errno = 0; { mrn::Lock lock(&mrn_log_mutex); FILE *new_log_file; new_log_file = fopen(new_value, "a"); if (new_log_file) { if (mrn_log_file_opened) { fclose(mrn_log_file); } mrn_log_file = new_log_file; mrn_log_file_opened = true; } else { log_file_open_errno = errno; } } if (log_file_open_errno == 0) { GRN_LOG(ctx, GRN_LOG_NOTICE, "log file is changed: <%s> -> <%s>", *old_value_ptr, new_value); new_log_file_name = new_value; } else { if (mrn_log_file) { GRN_LOG(ctx, GRN_LOG_ERROR, "log file isn't changed " "because the requested path can't be opened: <%s>: <%s>", new_value, strerror(log_file_open_errno)); } else { GRN_LOG(ctx, GRN_LOG_ERROR, "log file can't be opened: <%s>: <%s>", new_value, strerror(log_file_open_errno)); } } } #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR char *old_log_file_name = *old_value_ptr; *old_value_ptr = mrn_my_strdup(new_log_file_name, MYF(MY_WME)); my_free(old_log_file_name); #else *old_value_ptr = mrn_my_strdup(new_log_file_name, MYF(MY_WME)); #endif DBUG_VOID_RETURN; } static MYSQL_SYSVAR_STR(log_file, mrn_log_file_path, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "log file for " MRN_PLUGIN_NAME_STRING, NULL, mrn_log_file_update, MRN_LOG_FILE_PATH); static void mrn_query_log_file_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const char *new_value = *((const char **)save); char **old_value_ptr = (char **)var_ptr; const char *normalized_new_value = NULL; grn_ctx *ctx = &mrn_ctx; mrn_change_encoding(ctx, system_charset_info); const char *new_query_log_file_name; new_query_log_file_name = *old_value_ptr; bool need_update = false; if (!*old_value_ptr) { if (new_value && new_value[0] != '\0') { GRN_LOG(ctx, GRN_LOG_NOTICE, "query log is enabled: <%s>", new_value); need_update = true; normalized_new_value = new_value; } else { GRN_LOG(ctx, GRN_LOG_NOTICE, "query log file is still disabled"); } } else { if (!new_value || new_value[0] == '\0') { GRN_LOG(ctx, GRN_LOG_NOTICE, "query log file is disabled: <%s>", *old_value_ptr); need_update = true; normalized_new_value = NULL; } else if (strcmp(*old_value_ptr, new_value) == 0) { GRN_LOG(ctx, GRN_LOG_NOTICE, "query log file isn't changed " "because the requested path isn't different: <%s>", new_value); } else { GRN_LOG(ctx, GRN_LOG_NOTICE, "query log file is changed: <%s> -> <%s>", *old_value_ptr, new_value); need_update = true; normalized_new_value = new_value; } } if (need_update) { { // TODO: Remove me when Groonga 7.0.5 is released. mrn::Lock lock(&mrn_query_log_mutex); grn_default_query_logger_set_path(normalized_new_value); } grn_query_logger_reopen(ctx); new_query_log_file_name = normalized_new_value; } #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR char *old_query_log_file_name = *old_value_ptr; #endif if (new_query_log_file_name) { *old_value_ptr = mrn_my_strdup(new_query_log_file_name, MYF(0)); } else { *old_value_ptr = NULL; } #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR my_free(old_query_log_file_name); #endif DBUG_VOID_RETURN; } static MYSQL_SYSVAR_STR(query_log_file, mrn_query_log_file_path, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "query log file for " MRN_PLUGIN_NAME_STRING, NULL, mrn_query_log_file_update, NULL); static void mrn_default_tokenizer_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const char *new_value = *((const char **)save); char **old_value_ptr = (char **)var_ptr; grn_ctx *ctx = &mrn_ctx; mrn_change_encoding(ctx, system_charset_info); if (strcmp(*old_value_ptr, new_value) == 0) { GRN_LOG(ctx, GRN_LOG_NOTICE, "default tokenizer for fulltext index isn't changed " "because the requested default tokenizer isn't different: <%s>", new_value); } else { GRN_LOG(ctx, GRN_LOG_NOTICE, "default tokenizer for fulltext index is changed: <%s> -> <%s>", *old_value_ptr, new_value); } #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR my_free(*old_value_ptr); *old_value_ptr = mrn_my_strdup(new_value, MYF(MY_WME)); #else *old_value_ptr = (char *)new_value; #endif DBUG_VOID_RETURN; } static MYSQL_SYSVAR_STR(default_parser, mrn_default_tokenizer, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "default fulltext parser " "(Deprecated. Use mroonga_default_tokenizer instead.)", NULL, mrn_default_tokenizer_update, MRN_DEFAULT_TOKENIZER); // since 10.1.6 static MYSQL_SYSVAR_STR(default_tokenizer, mrn_default_tokenizer, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "default tokenizer for fulltext index", NULL, mrn_default_tokenizer_update, MRN_DEFAULT_TOKENIZER); static MYSQL_THDVAR_BOOL( dry_write, /* name */ PLUGIN_VAR_OPCMDARG, /* options */ "If dry_write is true, any write operations are ignored.", /* comment */ NULL, /* check */ NULL, /* update */ false /* default */ ); static MYSQL_THDVAR_BOOL( enable_optimization, /* name */ PLUGIN_VAR_OPCMDARG, /* options */ "If enable_optimization is true, some optimizations will be applied.", /* comment */ NULL, /* check */ NULL, /* update */ true /* default */ ); static MYSQL_THDVAR_LONGLONG(match_escalation_threshold, PLUGIN_VAR_RQCMDARG, "The threshold to determin whether search method is escalated", NULL, NULL, grn_get_default_match_escalation_threshold(), -1, INT_MAX64, 0); static void mrn_vector_column_delimiter_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const char *new_value = *((const char **)save); char **old_value_ptr = (char **)var_ptr; #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR my_free(*old_value_ptr); *old_value_ptr = mrn_my_strdup(new_value, MYF(MY_WME)); #else *old_value_ptr = (char *)new_value; #endif DBUG_VOID_RETURN; } static MYSQL_SYSVAR_STR(vector_column_delimiter, mrn_vector_column_delimiter, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "The vector column delimiter", NULL, &mrn_vector_column_delimiter_update, " "); static void mrn_database_path_prefix_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const char *new_value = *((const char **)save); char **old_value_ptr = (char **)var_ptr; #ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR if (*old_value_ptr) my_free(*old_value_ptr); if (new_value) *old_value_ptr = mrn_my_strdup(new_value, MYF(MY_WME)); else *old_value_ptr = NULL; #else *old_value_ptr = (char *)new_value; #endif DBUG_VOID_RETURN; } static MYSQL_SYSVAR_STR(database_path_prefix, mrn::PathMapper::default_path_prefix, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, "The database path prefix", NULL, &mrn_database_path_prefix_update, NULL); static MYSQL_SYSVAR_STR(default_wrapper_engine, mrn_default_wrapper_engine, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "The default engine for wrapper mode", NULL, NULL, NULL); static const char *mrn_action_on_error_names[] = { "ERROR", "ERROR_AND_LOG", "IGNORE", "IGNORE_AND_LOG", NullS, }; static TYPELIB mrn_action_on_error_typelib = { array_elements(mrn_action_on_error_names) - 1, "mrn_action_on_error_typelib", mrn_action_on_error_names, NULL }; static MYSQL_THDVAR_ENUM(action_on_fulltext_query_error, PLUGIN_VAR_RQCMDARG, "action on fulltext query error", NULL, NULL, mrn_action_on_fulltext_query_error_default, &mrn_action_on_error_typelib); static void mrn_lock_timeout_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const int new_value = *static_cast(save); int *old_value_ptr = static_cast(var_ptr); *old_value_ptr = new_value; grn_set_lock_timeout(new_value); DBUG_VOID_RETURN; } static MYSQL_SYSVAR_INT(lock_timeout, mrn_lock_timeout, PLUGIN_VAR_RQCMDARG, "lock timeout used in Groonga", NULL, mrn_lock_timeout_update, grn_get_lock_timeout(), -1, INT_MAX, 1); static MYSQL_SYSVAR_STR(libgroonga_version, mrn_libgroonga_version, PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY, "The version of libgroonga", NULL, NULL, grn_get_version()); static MYSQL_SYSVAR_STR(version, mrn_version, PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY, "The version of mroonga", NULL, NULL, MRN_VERSION); static mrn_bool grn_check_zlib_support() { bool is_zlib_support = false; grn_obj grn_support_p; GRN_BOOL_INIT(&grn_support_p, 0); grn_obj_get_info(&mrn_ctx, NULL, GRN_INFO_SUPPORT_ZLIB, &grn_support_p); is_zlib_support = (GRN_BOOL_VALUE(&grn_support_p)); grn_obj_unlink(&mrn_ctx, &grn_support_p); return is_zlib_support; } static MYSQL_SYSVAR_BOOL(libgroonga_support_zlib, mrn_libgroonga_support_zlib, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "The status of libgroonga supports zlib", NULL, NULL, grn_check_zlib_support()); static mrn_bool grn_check_lz4_support() { bool is_lz4_support = false; grn_obj grn_support_p; GRN_BOOL_INIT(&grn_support_p, 0); grn_obj_get_info(&mrn_ctx, NULL, GRN_INFO_SUPPORT_LZ4, &grn_support_p); is_lz4_support = (GRN_BOOL_VALUE(&grn_support_p)); grn_obj_unlink(&mrn_ctx, &grn_support_p); return is_lz4_support; } static MYSQL_SYSVAR_BOOL(libgroonga_support_lz4, mrn_libgroonga_support_lz4, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "The status of libgroonga supports LZ4", NULL, NULL, grn_check_lz4_support()); static mrn_bool grn_check_zstd_support() { bool is_zstd_support = false; grn_obj grn_support_p; GRN_BOOL_INIT(&grn_support_p, 0); grn_obj_get_info(&mrn_ctx, NULL, GRN_INFO_SUPPORT_ZSTD, &grn_support_p); is_zstd_support = (GRN_BOOL_VALUE(&grn_support_p)); grn_obj_unlink(&mrn_ctx, &grn_support_p); return is_zstd_support; } static MYSQL_SYSVAR_BOOL(libgroonga_support_zstd, mrn_libgroonga_support_zstd, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "The status of libgroonga supports Zstandard", NULL, NULL, grn_check_zstd_support()); static void mrn_enable_operations_recording_update(THD *thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save) { MRN_DBUG_ENTER_FUNCTION(); const bool new_value = *static_cast(save); bool *old_value_ptr = static_cast(var_ptr); *old_value_ptr = new_value; DBUG_VOID_RETURN; } static MYSQL_SYSVAR_BOOL(enable_operations_recording, mrn_enable_operations_recording, PLUGIN_VAR_RQCMDARG, "Whether recording operations for recovery is enabled or not", NULL, mrn_enable_operations_recording_update, true); #ifdef MRN_SUPPORT_THDVAR_SET static MYSQL_THDVAR_SET(boolean_mode_syntax_flags, PLUGIN_VAR_RQCMDARG, "The flags to custom syntax in BOOLEAN MODE. " "Available flags: " "DEFAULT(=SYNTAX_QUERY,ALLOW_LEADING_NOT), " "SYNTAX_QUERY, SYNTAX_SCRIPT, " "ALLOW_COLUMN, ALLOW_UPDATE and ALLOW_LEADING_NOT", NULL, NULL, mrn::variables::BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT, &mrn_boolean_mode_syntax_flags_typelib); #endif static const int MRN_MAX_N_RECORDS_FOR_ESTIMATE_DEFAULT = 1000; static MYSQL_THDVAR_INT(max_n_records_for_estimate, PLUGIN_VAR_RQCMDARG, "The max number of records to " "estimate the number of matched records", NULL, NULL, MRN_MAX_N_RECORDS_FOR_ESTIMATE_DEFAULT, -1, INT_MAX, 0); static MYSQL_SYSVAR_BOOL(libgroonga_embedded, mrn_libgroonga_embedded, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "Whether libgroonga is embedded or not", NULL, NULL, mrn_libgroonga_embedded); static struct st_mysql_sys_var *mrn_system_variables[] = { MYSQL_SYSVAR(log_level), MYSQL_SYSVAR(log_file), MYSQL_SYSVAR(default_parser), MYSQL_SYSVAR(default_tokenizer), MYSQL_SYSVAR(dry_write), MYSQL_SYSVAR(enable_optimization), MYSQL_SYSVAR(match_escalation_threshold), MYSQL_SYSVAR(database_path_prefix), MYSQL_SYSVAR(default_wrapper_engine), MYSQL_SYSVAR(action_on_fulltext_query_error), MYSQL_SYSVAR(lock_timeout), MYSQL_SYSVAR(libgroonga_version), MYSQL_SYSVAR(version), MYSQL_SYSVAR(vector_column_delimiter), MYSQL_SYSVAR(libgroonga_support_zlib), MYSQL_SYSVAR(libgroonga_support_lz4), MYSQL_SYSVAR(libgroonga_support_zstd), #ifdef MRN_SUPPORT_THDVAR_SET MYSQL_SYSVAR(boolean_mode_syntax_flags), #endif MYSQL_SYSVAR(max_n_records_for_estimate), MYSQL_SYSVAR(libgroonga_embedded), MYSQL_SYSVAR(query_log_file), MYSQL_SYSVAR(enable_operations_recording), NULL }; /* mroonga information schema */ static struct st_mysql_information_schema i_s_info = { MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; namespace Show { static ST_FIELD_INFO i_s_mrn_stats_fields_info[] = { Column("VERSION", Varchar(40), NOT_NULL), Column("rows_written", SLong(), NOT_NULL, "Rows written to Groonga"), Column("rows_read", SLong(), NOT_NULL, "Rows read from Groonga"), CEnd() }; } // namespace Show static int i_s_mrn_stats_deinit(void* p) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(0); } static int i_s_mrn_stats_fill( THD* thd, TABLE_LIST* tables, Item* cond) { TABLE* table = (TABLE *) tables->table; int status = 0; MRN_DBUG_ENTER_FUNCTION(); table->field[0]->store(grn_get_version(), strlen(grn_get_version()), system_charset_info); table->field[0]->set_notnull(); table->field[1]->store(1); /* TODO */ table->field[2]->store(2); /* TODO */ if (schema_table_store_record(thd, table)) { status = 1; } DBUG_RETURN(status); } static int i_s_mrn_stats_init(void* p) { MRN_DBUG_ENTER_FUNCTION(); ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p; schema->fields_info = Show::i_s_mrn_stats_fields_info; schema->fill_table = i_s_mrn_stats_fill; DBUG_RETURN(0); } struct st_mysql_plugin i_s_mrn_stats = { MYSQL_INFORMATION_SCHEMA_PLUGIN, &i_s_info, MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_stats", MRN_PLUGIN_AUTHOR, "Statistics for " MRN_PLUGIN_NAME_STRING, PLUGIN_LICENSE_GPL, i_s_mrn_stats_init, #ifdef MRN_ST_MYSQL_PLUGIN_HAVE_CHECK_UNINSTALL NULL, #endif i_s_mrn_stats_deinit, MRN_VERSION_IN_HEX, NULL, NULL, MRN_PLUGIN_LAST_VALUES }; /* End of mroonga information schema implementations */ static handler *mrn_handler_create(handlerton *hton, TABLE_SHARE *share, #ifdef MRN_HANDLERTON_CREATE_HAVE_PARTITIONED bool partitioned, #endif MEM_ROOT *root) { MRN_DBUG_ENTER_FUNCTION(); handler *new_handler = new (root) ha_mroonga(hton, share); DBUG_RETURN(new_handler); } static void mrn_drop_database(handlerton *hton, char *path) { MRN_DBUG_ENTER_FUNCTION(); mrn_db_manager->drop(path); DBUG_VOID_RETURN; } static int mrn_close_connection(handlerton *hton, THD *thd) { MRN_DBUG_ENTER_FUNCTION(); void *p = thd_get_ha_data(thd, mrn_hton_ptr); if (p) { mrn_clear_slot_data(thd); free(p); { mrn::Lock lock(&mrn_allocated_thds_mutex); my_hash_delete(&mrn_allocated_thds, (uchar*) thd); } } DBUG_RETURN(0); } #ifdef MRN_FLUSH_LOGS_HAVE_BINLOG_GROUP_FLUSH static bool mrn_flush_logs(handlerton *hton, bool binlog_group_flush) #else static bool mrn_flush_logs(handlerton *hton) #endif { MRN_DBUG_ENTER_FUNCTION(); bool result = 0; if (mrn_log_file_opened) { mrn::Lock lock(&mrn_log_mutex); fclose(mrn_log_file); mrn_log_file = fopen(mrn_log_file_path, "a"); } DBUG_RETURN(result); } static grn_builtin_type mrn_grn_type_from_field(grn_ctx *ctx, Field *field, bool for_index_key) { grn_builtin_type type = GRN_DB_VOID; enum_field_types mysql_field_type = field->real_type(); switch (mysql_field_type) { case MYSQL_TYPE_DECIMAL: // DECIMAL; <= 65bytes type = GRN_DB_SHORT_TEXT; // 4Kbytes break; case MYSQL_TYPE_TINY: // TINYINT; 1byte if (static_cast(field)->unsigned_flag) { type = GRN_DB_UINT8; // 1byte } else { type = GRN_DB_INT8; // 1byte } break; case MYSQL_TYPE_SHORT: // SMALLINT; 2bytes if (static_cast(field)->unsigned_flag) { type = GRN_DB_UINT16; // 2bytes } else { type = GRN_DB_INT16; // 2bytes } break; case MYSQL_TYPE_LONG: // INT; 4bytes if (static_cast(field)->unsigned_flag) { type = GRN_DB_UINT32; // 4bytes } else { type = GRN_DB_INT32; // 4bytes } break; case MYSQL_TYPE_FLOAT: // FLOAT; 4 or 8bytes case MYSQL_TYPE_DOUBLE: // DOUBLE; 8bytes type = GRN_DB_FLOAT; // 8bytes break; case MYSQL_TYPE_NULL: // NULL; 1byte type = GRN_DB_INT8; // 1byte break; case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP; 4bytes type = GRN_DB_TIME; // 8bytes break; case MYSQL_TYPE_LONGLONG: // BIGINT; 8bytes if (static_cast(field)->unsigned_flag) { type = GRN_DB_UINT64; // 8bytes } else { type = GRN_DB_INT64; // 8bytes } break; case MYSQL_TYPE_INT24: // MEDIUMINT; 3bytes if (static_cast(field)->unsigned_flag) { type = GRN_DB_UINT32; // 4bytes } else { type = GRN_DB_INT32; // 4bytes } break; case MYSQL_TYPE_DATE: // DATE; 4bytes case MYSQL_TYPE_TIME: // TIME; 3bytes case MYSQL_TYPE_DATETIME: // DATETIME; 8bytes case MYSQL_TYPE_YEAR: // YEAR; 1byte case MYSQL_TYPE_NEWDATE: // DATE; 3bytes type = GRN_DB_TIME; // 8bytes break; case MYSQL_TYPE_VARCHAR: // VARCHAR; <= 64KB * 4 + 2bytes if (for_index_key) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else { if (field->field_length <= MRN_SHORT_TEXT_SIZE) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else if (field->field_length <= MRN_TEXT_SIZE) { type = GRN_DB_TEXT; // 64Kbytes } else { type = GRN_DB_LONG_TEXT; // 2Gbytes } } break; case MYSQL_TYPE_BIT: // BIT; <= 8bytes type = GRN_DB_INT64; // 8bytes break; #ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 case MYSQL_TYPE_TIMESTAMP2: // TIMESTAMP; 4bytes type = GRN_DB_TIME; // 8bytes break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 case MYSQL_TYPE_DATETIME2: // DATETIME; 8bytes type = GRN_DB_TIME; // 8bytes break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 case MYSQL_TYPE_TIME2: // TIME(FSP); 3 + (FSP + 1) / 2 bytes // 0 <= FSP <= 6; 3-6bytes type = GRN_DB_TIME; // 8bytes break; #endif case MYSQL_TYPE_NEWDECIMAL: // DECIMAL; <= 9bytes type = GRN_DB_SHORT_TEXT; // 4Kbytes break; case MYSQL_TYPE_ENUM: // ENUM; <= 2bytes if (field->pack_length() == 1) { type = GRN_DB_UINT8; // 1bytes } else { type = GRN_DB_UINT16; // 2bytes } break; case MYSQL_TYPE_SET: // SET; <= 8bytes switch (field->pack_length()) { case 1: type = GRN_DB_UINT8; // 1byte break; case 2: type = GRN_DB_UINT16; // 2bytes break; case 3: case 4: type = GRN_DB_UINT32; // 3bytes break; case 8: default: type = GRN_DB_UINT64; // 8bytes break; } break; case MYSQL_TYPE_TINY_BLOB: // TINYBLOB; <= 256bytes + 1byte type = GRN_DB_SHORT_TEXT; // 4Kbytes break; case MYSQL_TYPE_MEDIUM_BLOB: // MEDIUMBLOB; <= 16Mbytes + 3bytes if (for_index_key) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else { type = GRN_DB_LONG_TEXT; // 2Gbytes } break; case MYSQL_TYPE_LONG_BLOB: // LONGBLOB; <= 4Gbytes + 4bytes if (for_index_key) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else { type = GRN_DB_LONG_TEXT; // 2Gbytes } break; case MYSQL_TYPE_BLOB: // BLOB; <= 64Kbytes + 2bytes if (for_index_key) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else { type = GRN_DB_LONG_TEXT; // 2Gbytes } break; case MYSQL_TYPE_VAR_STRING: // VARCHAR; <= 255byte * 4 + 1bytes if (for_index_key) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else { if (field->field_length <= MRN_SHORT_TEXT_SIZE) { type = GRN_DB_SHORT_TEXT; // 4Kbytes } else if (field->field_length <= MRN_TEXT_SIZE) { type = GRN_DB_TEXT; // 64Kbytes } else { type = GRN_DB_LONG_TEXT; // 2Gbytes } } break; case MYSQL_TYPE_STRING: // CHAR; < 1Kbytes =~ (255 * 4)bytes // 4 is the maximum size of a character type = GRN_DB_SHORT_TEXT; // 4Kbytes break; case MYSQL_TYPE_GEOMETRY: // case-by-case type = GRN_DB_WGS84_GEO_POINT; // 8bytes break; case MYSQL_TYPE_VARCHAR_COMPRESSED: case MYSQL_TYPE_BLOB_COMPRESSED: DBUG_ASSERT(0); #ifdef MRN_HAVE_MYSQL_TYPE_JSON case MYSQL_TYPE_JSON: type = GRN_DB_TEXT; break; #endif } return type; } static bool mrn_parse_grn_column_create_flags(THD *thd, grn_ctx *ctx, const char *flag_names, uint flag_names_length, grn_obj_flags *column_flags) { const char *flag_names_end = flag_names + flag_names_length; bool found = false; while (flag_names < flag_names_end) { uint rest_length = flag_names_end - flag_names; if (*flag_names == '|' || *flag_names == ' ') { flag_names += 1; continue; } if (rest_length >= 13 && !memcmp(flag_names, "COLUMN_SCALAR", 13)) { *column_flags |= GRN_OBJ_COLUMN_SCALAR; flag_names += 13; found = true; } else if (rest_length >= 13 && !memcmp(flag_names, "COLUMN_VECTOR", 13)) { *column_flags |= GRN_OBJ_COLUMN_VECTOR; flag_names += 13; found = true; } else if (rest_length >= 13 && !memcmp(flag_names, "COMPRESS_ZLIB", 13)) { if (mrn_libgroonga_support_zlib) { *column_flags |= GRN_OBJ_COMPRESS_ZLIB; found = true; } else { push_warning_printf(thd, MRN_SEVERITY_WARNING, ER_MRN_UNSUPPORTED_COLUMN_FLAG_NUM, ER_MRN_UNSUPPORTED_COLUMN_FLAG_STR, "COMPRESS_ZLIB"); } flag_names += 13; } else if (rest_length >= 12 && !memcmp(flag_names, "COMPRESS_LZ4", 12)) { if (mrn_libgroonga_support_lz4) { *column_flags |= GRN_OBJ_COMPRESS_LZ4; found = true; } else { push_warning_printf(thd, MRN_SEVERITY_WARNING, ER_MRN_UNSUPPORTED_COLUMN_FLAG_NUM, ER_MRN_UNSUPPORTED_COLUMN_FLAG_STR, "COMPRESS_LZ4"); } flag_names += 12; } else if (rest_length >= 13 && !memcmp(flag_names, "COMPRESS_ZSTD", 13)) { if (mrn_libgroonga_support_zstd) { *column_flags |= GRN_OBJ_COMPRESS_ZSTD; found = true; } else { push_warning_printf(thd, MRN_SEVERITY_WARNING, ER_MRN_UNSUPPORTED_COLUMN_FLAG_NUM, ER_MRN_UNSUPPORTED_COLUMN_FLAG_STR, "COMPRESS_ZSTD"); } flag_names += 13; } else { char invalid_flag_name[MRN_MESSAGE_BUFFER_SIZE]; snprintf(invalid_flag_name, MRN_MESSAGE_BUFFER_SIZE, "%.*s", static_cast(rest_length), flag_names); push_warning_printf(thd, MRN_SEVERITY_WARNING, ER_MRN_INVALID_COLUMN_FLAG_NUM, ER_MRN_INVALID_COLUMN_FLAG_STR, invalid_flag_name); break; } } return found; } static bool mrn_parse_grn_index_column_flags(THD *thd, grn_ctx *ctx, const char *flag_names, uint flag_names_length, grn_column_flags *index_column_flags) { const char *flag_names_end = flag_names + flag_names_length; bool found = false; while (flag_names < flag_names_end) { uint rest_length = flag_names_end - flag_names; if (*flag_names == '|' || *flag_names == ' ') { flag_names += 1; continue; } if (rest_length >= 4 && !memcmp(flag_names, "NONE", 4)) { flag_names += 4; found = true; } else if (rest_length >= 13 && !memcmp(flag_names, "WITH_POSITION", 13)) { *index_column_flags |= GRN_OBJ_WITH_POSITION; flag_names += 13; found = true; } else if (rest_length >= 12 && !memcmp(flag_names, "WITH_SECTION", 12)) { *index_column_flags |= GRN_OBJ_WITH_SECTION; flag_names += 12; found = true; } else if (rest_length >= 11 && !memcmp(flag_names, "WITH_WEIGHT", 11)) { *index_column_flags |= GRN_OBJ_WITH_WEIGHT; flag_names += 11; found = true; } else if (rest_length >= 11 && !memcmp(flag_names, "INDEX_SMALL", 11)) { *index_column_flags |= GRN_OBJ_INDEX_SMALL; flag_names += 11; found = true; } else if (rest_length >= 12 && !memcmp(flag_names, "INDEX_MEDIUM", 12)) { *index_column_flags |= GRN_OBJ_INDEX_MEDIUM; flag_names += 12; found = true; } else { char invalid_flag_name[MRN_MESSAGE_BUFFER_SIZE]; snprintf(invalid_flag_name, MRN_MESSAGE_BUFFER_SIZE, "%.*s", static_cast(rest_length), flag_names); push_warning_printf(thd, MRN_SEVERITY_WARNING, ER_MRN_INVALID_INDEX_FLAG_NUM, ER_MRN_INVALID_INDEX_FLAG_STR, invalid_flag_name); } } return found; } #ifdef MRN_HAVE_SPATIAL static int mrn_set_geometry(grn_ctx *ctx, grn_obj *buf, const char *wkb, uint wkb_size) { int error = 0; Geometry_buffer buffer; Geometry *geometry; geometry = Geometry::construct(&buffer, wkb, wkb_size); if (!geometry) { return ER_CANT_CREATE_GEOMETRY_OBJECT; } switch (geometry->get_class_info()->m_type_id) { case Geometry::wkb_point: { Gis_point *point = (Gis_point *)geometry; double latitude = 0.0, longitude = 0.0; #ifdef MRN_HAVE_POINT_XY point_xy xy(0.0, 0.0); point->get_xy(&xy); longitude = xy.x; latitude = xy.y; #else point->get_xy(&longitude, &latitude); #endif grn_obj_reinit(ctx, buf, GRN_DB_WGS84_GEO_POINT, 0); GRN_GEO_POINT_SET(ctx, buf, GRN_GEO_DEGREE2MSEC(latitude), GRN_GEO_DEGREE2MSEC(longitude)); break; } default: my_printf_error(ER_MRN_GEOMETRY_NOT_SUPPORT_NUM, ER_MRN_GEOMETRY_NOT_SUPPORT_STR, MYF(0)); error = ER_MRN_GEOMETRY_NOT_SUPPORT_NUM; break; } MRN_GEOMETRY_FREE(geometry); return error; } #endif #ifdef MRN_HAVE_HTON_ALTER_TABLE_FLAGS static alter_table_operations mrn_alter_table_flags(alter_table_operations flags) { ulonglong alter_flags = 0; #ifdef HA_INPLACE_ADD_INDEX_NO_READ_WRITE bool is_inplace_index_change; # ifdef MRN_HAVE_ALTER_INFO is_inplace_index_change = (((flags & ALTER_ADD_INDEX) && (flags & ALTER_DROP_INDEX)) || (flags & ALTER_CHANGE_COLUMN)); # else is_inplace_index_change = (((flags & ALTER_ADD_INDEX) && (flags & ALTER_DROP_INDEX)) || (flags & ALTER_CHANGE_COLUMN)); # endif if (!is_inplace_index_change) { alter_flags |= HA_INPLACE_ADD_INDEX_NO_READ_WRITE | HA_INPLACE_DROP_INDEX_NO_READ_WRITE | HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE | HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE | HA_INPLACE_ADD_INDEX_NO_WRITE | HA_INPLACE_DROP_INDEX_NO_WRITE | HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE | HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE; } #endif return alter_flags; } #endif #ifdef MRN_SUPPORT_CUSTOM_OPTIONS static ha_create_table_option mrn_field_options[] = { HA_FOPTION_STRING("GROONGA_TYPE", groonga_type), HA_FOPTION_STRING("FLAGS", flags), HA_FOPTION_END }; static ha_create_table_option mrn_index_options[] = { HA_IOPTION_STRING("TOKENIZER", tokenizer), HA_IOPTION_STRING("NORMALIZER", normalizer), HA_IOPTION_STRING("TOKEN_FILTERS", token_filters), HA_IOPTION_STRING("FLAGS", flags), HA_IOPTION_END }; #endif static int mrn_init(void *p) { // init handlerton grn_ctx *ctx = NULL; handlerton *hton = static_cast(p); hton->create = mrn_handler_create; hton->flags = HTON_NO_FLAGS; #ifndef MRN_SUPPORT_PARTITION hton->flags |= HTON_NO_PARTITION; #endif hton->drop_database = mrn_drop_database; hton->close_connection = mrn_close_connection; hton->flush_logs = mrn_flush_logs; #ifdef MRN_HAVE_HTON_ALTER_TABLE_FLAGS hton->alter_table_flags = mrn_alter_table_flags; #endif #ifdef MRN_SUPPORT_CUSTOM_OPTIONS hton->field_options = mrn_field_options; hton->index_options = mrn_index_options; #endif mrn_hton_ptr = hton; #ifdef _WIN32 HMODULE current_module = GetModuleHandle(NULL); mrn_binlog_filter = *((Rpl_filter **)GetProcAddress(current_module, MRN_BINLOG_FILTER_PROC)); mrn_my_tz_UTC = *((Time_zone **)GetProcAddress(current_module, MRN_MY_TZ_UTC_PROC)); # ifdef MRN_HAVE_TABLE_DEF_CACHE mrn_table_def_cache = (HASH *)GetProcAddress(current_module, "?table_def_cache@@3Ust_hash@@A"); # endif # ifndef MRN_HAVE_TDC_LOCK_TABLE_SHARE mrn_LOCK_open = (mysql_mutex_t *)GetProcAddress(current_module, "?LOCK_open@@3Ust_mysql_mutex@@A"); # endif # ifdef HAVE_PSI_INTERFACE # ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE mrn_table_share_lock_share = (PSI_mutex_key *)GetProcAddress(current_module, MRN_TABLE_SHARE_LOCK_SHARE_PROC); # endif mrn_table_share_lock_ha_data = (PSI_mutex_key *)GetProcAddress(current_module, MRN_TABLE_SHARE_LOCK_HA_DATA_PROC); # endif #else mrn_binlog_filter = binlog_filter; mrn_my_tz_UTC = my_tz_UTC; # ifdef MRN_HAVE_TABLE_DEF_CACHE mrn_table_def_cache = &table_def_cache; # endif # ifndef MRN_HAVE_TDC_LOCK_TABLE_SHARE mrn_LOCK_open = &LOCK_open; # endif #endif #ifdef MRN_HAVE_PSI_SERVER if (PSI_server) { const char *category = "mroonga"; int n_mutexes = array_elements(mrn_mutexes); PSI_server->register_mutex(category, mrn_mutexes, n_mutexes); } #endif grn_default_query_logger_set_path(mrn_query_log_file_path); if (grn_init() != GRN_SUCCESS) { goto err_grn_init; } grn_set_lock_timeout(mrn_lock_timeout); mrn_init_encoding_map(); grn_ctx_init(&mrn_ctx, 0); ctx = &mrn_ctx; if (mrn_change_encoding(ctx, system_charset_info)) goto err_mrn_change_encoding; #ifdef MRN_HAVE_PSI_MEMORY_KEY { const char *category = "ha_mroonga"; int n_keys = array_elements(mrn_all_memory_keys); mysql_memory_register(category, mrn_all_memory_keys, n_keys); } #endif if (mysql_mutex_init(mrn_log_mutex_key, &mrn_log_mutex, MY_MUTEX_INIT_FAST) != 0) { goto err_log_mutex_init; } if (mysql_mutex_init(mrn_query_log_mutex_key, &mrn_query_log_mutex, MY_MUTEX_INIT_FAST) != 0) { goto err_query_log_mutex_init; } mrn_logger.max_level = static_cast(mrn_log_level); grn_logger_set(ctx, &mrn_logger); if (!(mrn_log_file = fopen(mrn_log_file_path, "a"))) { goto err_log_file_open; } mrn_log_file_opened = true; GRN_LOG(ctx, GRN_LOG_NOTICE, "%s started.", MRN_PACKAGE_STRING); GRN_LOG(ctx, GRN_LOG_NOTICE, "log level is '%s'", mrn_log_level_type_names[mrn_log_level]); // init meta-info database if (!(mrn_db = grn_db_create(ctx, NULL, NULL))) { GRN_LOG(ctx, GRN_LOG_ERROR, "cannot create system database, exiting"); goto err_db_create; } grn_ctx_use(ctx, mrn_db); grn_ctx_init(&mrn_db_manager_ctx, 0); grn_logger_set(&mrn_db_manager_ctx, &mrn_logger); if (mysql_mutex_init(mrn_db_manager_mutex_key, &mrn_db_manager_mutex, MY_MUTEX_INIT_FAST) != 0) { GRN_LOG(&mrn_db_manager_ctx, GRN_LOG_ERROR, "failed to initialize mutex for database manager"); goto err_db_manager_mutex_init; } mrn_db_manager = new mrn::DatabaseManager(&mrn_db_manager_ctx, &mrn_db_manager_mutex); if (!mrn_db_manager->init()) { goto err_db_manager_init; } if (mysql_mutex_init(mrn_context_pool_mutex_key, &mrn_context_pool_mutex, MY_MUTEX_INIT_FAST) != 0) { GRN_LOG(ctx, GRN_LOG_ERROR, "failed to initialize mutex for context pool"); goto error_context_pool_mutex_init; } mrn_context_pool = new mrn::ContextPool(&mrn_context_pool_mutex); if (mysql_mutex_init(mrn_operations_mutex_key, &mrn_operations_mutex, MY_MUTEX_INIT_FAST) != 0) { GRN_LOG(ctx, GRN_LOG_ERROR, "failed to initialize mutex for operations"); goto error_operations_mutex_init; } if ((mysql_mutex_init(mrn_allocated_thds_mutex_key, &mrn_allocated_thds_mutex, MY_MUTEX_INIT_FAST) != 0)) { goto err_allocated_thds_mutex_init; } if (mrn_my_hash_init(&mrn_allocated_thds, system_charset_info, 32, 0, 0, mrn_allocated_thds_get_key, 0, 0)) { goto error_allocated_thds_hash_init; } if ((mysql_mutex_init(mrn_open_tables_mutex_key, &mrn_open_tables_mutex, MY_MUTEX_INIT_FAST) != 0)) { goto err_allocated_open_tables_mutex_init; } if (mrn_my_hash_init(&mrn_open_tables, system_charset_info, 32, 0, 0, mrn_open_tables_get_key, 0, 0)) { goto error_allocated_open_tables_hash_init; } if ((mysql_mutex_init(mrn_long_term_share_mutex_key, &mrn_long_term_share_mutex, MY_MUTEX_INIT_FAST) != 0)) { goto error_allocated_long_term_share_mutex_init; } if (mrn_my_hash_init(&mrn_long_term_share, system_charset_info, 32, 0, 0, mrn_long_term_share_get_key, 0, 0)) { goto error_allocated_long_term_share_hash_init; } #ifdef MRN_USE_MYSQL_DATA_HOME mrn::PathMapper::default_mysql_data_home_path = mysql_data_home; #endif return 0; error_allocated_long_term_share_hash_init: mysql_mutex_destroy(&mrn_long_term_share_mutex); error_allocated_long_term_share_mutex_init: my_hash_free(&mrn_open_tables); error_allocated_open_tables_hash_init: mysql_mutex_destroy(&mrn_open_tables_mutex); err_allocated_open_tables_mutex_init: my_hash_free(&mrn_allocated_thds); error_allocated_thds_hash_init: mysql_mutex_destroy(&mrn_allocated_thds_mutex); err_allocated_thds_mutex_init: mysql_mutex_destroy(&mrn_operations_mutex); error_operations_mutex_init: delete mrn_context_pool; mysql_mutex_destroy(&mrn_context_pool_mutex); error_context_pool_mutex_init: err_db_manager_init: delete mrn_db_manager; mysql_mutex_destroy(&mrn_db_manager_mutex); err_db_manager_mutex_init: grn_ctx_fin(&mrn_db_manager_ctx); grn_obj_unlink(ctx, mrn_db); err_db_create: if (mrn_log_file_opened) { fclose(mrn_log_file); mrn_log_file_opened = false; } err_log_file_open: mysql_mutex_destroy(&mrn_query_log_mutex); err_query_log_mutex_init: mysql_mutex_destroy(&mrn_log_mutex); err_log_mutex_init: err_mrn_change_encoding: grn_ctx_fin(ctx); grn_fin(); err_grn_init: return -1; } static int mrn_deinit(void *p) { THD *thd = current_thd, *tmp_thd; grn_ctx *ctx = &mrn_ctx; MRN_LONG_TERM_SHARE *long_term_share; GRN_LOG(ctx, GRN_LOG_NOTICE, "%s deinit", MRN_PACKAGE_STRING); if (thd && thd_sql_command(thd) == SQLCOM_UNINSTALL_PLUGIN) { mrn::Lock lock(&mrn_allocated_thds_mutex); while ((tmp_thd = (THD *) my_hash_element(&mrn_allocated_thds, 0))) { mrn_clear_slot_data(tmp_thd); void *slot_ptr = mrn_get_slot_data(tmp_thd, false); if (slot_ptr) free(slot_ptr); thd_set_ha_data(tmp_thd, mrn_hton_ptr, 0); my_hash_delete(&mrn_allocated_thds, (uchar *) tmp_thd); } } { mrn::Lock lock(&mrn_open_tables_mutex); while ((long_term_share = (MRN_LONG_TERM_SHARE *) my_hash_element(&mrn_long_term_share, 0))) { mrn_free_long_term_share(long_term_share); } } my_hash_free(&mrn_long_term_share); mysql_mutex_destroy(&mrn_long_term_share_mutex); my_hash_free(&mrn_open_tables); mysql_mutex_destroy(&mrn_open_tables_mutex); my_hash_free(&mrn_allocated_thds); mysql_mutex_destroy(&mrn_allocated_thds_mutex); mysql_mutex_destroy(&mrn_operations_mutex); delete mrn_context_pool; mysql_mutex_destroy(&mrn_context_pool_mutex); delete mrn_db_manager; mysql_mutex_destroy(&mrn_db_manager_mutex); grn_ctx_fin(&mrn_db_manager_ctx); grn_obj_unlink(ctx, mrn_db); grn_ctx_fin(ctx); grn_fin(); if (mrn_log_file_opened) { fclose(mrn_log_file); mrn_log_file_opened = false; } mysql_mutex_destroy(&mrn_query_log_mutex); mysql_mutex_destroy(&mrn_log_mutex); return 0; } mrn_declare_plugin(MRN_PLUGIN_NAME) { MYSQL_STORAGE_ENGINE_PLUGIN, &storage_engine_structure, MRN_PLUGIN_NAME_STRING, MRN_PLUGIN_AUTHOR, "CJK-ready fulltext search, column store", PLUGIN_LICENSE_GPL, mrn_init, mrn_deinit, MRN_VERSION_IN_HEX, mrn_status_variables, mrn_system_variables, MRN_PLUGIN_LAST_VALUES }, i_s_mrn_stats mrn_declare_plugin_end; static double mrn_get_score_value(grn_obj *score) { MRN_DBUG_ENTER_FUNCTION(); double score_value; if (score->header.domain == GRN_DB_FLOAT) { score_value = GRN_FLOAT_VALUE(score); } else { score_value = (double)GRN_INT32_VALUE(score); } DBUG_RETURN(score_value); } static void mrn_generic_ft_clear(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; if (!info->ctx) { DBUG_VOID_RETURN; } if (info->cursor) { grn_obj_unlink(info->ctx, info->cursor); } if (info->id_accessor) { grn_obj_unlink(info->ctx, info->id_accessor); } if (info->key_accessor) { grn_obj_unlink(info->ctx, info->key_accessor); } grn_obj_unlink(info->ctx, info->result); grn_obj_unlink(info->ctx, info->score_column); grn_obj_unlink(info->ctx, &(info->key)); grn_obj_unlink(info->ctx, &(info->score)); info->ctx = NULL; DBUG_VOID_RETURN; } static void mrn_generic_ft_close_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; mrn_generic_ft_clear(handler); delete info; DBUG_VOID_RETURN; } static int mrn_wrapper_ft_read_next(FT_INFO *handler, char *record) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(HA_ERR_END_OF_FILE); } static float mrn_wrapper_ft_find_relevance(FT_INFO *handler, uchar *record, uint length) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; float score = 0.0; grn_id record_id; mrn_change_encoding(info->ctx, NULL); key_copy((uchar *)(GRN_TEXT_VALUE(&(info->key))), record, info->primary_key_info, info->primary_key_info->key_length); record_id = grn_table_get(info->ctx, info->table, GRN_TEXT_VALUE(&(info->key)), GRN_TEXT_LEN(&(info->key))); if (record_id != GRN_ID_NIL) { grn_id result_record_id; result_record_id = grn_table_get(info->ctx, info->result, &record_id, sizeof(grn_id)); if (result_record_id != GRN_ID_NIL) { GRN_BULK_REWIND(&(info->score)); grn_obj_get_value(info->ctx, info->score_column, result_record_id, &(info->score)); score = mrn_get_score_value(&(info->score)); } } DBUG_PRINT("info", ("mroonga: record_id=%d score=%g", record_id, score)); DBUG_RETURN(score); } static void mrn_wrapper_ft_close_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); mrn_generic_ft_close_search(handler); DBUG_VOID_RETURN; } static float mrn_wrapper_ft_get_relevance(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; float score = 0.0; grn_id record_id; ha_mroonga *mroonga = info->mroonga; mrn_change_encoding(info->ctx, NULL); record_id = grn_table_get(info->ctx, info->table, GRN_TEXT_VALUE(&(mroonga->key_buffer)), GRN_TEXT_LEN(&(mroonga->key_buffer))); if (record_id != GRN_ID_NIL) { grn_id result_record_id; result_record_id = grn_table_get(info->ctx, info->result, &record_id, sizeof(grn_id)); if (result_record_id != GRN_ID_NIL) { GRN_BULK_REWIND(&(info->score)); grn_obj_get_value(info->ctx, info->score_column, result_record_id, &(info->score)); score = mrn_get_score_value(&(info->score)); } } DBUG_PRINT("info", ("mroonga: record_id=%d score=%g", record_id, score)); DBUG_RETURN(score); } static void mrn_wrapper_ft_reinit_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); DBUG_VOID_RETURN; } static _ft_vft mrn_wrapper_ft_vft = { mrn_wrapper_ft_read_next, mrn_wrapper_ft_find_relevance, mrn_wrapper_ft_close_search, mrn_wrapper_ft_get_relevance, mrn_wrapper_ft_reinit_search }; static int mrn_storage_ft_read_next(FT_INFO *handler, char *record) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(HA_ERR_END_OF_FILE); } static float mrn_storage_ft_find_relevance(FT_INFO *handler, uchar *record, uint length) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; ha_mroonga *mroonga = info->mroonga; mrn_change_encoding(info->ctx, NULL); float score = 0.0; if (mroonga->record_id != GRN_ID_NIL) { grn_id result_record_id; result_record_id = grn_table_get(info->ctx, info->result, &(mroonga->record_id), sizeof(grn_id)); if (result_record_id != GRN_ID_NIL) { GRN_BULK_REWIND(&(info->score)); grn_obj_get_value(info->ctx, info->score_column, result_record_id, &(info->score)); score = mrn_get_score_value(&(info->score)); } } DBUG_PRINT("info", ("mroonga: record_id=%d score=%g", mroonga->record_id, score)); DBUG_RETURN(score); } static void mrn_storage_ft_close_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); mrn_generic_ft_close_search(handler); DBUG_VOID_RETURN; } static float mrn_storage_ft_get_relevance(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; ha_mroonga *mroonga = info->mroonga; mrn_change_encoding(info->ctx, NULL); float score = 0.0; if (mroonga->record_id != GRN_ID_NIL) { grn_id result_record_id; result_record_id = grn_table_get(info->ctx, info->result, &(mroonga->record_id), sizeof(grn_id)); if (result_record_id != GRN_ID_NIL) { GRN_BULK_REWIND(&(info->score)); grn_obj_get_value(info->ctx, info->score_column, result_record_id, &(info->score)); score = mrn_get_score_value(&(info->score)); } } DBUG_PRINT("info", ("mroonga: record_id=%d score=%g", mroonga->record_id, score)); DBUG_RETURN(score); } static void mrn_storage_ft_reinit_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); DBUG_VOID_RETURN; } static _ft_vft mrn_storage_ft_vft = { mrn_storage_ft_read_next, mrn_storage_ft_find_relevance, mrn_storage_ft_close_search, mrn_storage_ft_get_relevance, mrn_storage_ft_reinit_search }; static int mrn_no_such_key_ft_read_next(FT_INFO *handler, char *record) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(HA_ERR_END_OF_FILE); } static float mrn_no_such_key_ft_find_relevance(FT_INFO *handler, uchar *record, uint length) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(0.0); } static void mrn_no_such_key_ft_close_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; delete info; DBUG_VOID_RETURN; } static float mrn_no_such_key_ft_get_relevance(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); DBUG_RETURN(0.0); } static void mrn_no_such_key_ft_reinit_search(FT_INFO *handler) { MRN_DBUG_ENTER_FUNCTION(); DBUG_VOID_RETURN; } static _ft_vft mrn_no_such_key_ft_vft = { mrn_no_such_key_ft_read_next, mrn_no_such_key_ft_find_relevance, mrn_no_such_key_ft_close_search, mrn_no_such_key_ft_get_relevance, mrn_no_such_key_ft_reinit_search }; #ifdef HA_CAN_FULLTEXT_EXT static uint mrn_generic_ft_get_version() { MRN_DBUG_ENTER_FUNCTION(); // This value is not used in MySQL 5.6.7-rc. So it is // meaningless. It may be used in the future... uint version = 1; DBUG_RETURN(version); } static ulonglong mrn_generic_ft_ext_get_flags() { MRN_DBUG_ENTER_FUNCTION(); // TODO: Should we support FTS_ORDERED_RESULT? // TODO: Shuold we support FTS_DOCID_IN_RESULT? ulonglong flags = 0; DBUG_RETURN(flags); } // This function is used if we enable FTS_DOCID_IN_RESULT flag and the // table has "FTS_DOC_ID" (defined as FTS_DOC_ID_COL_NAME macro) // special name column. Should we support "FTS_DOC_ID" special name // column? // See also sql/sql_optimizer.cc:JOIN::optimize_fts_query(). static ulonglong mrn_generic_ft_ext_get_docid(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong id = GRN_ID_NIL; DBUG_RETURN(id); } static ulonglong mrn_generic_ft_ext_count_matches(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = reinterpret_cast(handler); ulonglong n_records = grn_table_size(info->ctx, info->result); DBUG_RETURN(n_records); } static uint mrn_wrapper_ft_ext_get_version() { MRN_DBUG_ENTER_FUNCTION(); uint version = mrn_generic_ft_get_version(); DBUG_RETURN(version); } static ulonglong mrn_wrapper_ft_ext_get_flags() { MRN_DBUG_ENTER_FUNCTION(); ulonglong flags = mrn_generic_ft_ext_get_flags(); DBUG_RETURN(flags); } static ulonglong mrn_wrapper_ft_ext_get_docid(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong id = mrn_generic_ft_ext_get_docid(handler); DBUG_RETURN(id); } static ulonglong mrn_wrapper_ft_ext_count_matches(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong n_records = mrn_generic_ft_ext_count_matches(handler); DBUG_RETURN(n_records); } static _ft_vft_ext mrn_wrapper_ft_vft_ext = { mrn_wrapper_ft_ext_get_version, mrn_wrapper_ft_ext_get_flags, mrn_wrapper_ft_ext_get_docid, mrn_wrapper_ft_ext_count_matches }; static uint mrn_storage_ft_ext_get_version() { MRN_DBUG_ENTER_FUNCTION(); uint version = mrn_generic_ft_get_version(); DBUG_RETURN(version); } static ulonglong mrn_storage_ft_ext_get_flags() { MRN_DBUG_ENTER_FUNCTION(); ulonglong flags = mrn_generic_ft_ext_get_flags(); DBUG_RETURN(flags); } static ulonglong mrn_storage_ft_ext_get_docid(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong id = mrn_generic_ft_ext_get_docid(handler); DBUG_RETURN(id); } static ulonglong mrn_storage_ft_ext_count_matches(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong n_records = mrn_generic_ft_ext_count_matches(handler); DBUG_RETURN(n_records); } static _ft_vft_ext mrn_storage_ft_vft_ext = { mrn_storage_ft_ext_get_version, mrn_storage_ft_ext_get_flags, mrn_storage_ft_ext_get_docid, mrn_storage_ft_ext_count_matches }; static uint mrn_no_such_key_ft_ext_get_version() { MRN_DBUG_ENTER_FUNCTION(); uint version = mrn_generic_ft_get_version(); DBUG_RETURN(version); } static ulonglong mrn_no_such_key_ft_ext_get_flags() { MRN_DBUG_ENTER_FUNCTION(); ulonglong flags = mrn_generic_ft_ext_get_flags(); DBUG_RETURN(flags); } static ulonglong mrn_no_such_key_ft_ext_get_docid(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong id = GRN_ID_NIL; DBUG_RETURN(id); } static ulonglong mrn_no_such_key_ft_ext_count_matches(FT_INFO_EXT *handler) { MRN_DBUG_ENTER_FUNCTION(); ulonglong n_records = 0; DBUG_RETURN(n_records); } static _ft_vft_ext mrn_no_such_key_ft_vft_ext = { mrn_no_such_key_ft_ext_get_version, mrn_no_such_key_ft_ext_get_flags, mrn_no_such_key_ft_ext_get_docid, mrn_no_such_key_ft_ext_count_matches }; #endif /* handler implementation */ ha_mroonga::ha_mroonga(handlerton *hton, TABLE_SHARE *share_arg) :handler(hton, share_arg), wrap_handler(NULL), is_clone(false), parent_for_clone(NULL), mem_root_for_clone(NULL), record_id(GRN_ID_NIL), key_id(NULL), del_key_id(NULL), wrap_ft_init_count(0), share(NULL), wrap_key_info(NULL), base_key_info(NULL), analyzed_for_create(false), wrap_handler_for_create(NULL), #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX hnd_add_index(NULL), #endif #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER alter_key_info_buffer(NULL), #else wrap_alter_key_info(NULL), #endif mrn_lock_type(F_UNLCK), ctx_entity_(), ctx(&ctx_entity_), grn_table(NULL), grn_columns(NULL), grn_column_ranges(NULL), grn_index_tables(NULL), grn_index_columns(NULL), grn_source_column_geo(NULL), cursor_geo(NULL), cursor(NULL), index_table_cursor(NULL), empty_value_records(NULL), empty_value_records_cursor(NULL), sorted_result(NULL), matched_record_keys(NULL), blob_buffers(NULL), dup_key(0), count_skip(false), fast_order_limit(false), fast_order_limit_with_index(false), ignoring_duplicated_key(false), inserting_with_update(false), fulltext_searching(false), ignoring_no_key_columns(false), replacing_(false), written_by_row_based_binlog(0), current_ft_item(NULL), operations_(NULL) { MRN_DBUG_ENTER_METHOD(); grn_ctx_init(ctx, 0); mrn_change_encoding(ctx, system_charset_info); grn_ctx_use(ctx, mrn_db); GRN_WGS84_GEO_POINT_INIT(&top_left_point, 0); GRN_WGS84_GEO_POINT_INIT(&bottom_right_point, 0); GRN_WGS84_GEO_POINT_INIT(&source_point, 0); GRN_TEXT_INIT(&key_buffer, 0); GRN_TEXT_INIT(&encoded_key_buffer, 0); GRN_VOID_INIT(&old_value_buffer); GRN_VOID_INIT(&new_value_buffer); DBUG_VOID_RETURN; } ha_mroonga::~ha_mroonga() { MRN_DBUG_ENTER_METHOD(); delete operations_; if (analyzed_for_create) { if (wrap_handler_for_create) { delete wrap_handler_for_create; } if (share_for_create.wrapper_mode) { plugin_unlock(NULL, share_for_create.plugin); } if (share_for_create.table_name) { my_free(share_for_create.table_name); } mrn_free_share_alloc(&share_for_create); free_root(&mem_root_for_create, MYF(0)); } if (blob_buffers) { delete [] blob_buffers; } grn_obj_unlink(ctx, &top_left_point); grn_obj_unlink(ctx, &bottom_right_point); grn_obj_unlink(ctx, &source_point); grn_obj_unlink(ctx, &key_buffer); grn_obj_unlink(ctx, &encoded_key_buffer); grn_obj_unlink(ctx, &old_value_buffer); grn_obj_unlink(ctx, &new_value_buffer); grn_ctx_fin(ctx); DBUG_VOID_RETURN; } const char *ha_mroonga::table_type() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(MRN_PLUGIN_NAME_STRING); } const char *ha_mroonga::index_type(uint key_nr) { MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->s->key_info[key_nr]); if (key_info->algorithm == HA_KEY_ALG_FULLTEXT) { DBUG_RETURN("FULLTEXT"); } else if (key_info->algorithm == HA_KEY_ALG_HASH) { DBUG_RETURN("HASH"); } else { DBUG_RETURN("BTREE"); } } static const char *ha_mroonga_exts[] = { NullS }; const char **ha_mroonga::bas_ext() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(ha_mroonga_exts); } uint ha_mroonga::wrapper_max_supported_record_length() const { uint res; MRN_DBUG_ENTER_METHOD(); if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrap_handler_for_create->max_supported_record_length(); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->max_supported_record_length(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(res); } uint ha_mroonga::storage_max_supported_record_length() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(HA_MAX_REC_LENGTH); } uint ha_mroonga::max_supported_record_length() const { MRN_DBUG_ENTER_METHOD(); uint res; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrapper_max_supported_record_length(); } else if (wrap_handler && share && share->wrapper_mode) { res = wrapper_max_supported_record_length(); } else { res = storage_max_supported_record_length(); } DBUG_RETURN(res); } uint ha_mroonga::wrapper_max_supported_keys() const { uint res; MRN_DBUG_ENTER_METHOD(); if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrap_handler_for_create->max_supported_keys(); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->max_supported_keys(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(res); } uint ha_mroonga::storage_max_supported_keys() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(HA_MAX_REC_LENGTH); } uint ha_mroonga::max_supported_keys() const { MRN_DBUG_ENTER_METHOD(); uint res; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrapper_max_supported_keys(); } else if (wrap_handler && share && share->wrapper_mode) { res = wrapper_max_supported_keys(); } else { res = storage_max_supported_keys(); } DBUG_RETURN(res); } uint ha_mroonga::wrapper_max_supported_key_length() const { uint res; MRN_DBUG_ENTER_METHOD(); if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrap_handler_for_create->max_supported_key_length(); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->max_supported_key_length(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(res); } uint ha_mroonga::storage_max_supported_key_length() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(GRN_TABLE_MAX_KEY_SIZE); } uint ha_mroonga::max_supported_key_length() const { MRN_DBUG_ENTER_METHOD(); uint res; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrapper_max_supported_key_length(); } else if (wrap_handler && share && share->wrapper_mode) { res = wrapper_max_supported_key_length(); } else { res = storage_max_supported_key_length(); } DBUG_RETURN(res); } uint ha_mroonga::wrapper_max_supported_key_part_length() const { uint res; MRN_DBUG_ENTER_METHOD(); if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrap_handler_for_create->max_supported_key_part_length(); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->max_supported_key_part_length(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(res); } uint ha_mroonga::storage_max_supported_key_part_length() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(GRN_TABLE_MAX_KEY_SIZE); } uint ha_mroonga::max_supported_key_part_length() const { MRN_DBUG_ENTER_METHOD(); uint res; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { res = wrapper_max_supported_key_part_length(); } else if (wrap_handler && share && share->wrapper_mode) { res = wrapper_max_supported_key_part_length(); } else { res = storage_max_supported_key_part_length(); } DBUG_RETURN(res); } ulonglong ha_mroonga::wrapper_table_flags() const { ulonglong table_flags; MRN_DBUG_ENTER_METHOD(); if (analyzed_for_create && share_for_create.wrapper_mode) { table_flags = wrap_handler_for_create->ha_table_flags(); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); table_flags = wrap_handler->ha_table_flags(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } table_flags |= HA_CAN_FULLTEXT | HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | HA_CAN_RTREEKEYS | HA_REC_NOT_IN_SEQ; #ifdef HA_CAN_REPAIR table_flags |= HA_CAN_REPAIR; #endif #ifdef HA_CAN_FULLTEXT_EXT table_flags |= HA_CAN_FULLTEXT_EXT; #endif #ifdef HA_GENERATED_COLUMNS table_flags |= HA_GENERATED_COLUMNS; #endif #ifdef HA_CAN_VIRTUAL_COLUMNS table_flags |= HA_CAN_VIRTUAL_COLUMNS; #endif table_flags |= HA_CAN_HASH_KEYS; DBUG_RETURN(table_flags); } ulonglong ha_mroonga::storage_table_flags() const { MRN_DBUG_ENTER_METHOD(); ulonglong flags = HA_NO_TRANSACTIONS | HA_PARTIAL_COLUMN_READ | HA_REC_NOT_IN_SEQ | HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_STATS_RECORDS_IS_EXACT | HA_CAN_FULLTEXT | HA_BINLOG_FLAGS | HA_CAN_BIT_FIELD | HA_DUPLICATE_POS | HA_CAN_GEOMETRY | HA_CAN_RTREEKEYS; //HA_HAS_RECORDS; #ifdef HA_MUST_USE_TABLE_CONDITION_PUSHDOWN flags |= HA_MUST_USE_TABLE_CONDITION_PUSHDOWN; #endif #ifdef HA_CAN_REPAIR flags |= HA_CAN_REPAIR; #endif #ifdef HA_CAN_FULLTEXT_EXT flags |= HA_CAN_FULLTEXT_EXT; #endif #ifdef HA_GENERATED_COLUMNS flags |= HA_GENERATED_COLUMNS; #endif #ifdef HA_CAN_VIRTUAL_COLUMNS flags |= HA_CAN_VIRTUAL_COLUMNS; #endif flags |= HA_CAN_HASH_KEYS; DBUG_RETURN(flags); } ulonglong ha_mroonga::table_flags() const { MRN_DBUG_ENTER_METHOD(); ulonglong flags; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { flags = wrapper_table_flags(); } else if (wrap_handler && share && share->wrapper_mode) { flags = wrapper_table_flags(); } else { flags = storage_table_flags(); } DBUG_RETURN(flags); } ulong ha_mroonga::wrapper_index_flags(uint idx, uint part, bool all_parts) const { ulong index_flags; KEY *key = &(table_share->key_info[idx]); MRN_DBUG_ENTER_METHOD(); if (key->algorithm == HA_KEY_ALG_BTREE || key->algorithm == HA_KEY_ALG_UNDEF) { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); index_flags = wrap_handler->index_flags(idx, part, all_parts); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } else { index_flags = HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR; } DBUG_RETURN(index_flags); } ulong ha_mroonga::storage_index_flags(uint idx, uint part, bool all_parts) const { MRN_DBUG_ENTER_METHOD(); ulong flags; KEY *key = &(table_share->key_info[idx]); if (key->algorithm == HA_KEY_ALG_BTREE || key->algorithm == HA_KEY_ALG_UNDEF) { flags = HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE; bool need_normalize_p = false; // TODO: MariaDB 10.1 passes key->user_defined_key_parts as part // for ORDER BY DESC. We just it fallback to part = 0. We may use // it for optimization in the future. // // See also: test_if_order_by_key() in sql/sql_select.cc. if (KEY_N_KEY_PARTS(key) == part) { part = 0; } Field *field = &(key->key_part[part].field[0]); if (field && (have_custom_normalizer(key) || should_normalize(field))) { need_normalize_p = true; } if (!need_normalize_p) { flags |= HA_KEYREAD_ONLY; } if (KEY_N_KEY_PARTS(key) > 1 || !need_normalize_p) { flags |= HA_READ_ORDER; } } else { flags = HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR; } DBUG_RETURN(flags); } ulong ha_mroonga::index_flags(uint idx, uint part, bool all_parts) const { MRN_DBUG_ENTER_METHOD(); KEY *key = &(table_share->key_info[idx]); if (key->algorithm == HA_KEY_ALG_FULLTEXT) { DBUG_RETURN(HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR); } if (mrn_is_geo_key(key)) { DBUG_RETURN(HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR | HA_READ_RANGE); } int error = 0; if (wrap_handler && share && share->wrapper_mode) { error = wrapper_index_flags(idx, part, all_parts); } else { error = storage_index_flags(idx, part, all_parts); } DBUG_RETURN(error); } int ha_mroonga::create_share_for_create() const { int error; THD *thd = ha_thd(); LEX *lex = thd->lex; HA_CREATE_INFO *create_info = &lex->create_info; TABLE_LIST *table_list = MRN_LEX_GET_TABLE_LIST(lex); MRN_DBUG_ENTER_METHOD(); wrap_handler_for_create = NULL; table_for_create.reset(); table_share_for_create.reset(); memset(&share_for_create, 0, sizeof(MRN_SHARE)); if (table_share) { table_share_for_create.comment = table_share->comment; table_share_for_create.connect_string = table_share->connect_string; } else { #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER if (thd_sql_command(ha_thd()) != SQLCOM_CREATE_INDEX) { #endif table_share_for_create.comment = create_info->comment; table_share_for_create.connect_string = create_info->connect_string; #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER } #endif if (thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX) { st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false); if (slot_data && slot_data->alter_create_info) { create_info = slot_data->alter_create_info; if (slot_data->alter_connect_string) { table_share_for_create.connect_string.str = slot_data->alter_connect_string; table_share_for_create.connect_string.length = strlen(slot_data->alter_connect_string); } else { table_share_for_create.connect_string.str = NULL; table_share_for_create.connect_string.length = 0; } if (slot_data->alter_comment) { table_share_for_create.comment.str = slot_data->alter_comment; table_share_for_create.comment.length = strlen(slot_data->alter_comment); } else { table_share_for_create.comment.str = NULL; table_share_for_create.comment.length = 0; } } } } mrn_init_alloc_root(&mem_root_for_create, 1024, 0, MYF(0)); analyzed_for_create = true; if (table_list) { share_for_create.table_name = mrn_my_strndup(table_list->table_name.str, table_list->table_name.length, MYF(MY_WME)); share_for_create.table_name_length = table_list->table_name.length; } share_for_create.table_share = &table_share_for_create; table_for_create.s = &table_share_for_create; #ifdef WITH_PARTITION_STORAGE_ENGINE table_for_create.part_info = NULL; #endif if ((error = mrn_parse_table_param(&share_for_create, &table_for_create))) goto error; if (share_for_create.wrapper_mode) { wrap_handler_for_create = share_for_create.hton->create(share_for_create.hton, NULL, &mem_root_for_create); if (!wrap_handler_for_create) { error = HA_ERR_OUT_OF_MEM; goto error; } wrap_handler_for_create->init(); } DBUG_RETURN(0); error: if (share_for_create.wrapper_mode) { plugin_unlock(NULL, share_for_create.plugin); } mrn_free_share_alloc(&share_for_create); free_root(&mem_root_for_create, MYF(0)); analyzed_for_create = false; thd->clear_error(); DBUG_RETURN(error); } int ha_mroonga::wrapper_create(const char *name, TABLE *table, HA_CREATE_INFO *info, MRN_SHARE *tmp_share) { int error = 0; handler *hnd; MRN_DBUG_ENTER_METHOD(); if (table_share->primary_key == MAX_KEY) { my_message(ER_REQUIRES_PRIMARY_KEY, MRN_GET_ERR_MSG(ER_REQUIRES_PRIMARY_KEY), MYF(0)); DBUG_RETURN(ER_REQUIRES_PRIMARY_KEY); } error = ensure_database_open(name); if (error) DBUG_RETURN(error); error = wrapper_create_index(name, table, tmp_share); if (error) DBUG_RETURN(error); wrap_key_info = mrn_create_key_info_for_table(tmp_share, table, &error); if (error) DBUG_RETURN(error); base_key_info = table->key_info; share = tmp_share; MRN_SET_WRAP_SHARE_KEY(tmp_share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_SUPPORT_CUSTOM_OPTIONS if (parse_engine_table_options(ha_thd(), tmp_share->hton, table->s)) { MRN_SET_BASE_SHARE_KEY(tmp_share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); share = NULL; if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; error = MRN_GET_ERROR_NUMBER; DBUG_RETURN(error); } #endif hnd = get_new_handler(table->s, current_thd->mem_root, tmp_share->hton); if (!hnd) { MRN_SET_BASE_SHARE_KEY(tmp_share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); share = NULL; if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; DBUG_RETURN(HA_ERR_OUT_OF_MEM); } error = hnd->ha_create(name, table, info); MRN_SET_BASE_SHARE_KEY(tmp_share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); share = NULL; delete hnd; if (error) { mrn::PathMapper mapper(name); generic_delete_table(name, mapper.table_name()); } if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; DBUG_RETURN(error); } int ha_mroonga::wrapper_create_index_fulltext_validate(KEY *key_info) { MRN_DBUG_ENTER_METHOD(); int error = 0; uint i; for (i = 0; i < KEY_N_KEY_PARTS(key_info); i++) { Field *field = key_info->key_part[i].field; grn_builtin_type gtype = mrn_grn_type_from_field(ctx, field, true); if (gtype != GRN_DB_SHORT_TEXT) { error = ER_CANT_CREATE_TABLE; GRN_LOG(ctx, GRN_LOG_ERROR, "key type must be text: <%d> " "(TODO: We should show type name not type ID.)", field->type()); my_message(ER_CANT_CREATE_TABLE, "key type must be text. (TODO: We should show type name.)", MYF(0)); DBUG_RETURN(error); } } DBUG_RETURN(error); } int ha_mroonga::wrapper_create_index_fulltext(const char *grn_table_name, int i, KEY *key_info, grn_obj **index_tables, grn_obj **index_columns, MRN_SHARE *tmp_share) { MRN_DBUG_ENTER_METHOD(); int error = 0; error = wrapper_create_index_fulltext_validate(key_info); if (error) { DBUG_RETURN(error); } error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); grn_obj_flags index_table_flags = GRN_OBJ_TABLE_PAT_KEY | GRN_OBJ_PERSISTENT; grn_obj *index_table; grn_column_flags index_column_flags = GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT; if (!find_index_column_flags(key_info, &index_column_flags)) { index_column_flags |= GRN_OBJ_WITH_POSITION; if (KEY_N_KEY_PARTS(key_info) > 1) { index_column_flags |= GRN_OBJ_WITH_SECTION; } } mrn::SmartGrnObj lexicon_key_type(ctx, GRN_DB_SHORT_TEXT); error = mrn_change_encoding(ctx, key_info->key_part->field->charset()); if (error) { DBUG_RETURN(error); } mrn::IndexTableName index_table_name(grn_table_name, key_info->name.str); index_table = grn_table_create(ctx, index_table_name.c_str(), index_table_name.length(), NULL, index_table_flags, lexicon_key_type.get(), 0); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } mrn_change_encoding(ctx, system_charset_info); index_tables[i] = index_table; grn_obj *tokenizer = find_tokenizer(key_info, tmp_share, i); if (tokenizer) { grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER; grn_obj_set_info(ctx, index_table, info_type, tokenizer); grn_obj_unlink(ctx, tokenizer); } { grn_obj token_filters; GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, 0); if (find_token_filters(key_info, &token_filters)) { grn_obj_set_info(ctx, index_table, GRN_INFO_TOKEN_FILTERS, &token_filters); } grn_obj_unlink(ctx, &token_filters); } if (have_custom_normalizer(key_info) || should_normalize(&key_info->key_part->field[0])) { grn_info_type info_type = GRN_INFO_NORMALIZER; grn_obj *normalizer = find_normalizer(key_info); if (normalizer) { grn_obj_set_info(ctx, index_table, info_type, normalizer); grn_obj_unlink(ctx, normalizer); } } grn_obj *index_column = grn_column_create(ctx, index_table, INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME), NULL, index_column_flags, grn_table); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (index_columns) { index_columns[i] = index_column; } else { grn_obj_unlink(ctx, index_column); } DBUG_RETURN(error); } int ha_mroonga::wrapper_create_index_geo(const char *grn_table_name, int i, KEY *key_info, grn_obj **index_tables, grn_obj **index_columns, MRN_SHARE *tmp_share) { MRN_DBUG_ENTER_METHOD(); int error; error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); mrn::IndexTableName index_table_name(grn_table_name, key_info->name.str); grn_obj_flags index_table_flags = GRN_OBJ_TABLE_PAT_KEY | GRN_OBJ_PERSISTENT; grn_obj *index_table; grn_obj_flags index_column_flags = GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT; grn_obj *lexicon_key_type = grn_ctx_at(ctx, GRN_DB_WGS84_GEO_POINT); index_table = grn_table_create(ctx, index_table_name.c_str(), index_table_name.length(), NULL, index_table_flags, lexicon_key_type, 0); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0)); grn_obj_unlink(ctx, lexicon_key_type); DBUG_RETURN(error); } grn_obj_unlink(ctx, lexicon_key_type); index_tables[i] = index_table; grn_obj *index_column = grn_column_create(ctx, index_table, INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME), NULL, index_column_flags, grn_table); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (index_columns) { index_columns[i] = index_column; } else { grn_obj_unlink(ctx, index_column); } DBUG_RETURN(error); } int ha_mroonga::wrapper_create_index(const char *name, TABLE *table, MRN_SHARE *tmp_share) { MRN_DBUG_ENTER_METHOD(); int error = 0; error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); grn_obj *grn_index_table; mrn::PathMapper mapper(name); const char *grn_table_name = mapper.table_name(); char *grn_table_path = NULL; // we don't specify path grn_obj *pkey_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT); grn_obj *pkey_value_type = NULL; // we don't use this grn_obj_flags grn_table_flags = GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_HASH_KEY; grn_index_table = grn_table_create(ctx, grn_table_name, strlen(grn_table_name), grn_table_path, grn_table_flags, pkey_type, pkey_value_type); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (grn_table) { grn_obj_unlink(ctx, grn_table); } grn_table = grn_index_table; uint i; uint n_keys = table->s->keys; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys); if (!tmp_share->disable_keys) { for (i = 0; i < n_keys; i++) { index_tables[i] = NULL; KEY *key_info = &(table->s->key_info[i]); if (key_info->algorithm == HA_KEY_ALG_FULLTEXT) { error = wrapper_create_index_fulltext(grn_table_name, i, key_info, index_tables, NULL, tmp_share); } else if (mrn_is_geo_key(key_info)) { error = wrapper_create_index_geo(grn_table_name, i, key_info, index_tables, NULL, tmp_share); } } if (error) { for (uint j = 0; j < i; j++) { if (index_tables[j]) { grn_obj_remove(ctx, index_tables[j]); } } grn_obj_remove(ctx, grn_table); grn_table = NULL; } } MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); DBUG_RETURN(error); } int ha_mroonga::storage_create(const char *name, TABLE *table, HA_CREATE_INFO *info, MRN_SHARE *tmp_share) { int error; MRN_LONG_TERM_SHARE *long_term_share = tmp_share->long_term_share; MRN_DBUG_ENTER_METHOD(); if (info->auto_increment_value) { mrn::Lock lock(&long_term_share->auto_inc_mutex); long_term_share->auto_inc_value = info->auto_increment_value; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); long_term_share->auto_inc_inited = true; } error = storage_create_validate_pseudo_column(table); if (error) DBUG_RETURN(error); error = storage_create_validate_index(table); if (error) DBUG_RETURN(error); error = ensure_database_open(name); if (error) DBUG_RETURN(error); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); grn_obj_flags table_flags = GRN_OBJ_PERSISTENT; /* primary key must be handled before creating table */ grn_obj *pkey_type; uint pkey_nr = table->s->primary_key; if (pkey_nr != MAX_INDEXES) { KEY *key_info = &(table->s->key_info[pkey_nr]); bool is_id; int key_parts = KEY_N_KEY_PARTS(key_info); if (key_parts == 1) { Field *pkey_field = key_info->key_part[0].field; const char *column_name = pkey_field->field_name.str; is_id = (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0); grn_builtin_type gtype = mrn_grn_type_from_field(ctx, pkey_field, false); pkey_type = grn_ctx_at(ctx, gtype); } else { is_id = false; pkey_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT); } // default algorithm is BTREE ==> PAT if (!is_id && key_info->algorithm == HA_KEY_ALG_HASH) { table_flags |= GRN_OBJ_TABLE_HASH_KEY; } else if (!is_id) { table_flags |= GRN_OBJ_TABLE_PAT_KEY; } else { // for _id table_flags |= GRN_OBJ_TABLE_NO_KEY; pkey_type = NULL; } } else { // primary key doesn't exists table_flags |= GRN_OBJ_TABLE_NO_KEY; pkey_type = NULL; } /* create table */ grn_obj *table_obj; mrn::PathMapper mapper(name); char *table_path = NULL; // we don't specify path grn_obj *pkey_value_type = NULL; // we don't use this table_obj = grn_table_create(ctx, mapper.table_name(), strlen(mapper.table_name()), table_path, table_flags, pkey_type, pkey_value_type); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (table_flags == (GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_PAT_KEY) || table_flags == (GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_HASH_KEY)) { KEY *key_info = &(table->s->key_info[pkey_nr]); int key_parts = KEY_N_KEY_PARTS(key_info); if (key_parts == 1) { grn_obj *normalizer = NULL; if (tmp_share->normalizer) { normalizer = grn_ctx_get(ctx, tmp_share->normalizer, tmp_share->normalizer_length); } else { Field *field = &(key_info->key_part->field[0]); if (should_normalize(field)) { normalizer = find_normalizer(key_info); } } if (normalizer) { grn_info_type info_type = GRN_INFO_NORMALIZER; grn_obj_set_info(ctx, table_obj, info_type, normalizer); grn_obj_unlink(ctx, normalizer); } if (tmp_share->default_tokenizer) { grn_obj *default_tokenizer = grn_ctx_get(ctx, tmp_share->default_tokenizer, tmp_share->default_tokenizer_length); if (default_tokenizer) { grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER; grn_obj_set_info(ctx, table_obj, info_type, default_tokenizer); grn_obj_unlink(ctx, default_tokenizer); } } if (tmp_share->token_filters) { grn_obj token_filters; GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, 0); if (find_token_filters_fill(&token_filters, tmp_share->token_filters, tmp_share->token_filters_length)) { grn_obj_set_info(ctx, table_obj, GRN_INFO_TOKEN_FILTERS, &token_filters); } grn_obj_unlink(ctx, &token_filters); } } } /* create columns */ uint n_columns = table->s->fields; for (uint i = 0; i < n_columns; i++) { Field *field = table->s->field[i]; mrn::ColumnName column_name(field->field_name); if (strcmp(MRN_COLUMN_NAME_ID, column_name.mysql_name()) == 0) { continue; } #ifdef MRN_SUPPORT_FOREIGN_KEYS if (storage_create_foreign_key(table, mapper.table_name(), field, table_obj, error)) { continue; } if (error) { grn_obj_remove(ctx, table_obj); DBUG_RETURN(error); } #endif #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif grn_obj_flags col_flags = GRN_OBJ_PERSISTENT; if (!find_column_flags(field, tmp_share, i, &col_flags)) { col_flags |= GRN_OBJ_COLUMN_SCALAR; } grn_obj *col_type; { int column_type_error_code = ER_CANT_CREATE_TABLE; col_type = find_column_type(field, tmp_share, i, column_type_error_code); if (!col_type) { grn_obj_remove(ctx, table_obj); DBUG_RETURN(column_type_error_code); } } char *col_path = NULL; // we don't specify path grn_column_create(ctx, table_obj, column_name.c_str(), column_name.length(), col_path, col_flags, col_type); if (ctx->rc) { error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); grn_obj_remove(ctx, table_obj); DBUG_RETURN(error); } } error = storage_create_indexes(table, mapper.table_name(), table_obj, tmp_share); if (error) { grn_obj_remove(ctx, table_obj); table_obj = NULL; } if (table_obj) { grn_obj_unlink(ctx, table_obj); } DBUG_RETURN(error); } int ha_mroonga::storage_create_validate_pseudo_column(TABLE *table) { int error = 0; uint i, n_columns; MRN_DBUG_ENTER_METHOD(); n_columns = table->s->fields; for (i = 0; i < n_columns; i++) { Field *field = table->s->field[i]; const char *column_name = field->field_name.str; if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) { switch (field->type()) { case MYSQL_TYPE_TINY : case MYSQL_TYPE_SHORT : case MYSQL_TYPE_INT24 : case MYSQL_TYPE_LONG : case MYSQL_TYPE_LONGLONG : break; default: GRN_LOG(ctx, GRN_LOG_ERROR, "_id must be numeric data type"); error = ER_CANT_CREATE_TABLE; my_message(error, "_id must be numeric data type", MYF(0)); DBUG_RETURN(error); } } } DBUG_RETURN(error); } #ifdef MRN_SUPPORT_FOREIGN_KEYS bool ha_mroonga::storage_create_foreign_key(TABLE *table, const char *grn_table_name, Field *field, grn_obj *table_obj, int &error) { MRN_DBUG_ENTER_METHOD(); LEX *lex = ha_thd()->lex; Alter_info *alter_info = &lex->alter_info; List_iterator key_iterator(alter_info->key_list); Key *key; char ref_db_buff[NAME_LEN + 1], ref_table_buff[NAME_LEN + 1]; while ((key = key_iterator++)) { if (key->type != MRN_KEYTYPE_FOREIGN) { continue; } if (key->columns.elements > 1) { error = ER_CANT_CREATE_TABLE; my_message(error, "mroonga can't use FOREIGN_KEY with multiple columns", MYF(0)); DBUG_RETURN(false); } List_iterator key_part_col_iterator(key->columns); Key_part_spec *key_part_col = key_part_col_iterator++; LEX_CSTRING field_name = key_part_col->field_name; DBUG_PRINT("info", ("mroonga: field_name=%s", field_name.str)); DBUG_PRINT("info", ("mroonga: field->field_name=%s", field->field_name.str)); if (strcmp(field->field_name.str, field_name.str)) { continue; } Foreign_key *fk = (Foreign_key *) key; List_iterator key_part_ref_col_iterator(fk->ref_columns); Key_part_spec *key_part_ref_col = key_part_ref_col_iterator++; LEX_CSTRING ref_field_name = key_part_ref_col->field_name; DBUG_PRINT("info", ("mroonga: ref_field_name=%s", ref_field_name.str)); #ifdef MRN_FOREIGN_KEY_USE_CONST_STRING LEX_CSTRING ref_db_name = fk->ref_db; #else LEX_STRING ref_db_name = fk->ref_db; #endif DBUG_PRINT("info", ("mroonga: ref_db_name=%s", ref_db_name.str)); if (ref_db_name.str && lower_case_table_names) { strmake(ref_db_buff, ref_db_name.str, sizeof(ref_db_buff) - 1); my_casedn_str(system_charset_info, ref_db_buff); ref_db_name.str = ref_db_buff; DBUG_PRINT("info", ("mroonga: casedn ref_db_name=%s", ref_db_name.str)); } #ifdef MRN_FOREIGN_KEY_USE_CONST_STRING LEX_CSTRING ref_table_name = fk->ref_table; #else LEX_STRING ref_table_name = fk->ref_table; #endif DBUG_PRINT("info", ("mroonga: ref_table_name=%s", ref_table_name.str)); if (ref_table_name.str && lower_case_table_names) { strmake(ref_table_buff, ref_table_name.str, sizeof(ref_table_buff) - 1); my_casedn_str(system_charset_info, ref_table_buff); ref_table_name.str = ref_table_buff; DBUG_PRINT("info", ("mroonga: casedn ref_table_name=%s", ref_table_name.str)); } if (ref_db_name.str && strcmp(table->s->db.str, ref_db_name.str)) { error = ER_CANT_CREATE_TABLE; my_message(error, "mroonga can't use FOREIGN_KEY during different database tables", MYF(0)); DBUG_RETURN(false); } grn_obj *column, *column_ref = NULL, *grn_table_ref = NULL; char ref_path[FN_REFLEN + 1]; TABLE_LIST table_list; TABLE_SHARE *tmp_ref_table_share; build_table_filename(ref_path, sizeof(ref_path) - 1, table->s->db.str, ref_table_name.str, "", 0); DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path)); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(false); mrn::PathMapper mapper(ref_path); grn_table_ref = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name())); if (!grn_table_ref) { error = ER_CANT_CREATE_TABLE; char err_msg[MRN_BUFFER_SIZE]; sprintf(err_msg, "reference table [%s.%s] is not mroonga table", table->s->db.str, ref_table_name.str); my_message(error, err_msg, MYF(0)); DBUG_RETURN(false); } LEX_CSTRING tmp_db_name= { mapper.db_name(), strlen(mapper.db_name()) }; LEX_CSTRING tmp_table_name= { mapper.mysql_table_name(), strlen(mapper.mysql_table_name()) }; table_list.init_one_table(&tmp_db_name, &tmp_table_name, 0, TL_WRITE); mrn_open_mutex_lock(table->s); tmp_ref_table_share = mrn_create_tmp_table_share(&table_list, ref_path, &error); mrn_open_mutex_unlock(table->s); if (!tmp_ref_table_share) { grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; char err_msg[MRN_BUFFER_SIZE]; sprintf(err_msg, "reference table [%s.%s] is not found", table->s->db.str, ref_table_name.str); my_message(error, err_msg, MYF(0)); DBUG_RETURN(false); } uint ref_pkey_nr = tmp_ref_table_share->primary_key; if (ref_pkey_nr == MAX_KEY) { mrn_open_mutex_lock(table->s); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table->s); grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; char err_msg[MRN_BUFFER_SIZE]; sprintf(err_msg, "reference table [%s.%s] has no primary key", table->s->db.str, ref_table_name.str); my_message(error, err_msg, MYF(0)); DBUG_RETURN(false); } KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr]; uint ref_key_parts = KEY_N_KEY_PARTS(ref_key_info); if (ref_key_parts > 1) { mrn_open_mutex_lock(table->s); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table->s); grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; char err_msg[MRN_BUFFER_SIZE]; sprintf(err_msg, "reference table [%s.%s] primary key is multiple column", table->s->db.str, ref_table_name.str); my_message(error, err_msg, MYF(0)); DBUG_RETURN(false); } Field *ref_field = &ref_key_info->key_part->field[0]; if (strcmp(ref_field->field_name.str, ref_field_name.str)) { mrn_open_mutex_lock(table->s); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table->s); grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; char err_msg[MRN_BUFFER_SIZE]; sprintf(err_msg, "reference column [%s.%s.%s] is not used for primary key", table->s->db.str, ref_table_name.str, ref_field_name.str); my_message(error, err_msg, MYF(0)); DBUG_RETURN(false); } mrn_open_mutex_lock(table->s); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table->s); grn_obj_flags col_flags = GRN_OBJ_PERSISTENT; column = grn_column_create(ctx, table_obj, field->field_name.str, field->field_name.length, NULL, col_flags, grn_table_ref); if (ctx->rc) { grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(false); } mrn::IndexColumnName index_column_name(grn_table_name, field->field_name.str); grn_obj_flags ref_col_flags = GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT; column_ref = grn_column_create(ctx, grn_table_ref, index_column_name.c_str(), index_column_name.length(), NULL, ref_col_flags, table_obj); if (ctx->rc) { grn_obj_unlink(ctx, column); grn_obj_unlink(ctx, grn_table_ref); error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(false); } grn_obj source_ids; grn_id source_id = grn_obj_id(ctx, column); GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); GRN_UINT32_PUT(ctx, &source_ids, source_id); if (error) { grn_obj_unlink(ctx, &source_ids); grn_obj_unlink(ctx, column_ref); grn_obj_unlink(ctx, column); grn_obj_unlink(ctx, grn_table_ref); DBUG_RETURN(false); } grn_obj_set_info(ctx, column_ref, GRN_INFO_SOURCE, &source_ids); grn_obj_unlink(ctx, &source_ids); grn_obj_unlink(ctx, column_ref); grn_obj_unlink(ctx, column); grn_obj_unlink(ctx, grn_table_ref); error = 0; DBUG_RETURN(true); } error = 0; DBUG_RETURN(false); } #endif int ha_mroonga::storage_create_validate_index(TABLE *table) { int error = 0; uint i; MRN_DBUG_ENTER_METHOD(); /* checking if index is used for virtual columns */ uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->s->key_info[i]); // must be single column key int key_parts = KEY_N_KEY_PARTS(key_info); for (int j = 0; j < key_parts; j++) { if (key_info->key_part[j].key_part_flag & HA_REVERSE_SORT) { GRN_LOG(ctx, GRN_LOG_ERROR, "DESC indexes are not supported"); error = ER_CANT_CREATE_TABLE; my_message(error, "DESC indexes are not supported", MYF(0)); DBUG_RETURN(error); } } if (key_parts != 1) { continue; } Field *field = key_info->key_part[0].field; const char *column_name = field->field_name.str; if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) { if (key_info->algorithm == HA_KEY_ALG_HASH) { continue; // hash index is ok } GRN_LOG(ctx, GRN_LOG_ERROR, "only hash index can be defined for _id"); error = ER_CANT_CREATE_TABLE; my_message(error, "only hash index can be defined for _id", MYF(0)); DBUG_RETURN(error); } } DBUG_RETURN(error); } int ha_mroonga::storage_create_index_table(TABLE *table, const char *grn_table_name, grn_obj *grn_table, MRN_SHARE *tmp_share, KEY *key_info, grn_obj **index_tables, uint i) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj *index_type; grn_obj *index_table; grn_obj_flags index_table_flags = GRN_OBJ_PERSISTENT; bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1; if (tmp_share->index_table && tmp_share->index_table[i]) { index_table = grn_ctx_get(ctx, tmp_share->index_table[i], tmp_share->index_table_length[i]); // TODO: add error check index_tables[i] = index_table; DBUG_RETURN(error); } if (is_multiple_column_index) { index_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT); } else { Field *field = key_info->key_part[0].field; grn_builtin_type groonga_type = mrn_grn_type_from_field(ctx, field, true); index_type = grn_ctx_at(ctx, groonga_type); } // TODO: Add NULL check for index_type int key_alg = key_info->algorithm; if (key_info->flags & HA_FULLTEXT) { index_table_flags |= GRN_OBJ_TABLE_PAT_KEY; error = mrn_change_encoding(ctx, key_info->key_part->field->charset()); if (error) { grn_obj_remove(ctx, grn_table); DBUG_RETURN(error); } } else if (key_alg == HA_KEY_ALG_HASH) { index_table_flags |= GRN_OBJ_TABLE_HASH_KEY; } else { index_table_flags |= GRN_OBJ_TABLE_PAT_KEY; } { mrn::IndexTableName index_table_name(grn_table_name, key_info->name.str); index_table = grn_table_create(ctx, index_table_name.c_str(), index_table_name.length(), NULL, index_table_flags, index_type, NULL); } if (ctx->rc) { grn_obj_unlink(ctx, index_type); grn_obj_remove(ctx, grn_table); error = ER_CANT_CREATE_TABLE; my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (key_info->flags & HA_FULLTEXT) { grn_obj *tokenizer = find_tokenizer(key_info, tmp_share, i); if (tokenizer) { grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER; grn_obj_set_info(ctx, index_table, info_type, tokenizer); grn_obj_unlink(ctx, tokenizer); } { grn_obj token_filters; GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, 0); if (find_token_filters(key_info, &token_filters)) { grn_obj_set_info(ctx, index_table, GRN_INFO_TOKEN_FILTERS, &token_filters); } grn_obj_unlink(ctx, &token_filters); } } { grn_obj *normalizer = NULL; Field *field = &(key_info->key_part->field[0]); if (key_info->flags & HA_FULLTEXT) { if (have_custom_normalizer(key_info) || should_normalize(field)) { normalizer = find_normalizer(key_info); } } else if (key_alg != HA_KEY_ALG_HASH) { if (!is_multiple_column_index && (have_custom_normalizer(key_info) || should_normalize(field))) { normalizer = find_normalizer(key_info); } } if (normalizer) { grn_info_type info_type = GRN_INFO_NORMALIZER; grn_obj_set_info(ctx, index_table, info_type, normalizer); grn_obj_unlink(ctx, normalizer); } } index_tables[i] = index_table; DBUG_RETURN(error); } int ha_mroonga::storage_create_index(TABLE *table, const char *grn_table_name, grn_obj *grn_table, MRN_SHARE *tmp_share, KEY *key_info, grn_obj **index_tables, grn_obj **index_columns, uint i) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj *index_column; bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1; if (!is_multiple_column_index) { Field *field = key_info->key_part[0].field; if (strcmp(MRN_COLUMN_NAME_ID, field->field_name.str) == 0) { // skipping _id virtual column DBUG_RETURN(0); } if (is_foreign_key_field(table->s->table_name.str, field->field_name.str)) { DBUG_RETURN(0); } #ifdef HA_CAN_VIRTUAL_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "mroonga: storage: failed to create index: " ER_MRN_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN_STR, field->field_name.str); error = ER_MRN_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN_NUM; my_message(error, error_message, MYF(0)); DBUG_RETURN(error); } } else { int j, n_key_parts = KEY_N_KEY_PARTS(key_info); for (j = 0; j < n_key_parts; j++) { Field *field = key_info->key_part[j].field; if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "mroonga: storage: failed to create index: " ER_MRN_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN_STR, field->field_name.str); error = ER_MRN_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN_NUM; my_message(error, error_message, MYF(0)); DBUG_RETURN(error); } } #endif } error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); error = storage_create_index_table(table, grn_table_name, grn_table, tmp_share, key_info, index_tables, i); if (error) DBUG_RETURN(error); grn_obj *index_table = index_tables[i]; grn_column_flags index_column_flags = GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT; if (!find_index_column_flags(key_info, &index_column_flags)) { grn_obj *tokenizer = grn_obj_get_info(ctx, index_table, GRN_INFO_DEFAULT_TOKENIZER, NULL); if (tokenizer) { index_column_flags |= GRN_OBJ_WITH_POSITION; } if (is_multiple_column_index && (key_info->flags & HA_FULLTEXT)) { index_column_flags |= GRN_OBJ_WITH_SECTION; } } const char *index_column_name; if (tmp_share->index_table && tmp_share->index_table[i]) { index_column_name = key_info->name.str; } else { index_column_name = INDEX_COLUMN_NAME; } index_column = grn_column_create(ctx, index_table, index_column_name, strlen(index_column_name), NULL, index_column_flags, grn_table); if (ctx->rc) { grn_obj_remove(ctx, index_table); error = ER_CANT_CREATE_TABLE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } mrn_change_encoding(ctx, system_charset_info); if (is_multiple_column_index) { if (key_info->flags & HA_FULLTEXT) { grn_obj source_ids; GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); int j, n_key_parts = KEY_N_KEY_PARTS(key_info); for (j = 0; j < n_key_parts; j++) { Field *field = key_info->key_part[j].field; mrn::ColumnName column_name(field->field_name); grn_obj *source_column = grn_obj_column(ctx, grn_table, column_name.c_str(), column_name.length()); grn_id source_id = grn_obj_id(ctx, source_column); GRN_UINT32_PUT(ctx, &source_ids, source_id); grn_obj_unlink(ctx, source_column); } mrn_change_encoding(ctx, key_info->key_part->field->charset()); grn_obj_set_info(ctx, index_column, GRN_INFO_SOURCE, &source_ids); grn_obj_unlink(ctx, &source_ids); } } else { Field *field = key_info->key_part[0].field; mrn::ColumnName column_name(field->field_name); grn_obj *column; column = grn_obj_column(ctx, grn_table, column_name.c_str(), column_name.length()); if (column) { grn_obj source_ids; grn_id source_id = grn_obj_id(ctx, column); GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); GRN_UINT32_PUT(ctx, &source_ids, source_id); mrn_change_encoding(ctx, key_info->key_part->field->charset()); grn_obj_set_info(ctx, index_column, GRN_INFO_SOURCE, &source_ids); grn_obj_unlink(ctx, &source_ids); grn_obj_unlink(ctx, column); } } mrn_change_encoding(ctx, system_charset_info); if (index_columns) { index_columns[i] = index_column; } DBUG_RETURN(error); } int ha_mroonga::storage_create_indexes(TABLE *table, const char *grn_table_name, grn_obj *grn_table, MRN_SHARE *tmp_share) { MRN_DBUG_ENTER_METHOD(); int error = 0; uint n_keys = table->s->keys; uint i; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys); for (i = 0; i < n_keys; i++) { index_tables[i] = NULL; if (i == table->s->primary_key) { continue; // pkey is already handled } KEY *key_info = &table->s->key_info[i]; if (tmp_share->disable_keys && !(key_info->flags & HA_NOSAME)) { continue; // key is disabled } if ((error = storage_create_index(table, grn_table_name, grn_table, tmp_share, key_info, index_tables, NULL, i))) { break; } } if (error) { while (true) { if (index_tables[i] && !(tmp_share->index_table && tmp_share->index_table[i])) { grn_obj_remove(ctx, index_tables[i]); } if (!i) break; i--; } } MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); DBUG_RETURN(error); } int ha_mroonga::ensure_database_open(const char *name, mrn::Database **db) { int error; MRN_DBUG_ENTER_METHOD(); if (db) *db = NULL; mrn::Database *local_db; error = mrn_db_manager->open(name, &local_db); if (error) DBUG_RETURN(error); if (db) *db = local_db; grn_ctx_use(ctx, local_db->get()); delete operations_; operations_ = new mrn::Operations(ctx); if (mrn_enable_operations_recording) { operations_->enable_recording(); } else { operations_->disable_recording(); } DBUG_RETURN(error); } int ha_mroonga::ensure_database_remove(const char *name) { int error; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); delete operations_; operations_ = NULL; mrn_db_manager->close(name); mrn::PathMapper mapper(name); remove_related_files(mapper.db_path()); DBUG_RETURN(error); } int ha_mroonga::create(const char *name, TABLE *table, HA_CREATE_INFO *info #ifdef MRN_HANDLER_CREATE_HAVE_TABLE_DEFINITION , dd::Table *table_def #endif ) { int error = 0; MRN_SHARE *tmp_share; MRN_DBUG_ENTER_METHOD(); /* checking data type of virtual columns */ if (!(tmp_share = mrn_get_share(name, table, &error))) DBUG_RETURN(error); st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), false); if (slot_data && slot_data->disable_keys_create_info == info) { tmp_share->disable_keys = true; } if (tmp_share->wrapper_mode) { error = wrapper_create(name, table, info, tmp_share); } else { error = storage_create(name, table, info, tmp_share); } if (error) { mrn_free_long_term_share(tmp_share->long_term_share); tmp_share->long_term_share = NULL; } else { error = add_wrap_hton(tmp_share->table_name, tmp_share->hton); } mrn_free_share(tmp_share); DBUG_RETURN(error); } int ha_mroonga::wrapper_open(const char *name, int mode, uint open_options) { int error = 0; MRN_DBUG_ENTER_METHOD(); mrn::Database *db = NULL; error = ensure_database_open(name, &db); if (error) DBUG_RETURN(error); if (!(open_options & HA_OPEN_FOR_REPAIR)) { error = open_table(name); if (error) DBUG_RETURN(error); error = wrapper_open_indexes(name); if (error) { grn_obj_unlink(ctx, grn_table); grn_table = NULL; DBUG_RETURN(error); } } mrn_init_alloc_root(&mem_root, 1024, 0, MYF(0)); wrap_key_info = mrn_create_key_info_for_table(share, table, &error); if (error) DBUG_RETURN(error); base_key_info = table->key_info; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (!is_clone) { wrap_handler = get_new_handler(table->s, &mem_root, share->hton); if (!wrap_handler) { MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; DBUG_RETURN(HA_ERR_OUT_OF_MEM); } #ifdef MRN_HANDLER_HAVE_SET_HA_SHARE_REF wrap_handler->set_ha_share_ref(&table->s->ha_share); #endif error = wrap_handler->ha_open(table, name, mode, open_options); } else { if (!(wrap_handler = parent_for_clone->wrap_handler->clone(name, mem_root_for_clone))) { MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; DBUG_RETURN(HA_ERR_OUT_OF_MEM); } } ref_length = wrap_handler->ref_length; key_used_on_scan = wrap_handler->key_used_on_scan; MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); init(); wrapper_overwrite_index_bits(); wrapper_set_keys_in_use(); pk_keypart_map = make_prev_keypart_map( KEY_N_KEY_PARTS(&(table->key_info[table_share->primary_key]))); if (!error) { if (open_options & HA_OPEN_FOR_REPAIR) { // TODO: How to check whether is DISABLE KEYS used or not? error = wrapper_recreate_indexes(ha_thd()); } else if (db) { mrn::Lock lock(&mrn_operations_mutex); mrn::PathMapper mapper(name); const char *table_name = mapper.table_name(); size_t table_name_size = strlen(table_name); if (db->is_broken_table(table_name, table_name_size)) { GRN_LOG(ctx, GRN_LOG_NOTICE, "Auto repair is started: <%s>", name); error = operations_->clear(table_name, table_name_size); if (!error) { db->mark_table_repaired(table_name, table_name_size); if (!share->disable_keys) { // TODO: implemented by "reindex" instead of "remove and recreate". // Because "remove and recreate" invalidates opened indexes by // other threads. error = wrapper_disable_indexes_mroonga(key_map(table->s->keys), false); if (!error) { error = wrapper_enable_indexes_mroonga(key_map(table->s->keys), false); } } } GRN_LOG(ctx, GRN_LOG_NOTICE, "Auto repair is done: <%s>: %s", name, error == 0 ? "success" : "failure"); } } } if (error) { grn_obj_unlink(ctx, grn_table); grn_table = NULL; // TODO: free indexes. delete wrap_handler; wrap_handler = NULL; if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; } DBUG_RETURN(error); } int ha_mroonga::wrapper_open_indexes(const char *name) { int error; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); uint n_keys = table->s->keys; uint n_primary_keys = table->s->primary_key; if (n_keys > 0) { // TODO: reduce allocate memories. We only need just // for HA_KEY_ALG_FULLTEXT keys. grn_index_tables = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys); grn_index_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys); } else { grn_index_tables = grn_index_columns = NULL; } mrn::PathMapper mapper(name); uint i = 0; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->s->key_info[i]); grn_index_tables[i] = NULL; grn_index_columns[i] = NULL; if (!(wrapper_is_target_index(key_info))) { continue; } if (i == n_primary_keys) { continue; } mrn::IndexTableName index_table_name(mapper.table_name(), key_info->name.str); grn_index_tables[i] = grn_ctx_get(ctx, index_table_name.c_str(), index_table_name.length()); if (ctx->rc == GRN_SUCCESS && !grn_index_tables[i]) { grn_index_tables[i] = grn_ctx_get(ctx, index_table_name.old_c_str(), index_table_name.old_length()); } if (ctx->rc) { DBUG_PRINT("info", ("mroonga: sql_command=%u", thd_sql_command(ha_thd()))); error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error; } grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i], INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME)); if (!grn_index_columns[i]) { /* just for backward compatibility before 1.0. */ Field *field = key_info->key_part[0].field; grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i], field->field_name.str, field->field_name.length); } if (ctx->rc) { DBUG_PRINT("info", ("mroonga: sql_command=%u", thd_sql_command(ha_thd()))); error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); grn_obj_unlink(ctx, grn_index_tables[i]); goto error; } } grn_bulk_space(ctx, &key_buffer, table->key_info->key_length); error: if (error) { while (i-- > 0) { grn_obj *index_column = grn_index_columns[i]; if (index_column) { grn_obj_unlink(ctx, index_column); } grn_obj *index_table = grn_index_tables[i]; if (index_table) { grn_obj_unlink(ctx, index_table); } } free(grn_index_columns); free(grn_index_tables); grn_index_columns = NULL; grn_index_tables = NULL; } DBUG_RETURN(error); } void ha_mroonga::wrapper_overwrite_index_bits() { uint i, j; longlong table_option = table_flags(); MRN_DBUG_ENTER_METHOD(); table_share->keys_for_keyread.clear_all(); for (i = 0; i < table_share->fields; i++) { Field *field = table_share->field[i]; field->part_of_key.clear_all(); #ifdef MRN_HAVE_MYSQL_FIELD_PART_OF_KEY_NOT_CLUSTERED field->part_of_key_not_clustered.clear_all(); #endif field->part_of_sortkey.clear_all(); /* TODO: We may need to update field->part_of_key_not_extended for MySQL >= 5.7.18. If users report "raw InnoDB can use index for this case but Mroonga wrapper mode for InnoDB can't use index for the same case", we'll reconsider it again. */ } for (i = 0; i < table_share->keys; i++) { KEY *key_info = &table->s->key_info[i]; KEY_PART_INFO *key_part = key_info->key_part; for (j = 0 ; j < KEY_N_KEY_PARTS(key_info); key_part++, j++) { Field *field = key_part->field; if (field->key_length() == key_part->length && !(field->flags & BLOB_FLAG)) { if (index_flags(i, j, 0) & HA_KEYREAD_ONLY) { table_share->keys_for_keyread.set_bit(i); field->part_of_key.set_bit(i); #ifdef MRN_HAVE_MYSQL_FIELD_PART_OF_KEY_NOT_CLUSTERED field->part_of_key_not_clustered.set_bit(i); #endif } if (index_flags(i, j, 1) & HA_READ_ORDER) field->part_of_sortkey.set_bit(i); } if (i == table_share->primary_key && (table_option & HA_PRIMARY_KEY_IN_READ_INDEX)) { if (field->key_length() == key_part->length && !(field->flags & BLOB_FLAG)) field->part_of_key = table_share->keys_in_use; if (field->part_of_sortkey.is_set(i)) field->part_of_sortkey = table_share->keys_in_use; } } } DBUG_VOID_RETURN; } int ha_mroonga::storage_reindex() { int error = 0; MRN_DBUG_ENTER_METHOD(); uint n_keys = table_share->keys; KEY *key_info = table->key_info; bool have_multiple_column_index = false; bitmap_clear_all(table->read_set); for (uint i = 0; i < n_keys; ++i) { if (!grn_index_columns[i]) continue; grn_hash *columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_OBJ_TABLE_HASH_KEY); grn_table_columns(ctx, grn_index_tables[i], NULL, 0, reinterpret_cast(columns)); unsigned int n_columns = grn_table_size(ctx, reinterpret_cast(columns)); grn_hash_close(ctx, columns); bool is_multiple_column_index = (KEY_N_KEY_PARTS(&(key_info[i])) != 1 && !(key_info[i].flags & HA_FULLTEXT)); if (n_columns == 1 || is_multiple_column_index) { grn_table_truncate(ctx, grn_index_tables[i]); if (ctx->rc != GRN_SUCCESS) { error = ER_ERROR_ON_WRITE; char error_message[MRN_MESSAGE_BUFFER_SIZE]; char index_table_name[GRN_TABLE_MAX_KEY_SIZE]; int index_table_name_size; index_table_name_size = grn_obj_name(ctx, grn_index_tables[i], index_table_name, GRN_TABLE_MAX_KEY_SIZE); snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "mroonga: reindex: failed to truncate index table: " "<%.*s>: <%s>(%d)", index_table_name_size, index_table_name, ctx->errbuf, ctx->rc); my_message(error, error_message, MYF(0)); break; } } if (is_multiple_column_index) { mrn_set_bitmap_by_key(table->read_set, &key_info[i]); have_multiple_column_index = true; } else { grn_obj_reindex(ctx, grn_index_columns[i]); if (ctx->rc != GRN_SUCCESS) { error = ER_ERROR_ON_WRITE; char error_message[MRN_MESSAGE_BUFFER_SIZE]; char index_column_name[GRN_TABLE_MAX_KEY_SIZE]; int index_column_name_size; index_column_name_size = grn_obj_name(ctx, grn_index_columns[i], index_column_name, GRN_TABLE_MAX_KEY_SIZE); snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "mroonga: reindex: failed to reindex: " "<%.*s>: <%s>(%d)", index_column_name_size, index_column_name, ctx->errbuf, ctx->rc); my_message(error, error_message, MYF(0)); break; } } } if (!error && have_multiple_column_index) error = storage_add_index_multiple_columns(key_info, n_keys, grn_index_tables, grn_index_columns, false); bitmap_set_all(table->read_set); DBUG_RETURN(error); } int ha_mroonga::storage_open(const char *name, int mode, uint open_options) { int error = 0; MRN_DBUG_ENTER_METHOD(); mrn::Database *db; error = ensure_database_open(name, &db); if (error) DBUG_RETURN(error); error = open_table(name); if (error) DBUG_RETURN(error); error = storage_open_columns(); if (error) { grn_obj_unlink(ctx, grn_table); grn_table = NULL; DBUG_RETURN(error); } if (!(open_options & HA_OPEN_FOR_REPAIR)) { error = storage_open_indexes(name); if (error) { storage_close_columns(); grn_obj_unlink(ctx, grn_table); grn_table = NULL; DBUG_RETURN(error); } storage_set_keys_in_use(); { mrn::Lock lock(&mrn_operations_mutex); mrn::PathMapper mapper(name); const char *table_name = mapper.table_name(); size_t table_name_size = strlen(table_name); if (db->is_broken_table(table_name, table_name_size)) { GRN_LOG(ctx, GRN_LOG_NOTICE, "Auto repair is started: <%s>", name); error = operations_->repair(table_name, table_name_size); if (!error) db->mark_table_repaired(table_name, table_name_size); if (!share->disable_keys) { if (!error) error = storage_reindex(); } GRN_LOG(ctx, GRN_LOG_NOTICE, "Auto repair is done: <%s>: %s", name, error == 0 ? "success" : "failure"); } } } ref_length = sizeof(grn_id); DBUG_RETURN(0); } int ha_mroonga::open_table(const char *name) { int error; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); mrn::PathMapper mapper(name); grn_table = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name())); if (ctx->rc) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (!grn_table) { error = ER_CANT_OPEN_FILE; char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "mroonga: failed to open table: <%s>", mapper.table_name()); my_message(error, error_message, MYF(0)); DBUG_RETURN(error); } DBUG_RETURN(0); } int ha_mroonga::storage_open_columns(void) { int error; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); int n_columns = table->s->fields; grn_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_columns); grn_column_ranges = (grn_obj **)malloc(sizeof(grn_obj *) * n_columns); for (int i = 0; i < n_columns; i++) { grn_columns[i] = NULL; grn_column_ranges[i] = NULL; } if (table_share->blob_fields) { DBUG_ASSERT(!blob_buffers); if (!(blob_buffers = new (&table->mem_root) String[n_columns])) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); } } for (int i = 0; i < n_columns; i++) { Field *field = table->field[i]; mrn::ColumnName column_name(field->field_name); if (table_share->blob_fields) { blob_buffers[i].set_charset(field->charset()); } if (strcmp(MRN_COLUMN_NAME_ID, column_name.mysql_name()) == 0) { continue; } #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { grn_columns[i] = NULL; grn_column_ranges[i] = NULL; continue; } #endif grn_columns[i] = grn_obj_column(ctx, grn_table, column_name.c_str(), column_name.length()); if (!grn_columns[i]) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); break; } grn_id range_id = grn_obj_get_range(ctx, grn_columns[i]); grn_column_ranges[i] = grn_ctx_at(ctx, range_id); if (!grn_column_ranges[i]) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); break; } } if (error != 0) { storage_close_columns(); } DBUG_RETURN(error); } void ha_mroonga::storage_close_columns(void) { int n_columns = table->s->fields; for (int i = 0; i < n_columns; i++) { grn_obj *column = grn_columns[i]; if (column) { grn_obj_unlink(ctx, column); } grn_obj *range = grn_column_ranges[i]; if (range) { grn_obj_unlink(ctx, range); } } free(grn_columns); grn_columns = NULL; free(grn_column_ranges); grn_column_ranges = NULL; } int ha_mroonga::storage_open_indexes(const char *name) { int error; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); uint n_keys = table->s->keys; uint pkey_nr = table->s->primary_key; if (n_keys > 0) { grn_index_tables = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys); grn_index_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys); key_id = (grn_id *)malloc(sizeof(grn_id) * n_keys); del_key_id = (grn_id *)malloc(sizeof(grn_id) * n_keys); } else { grn_index_tables = grn_index_columns = NULL; key_id = NULL; del_key_id = NULL; } mrn::PathMapper mapper(name); uint i, j; for (i = 0; i < n_keys; i++) { if (i == pkey_nr) { grn_index_tables[i] = grn_index_columns[i] = NULL; continue; } KEY *key_info = &(table->s->key_info[i]); if (KEY_N_KEY_PARTS(key_info) > 1) { KEY_PART_INFO *key_part = key_info->key_part; for (j = 0; j < KEY_N_KEY_PARTS(key_info); j++) { bitmap_set_bit(&multiple_column_key_bitmap, key_part[j].field->field_index); } } MRN_SHARE *tmp_share; tmp_share = mrn_get_share(name, table, &error); if (tmp_share->index_table[i]) { grn_index_tables[i] = grn_ctx_get(ctx, tmp_share->index_table[i], tmp_share->index_table_length[i]); if (ctx->rc == GRN_SUCCESS) { grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i], key_info->name.str, key_info->name.length); } } else { mrn::IndexTableName index_table_name(mapper.table_name(), key_info->name.str); grn_index_tables[i] = grn_ctx_get(ctx, index_table_name.c_str(), index_table_name.length()); if (ctx->rc == GRN_SUCCESS && !grn_index_tables[i]) { grn_index_tables[i] = grn_ctx_get(ctx, index_table_name.old_c_str(), index_table_name.old_length()); } if (ctx->rc == GRN_SUCCESS) { grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i], INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME)); if (!grn_index_columns[i] && ctx->rc == GRN_SUCCESS) { /* just for backward compatibility before 1.0. */ Field *field = key_info->key_part[0].field; grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i], field->field_name.str, field->field_name.length); } } } mrn_free_share(tmp_share); if (ctx->rc) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error; } if (ctx->rc) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error; } } error: if (error) { if (i) { while (true) { grn_obj *index_column = grn_index_columns[i]; if (index_column) { grn_obj_unlink(ctx, index_column); } grn_obj *index_table = grn_index_tables[i]; if (index_table) { grn_obj_unlink(ctx, index_table); } if (!i) break; i--; } } free(key_id); free(del_key_id); free(grn_index_columns); free(grn_index_tables); key_id = NULL; del_key_id = NULL; grn_index_columns = NULL; grn_index_tables = NULL; } DBUG_RETURN(error); } int ha_mroonga::open(const char *name, int mode, uint open_options #ifdef MRN_HANDLER_OPEN_HAVE_TABLE_DEFINITION , const dd::Table *table_def #endif ) { int error = 0; MRN_DBUG_ENTER_METHOD(); if (!(share = mrn_get_share(name, table, &error))) DBUG_RETURN(error); thr_lock_data_init(&share->lock,&thr_lock_data,NULL); if (my_bitmap_init(&multiple_column_key_bitmap, NULL, table->s->fields)) { mrn_free_share(share); share = NULL; DBUG_RETURN(HA_ERR_OUT_OF_MEM); } if (share->wrapper_mode) { error = wrapper_open(name, mode, open_options); } else { error = storage_open(name, mode, open_options); } if (error) { my_bitmap_free(&multiple_column_key_bitmap); mrn_free_share(share); share = NULL; } DBUG_RETURN(error); } int ha_mroonga::wrapper_close() { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER if (alter_key_info_buffer) { my_free(alter_key_info_buffer); alter_key_info_buffer = NULL; } #else if (wrap_alter_key_info) { my_free(wrap_alter_key_info); wrap_alter_key_info = NULL; } #endif #ifdef MRN_HANDLER_HAVE_HA_CLOSE error = wrap_handler->ha_close(); #else error = wrap_handler->close(); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); delete wrap_handler; wrap_handler = NULL; if (wrap_key_info) { my_free(wrap_key_info); wrap_key_info = NULL; } base_key_info = NULL; free_root(&mem_root, MYF(0)); DBUG_RETURN(error); } int ha_mroonga::storage_close() { MRN_DBUG_ENTER_METHOD(); grn_obj_unlink(ctx, grn_table); // TODO: unlink elements free(grn_columns); // TODO: unlink elements free(grn_column_ranges); DBUG_RETURN(0); } int ha_mroonga::close() { int error = 0; THD *thd = ha_thd(); MRN_DBUG_ENTER_METHOD(); clear_indexes(); if (share->wrapper_mode) { error = wrapper_close(); } else { error = storage_close(); } if (error != 0) { DBUG_RETURN(error); } if (thd) { error = add_wrap_hton(share->table_name, share->hton); } my_bitmap_free(&multiple_column_key_bitmap); if (share->use_count == 1) { mrn_free_long_term_share(share->long_term_share); } mrn_free_share(share); share = NULL; is_clone = false; if ( thd && thd_sql_command(thd) == SQLCOM_FLUSH ) { /* flush tables */ mrn::Lock lock(&mrn_open_tables_mutex); if (!mrn_open_tables.records) { int tmp_error = mrn_db_manager->clear(); if (tmp_error) error = tmp_error; } } DBUG_RETURN(error); } int ha_mroonga::wrapper_delete_table(const char *name, handlerton *wrap_handlerton, const char *table_name) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(wrap_handlerton->drop_table(wrap_handlerton, name)); } int ha_mroonga::generic_delete_table(const char *name, const char *table_name) { int error = 0; MRN_DBUG_ENTER_METHOD(); error = ensure_database_open(name); if (error) DBUG_RETURN(error); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); error = drop_indexes(table_name); grn_obj *table_obj = grn_ctx_get(ctx, table_name, strlen(table_name)); if (table_obj) { grn_obj_remove(ctx, table_obj); } if (ctx->rc) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } DBUG_RETURN(error); } int ha_mroonga::delete_table(const char *name) { MRN_DBUG_ENTER_METHOD(); int error = 0; THD *thd = ha_thd(); handlerton *wrap_handlerton = NULL; mrn::PathMapper mapper(name); st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false); if (slot_data && slot_data->first_wrap_hton) { st_mrn_wrap_hton *wrap_hton, *tmp_wrap_hton; tmp_wrap_hton = NULL; wrap_hton = slot_data->first_wrap_hton; while (wrap_hton) { if (!strcmp(wrap_hton->path, name)) { /* found */ wrap_handlerton = wrap_hton->hton; if (tmp_wrap_hton) tmp_wrap_hton->next = wrap_hton->next; else slot_data->first_wrap_hton = wrap_hton->next; free(wrap_hton); break; } tmp_wrap_hton = wrap_hton; wrap_hton = wrap_hton->next; } } if (!wrap_handlerton) { bool open_table_to_get_wrap_handlerton = true; if (mapper.is_internal_table_name()) { open_table_to_get_wrap_handlerton = false; } if (open_table_to_get_wrap_handlerton) { TABLE_LIST table_list; LEX_CSTRING db_name= { mapper.db_name(), strlen(mapper.db_name()) }; LEX_CSTRING table_name= { mapper.mysql_table_name(), strlen(mapper.mysql_table_name()) }; table_list.init_one_table(&db_name, &table_name, 0, TL_WRITE); mrn_open_mutex_lock(NULL); TABLE_SHARE *tmp_table_share = mrn_create_tmp_table_share(&table_list, name, &error); error = 0; mrn_open_mutex_unlock(NULL); if (tmp_table_share) { TABLE tmp_table; tmp_table.s = tmp_table_share; #ifdef WITH_PARTITION_STORAGE_ENGINE tmp_table.part_info = NULL; #endif MRN_SHARE *tmp_share = mrn_get_share(name, &tmp_table, &error); if (tmp_share) { wrap_handlerton = tmp_share->hton; mrn_free_long_term_share(tmp_share->long_term_share); tmp_share->long_term_share = NULL; mrn_free_share(tmp_share); } mrn_open_mutex_lock(NULL); mrn_free_tmp_table_share(tmp_table_share); mrn_open_mutex_unlock(NULL); if (error) { DBUG_RETURN(error); } } } } if (wrap_handlerton) { error = wrapper_delete_table(name, wrap_handlerton, mapper.table_name()); } if (!error) { error = generic_delete_table(name, mapper.table_name()); } if (!error) { error = operations_->clear(name, strlen(name)); } DBUG_RETURN(error); } void ha_mroonga::wrapper_set_keys_in_use() { uint i, j; MRN_DBUG_ENTER_METHOD(); mrn::AutoIncrementValueLock lock_(table_share); table_share->keys_in_use.set_prefix(table_share->keys); share->disable_keys = false; for (i = 0; i < table_share->keys; i++) { j = share->wrap_key_nr[i]; if (j < MAX_KEY) { if (!share->wrap_table_share->keys_in_use.is_set(j)) { /* copy bitmap */ table_share->keys_in_use.clear_bit(i); share->disable_keys = true; } } else { if (!grn_index_tables || !grn_index_tables[i]) { /* disabled */ table_share->keys_in_use.clear_bit(i); share->disable_keys = true; } } } table_share->keys_for_keyread.set_prefix(table_share->keys); table_share->keys_for_keyread.intersect(table_share->keys_in_use); DBUG_VOID_RETURN; } void ha_mroonga::storage_set_keys_in_use() { uint i; MRN_DBUG_ENTER_METHOD(); mrn::AutoIncrementValueLock lock_(table_share); table_share->keys_in_use.set_prefix(table_share->keys); share->disable_keys = false; for (i = 0; i < table_share->keys; i++) { if (i == table_share->primary_key) { continue; } if (!grn_index_tables[i]) { /* disabled */ table_share->keys_in_use.clear_bit(i); DBUG_PRINT("info", ("mroonga: key %u disabled", i)); share->disable_keys = true; } } table_share->keys_for_keyread.set_prefix(table_share->keys); table_share->keys_for_keyread.intersect(table_share->keys_in_use); DBUG_VOID_RETURN; } int ha_mroonga::wrapper_info(uint flag) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->info(flag); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (flag & HA_STATUS_ERRKEY) { errkey = wrap_handler->errkey; memcpy(dup_ref, wrap_handler->dup_ref, wrap_handler->ref_length); } if (flag & HA_STATUS_TIME) { stats.update_time = wrap_handler->stats.update_time; } if (flag & HA_STATUS_CONST) { stats.max_data_file_length = wrap_handler->stats.max_data_file_length; stats.create_time = wrap_handler->stats.create_time; stats.block_size = wrap_handler->stats.block_size; wrapper_set_keys_in_use(); } if (flag & HA_STATUS_VARIABLE) { stats.data_file_length = wrap_handler->stats.data_file_length; stats.index_file_length = wrap_handler->stats.index_file_length; stats.records = wrap_handler->stats.records; stats.mean_rec_length = wrap_handler->stats.mean_rec_length; stats.check_time = wrap_handler->stats.check_time; } if (flag & HA_STATUS_AUTO) { stats.auto_increment_value = wrap_handler->stats.auto_increment_value; } DBUG_RETURN(error); } int ha_mroonga::storage_info(uint flag) { MRN_DBUG_ENTER_METHOD(); mrn_change_encoding(ctx, NULL); if (flag & (HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK)) { errkey = dup_key; } if ((flag & HA_STATUS_AUTO) && table->found_next_number_field) { THD *thd = ha_thd(); ulonglong nb_reserved_values; bool next_number_field_is_null = !table->next_number_field; mrn::ExternalLock mrn_external_lock(ha_thd(), this, mrn_lock_type == F_UNLCK ? F_RDLCK : F_UNLCK); if (mrn_external_lock.error()) { DBUG_RETURN(mrn_external_lock.error()); } if (next_number_field_is_null) { table->next_number_field = table->found_next_number_field; } MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; { mrn::Lock lock(&long_term_share->auto_inc_mutex); unsigned long auto_increment_offset, auto_increment_increment; MRN_THD_GET_AUTOINC(thd, &auto_increment_offset, &auto_increment_increment); storage_get_auto_increment(auto_increment_offset, auto_increment_increment, 1, &stats.auto_increment_value, &nb_reserved_values); } if (next_number_field_is_null) { table->next_number_field = NULL; } } if (flag & HA_STATUS_CONST) { storage_set_keys_in_use(); } if (flag & HA_STATUS_VARIABLE) { storage_info_variable(); } DBUG_RETURN(0); } void ha_mroonga::storage_info_variable() { MRN_DBUG_ENTER_METHOD(); storage_info_variable_records(); storage_info_variable_data_file_length(); DBUG_VOID_RETURN; } void ha_mroonga::storage_info_variable_records() { MRN_DBUG_ENTER_METHOD(); stats.records = grn_table_size(ctx, grn_table); DBUG_VOID_RETURN; } void ha_mroonga::storage_info_variable_data_file_length() { MRN_DBUG_ENTER_METHOD(); stats.data_file_length = 0; stats.data_file_length += file_size(grn_obj_path(ctx, grn_table)); grn_hash *columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_OBJ_TABLE_HASH_KEY); grn_table_columns(ctx, grn_table, NULL, 0, (grn_obj *)columns); /* grn_id id __attribute__((unused)); */ grn_id *column_id; GRN_HASH_EACH(ctx, columns, id, &column_id, NULL, NULL, { grn_obj *column = grn_ctx_at(ctx, *column_id); stats.data_file_length += file_size(grn_obj_path(ctx, column)); grn_obj_unlink(ctx, column); }); grn_hash_close(ctx, columns); DBUG_VOID_RETURN; } int ha_mroonga::info(uint flag) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_info(flag); } else { error = storage_info(flag); } DBUG_RETURN(error); } uint ha_mroonga::wrapper_lock_count() const { uint lock_count; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); lock_count = wrap_handler->lock_count(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(lock_count); } uint ha_mroonga::storage_lock_count() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(1); } uint ha_mroonga::lock_count() const { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_lock_count(); } else { error = storage_lock_count(); } DBUG_RETURN(error); } THR_LOCK_DATA **ha_mroonga::wrapper_store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); to = wrap_handler->store_lock(thd, to, lock_type); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(to); } THR_LOCK_DATA **ha_mroonga::storage_store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { MRN_DBUG_ENTER_METHOD(); if (lock_type != TL_IGNORE && thr_lock_data.type == TL_UNLOCK) { if (!thd_in_lock_tables(thd)) { if (lock_type == TL_READ_NO_INSERT) { lock_type = TL_READ; } else if (lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE && !thd_tablespace_op(thd)) { lock_type = TL_WRITE_ALLOW_WRITE; } } thr_lock_data.type = lock_type; } *to++ = &thr_lock_data; DBUG_RETURN(to); } THR_LOCK_DATA **ha_mroonga::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: lock_type=%s", mrn_inspect_thr_lock_type(lock_type))); if (share->wrapper_mode) to = wrapper_store_lock(thd, to, lock_type); else to = storage_store_lock(thd, to, lock_type); DBUG_RETURN(to); } int ha_mroonga::wrapper_external_lock(THD *thd, int lock_type) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_external_lock(thd, lock_type); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_external_lock(THD *thd, int lock_type) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::external_lock(THD *thd, int lock_type) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn_lock_type = lock_type; if (share->wrapper_mode) { error = wrapper_external_lock(thd, lock_type); } else { error = storage_external_lock(thd, lock_type); } DBUG_RETURN(error); } int ha_mroonga::wrapper_rnd_init(bool scan) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_rnd_init(scan); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_rnd_init(bool scan) { MRN_DBUG_ENTER_METHOD(); mrn_change_encoding(ctx, NULL); cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0, 0, -1, 0); if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } DBUG_RETURN(0); } int ha_mroonga::rnd_init(bool scan) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_rnd_init(scan); } else { error = storage_rnd_init(scan); } DBUG_RETURN(error); } int ha_mroonga::wrapper_rnd_end() { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_rnd_end(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_rnd_end() { MRN_DBUG_ENTER_METHOD(); clear_cursor(); DBUG_RETURN(0); } int ha_mroonga::rnd_end() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_rnd_end(); } else { error = storage_rnd_end(); } DBUG_RETURN(error); } #ifdef MRN_HANDLER_RECORDS_RETURN_ERROR int ha_mroonga::wrapper_records(ha_rows *num_rows) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_records(num_rows); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_records(ha_rows *num_rows) { MRN_DBUG_ENTER_METHOD(); int error = handler::records(num_rows); DBUG_RETURN(error); } int ha_mroonga::records(ha_rows *num_rows) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_records(num_rows); } else { error = storage_records(num_rows); } DBUG_RETURN(error); } #else ha_rows ha_mroonga::wrapper_records() { ha_rows num_rows; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); num_rows = wrap_handler->records(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(num_rows); } ha_rows ha_mroonga::storage_records() { MRN_DBUG_ENTER_METHOD(); ha_rows num_rows = handler::records(); DBUG_RETURN(num_rows); } ha_rows ha_mroonga::records() { MRN_DBUG_ENTER_METHOD(); ha_rows num_rows; if (share->wrapper_mode) { num_rows = wrapper_records(); } else { num_rows = storage_records(); } DBUG_RETURN(num_rows); } #endif int ha_mroonga::wrapper_rnd_next(uchar *buf) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_RND_NEXT error = wrap_handler->ha_rnd_next(buf); #else error = wrap_handler->rnd_next(buf); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_rnd_next(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::rnd_next(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_rnd_next(buf); } else { error = storage_rnd_next(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_rnd_pos(uchar *buf, uchar *pos) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_HAVE_HA_RND_POS error = wrap_handler->ha_rnd_pos(buf, pos); #else error = wrap_handler->rnd_pos(buf, pos); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_rnd_pos(uchar *buf, uchar *pos) { MRN_DBUG_ENTER_METHOD(); record_id = *((grn_id*) pos); storage_store_fields(buf, record_id); DBUG_RETURN(0); } int ha_mroonga::rnd_pos(uchar *buf, uchar *pos) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_rnd_pos(buf, pos); } else { error = storage_rnd_pos(buf, pos); } DBUG_RETURN(error); } void ha_mroonga::wrapper_position(const uchar *record) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->ref = ref; wrap_handler->position(record); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_position(const uchar *record) { MRN_DBUG_ENTER_METHOD(); memcpy(ref, &record_id, sizeof(grn_id)); DBUG_VOID_RETURN; } void ha_mroonga::position(const uchar *record) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) wrapper_position(record); else storage_position(record); DBUG_VOID_RETURN; } int ha_mroonga::generic_extra(enum ha_extra_function operation) { MRN_DBUG_ENTER_METHOD(); switch (operation) { case HA_EXTRA_IGNORE_DUP_KEY: ignoring_duplicated_key = true; break; case HA_EXTRA_NO_IGNORE_DUP_KEY: ignoring_duplicated_key = false; break; case HA_EXTRA_WRITE_CAN_REPLACE: replacing_ = true; break; case HA_EXTRA_WRITE_CANNOT_REPLACE: replacing_ = false; break; case HA_EXTRA_INSERT_WITH_UPDATE: inserting_with_update = true; break; case HA_EXTRA_KEYREAD: ignoring_no_key_columns = true; break; case HA_EXTRA_NO_KEYREAD: ignoring_no_key_columns = false; break; default: break; } DBUG_RETURN(0); } int ha_mroonga::wrapper_extra(enum ha_extra_function operation) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->extra(operation); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_extra(enum ha_extra_function operation) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::extra(enum ha_extra_function operation) { int error = 0; MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: this=%p; extra-operation=%s", this, mrn_inspect_extra_function(operation))); if (share->wrapper_mode) { if ((error = wrapper_extra(operation))) DBUG_RETURN(error); } else { if ((error = storage_extra(operation))) DBUG_RETURN(error); } error = generic_extra(operation); DBUG_RETURN(error); } int ha_mroonga::wrapper_extra_opt(enum ha_extra_function operation, ulong cache_size) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->extra_opt(operation, cache_size); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_extra_opt(enum ha_extra_function operation, ulong cache_size) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::extra_opt(enum ha_extra_function operation, ulong cache_size) { int error = 0; MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { if ((error = wrapper_extra_opt(operation, cache_size))) DBUG_RETURN(error); } else { if ((error = storage_extra_opt(operation, cache_size))) DBUG_RETURN(error); } error = generic_extra(operation); DBUG_RETURN(error); } bool ha_mroonga::wrapper_is_target_index(KEY *key_info) { MRN_DBUG_ENTER_METHOD(); bool target_index = (key_info->algorithm == HA_KEY_ALG_FULLTEXT) || mrn_is_geo_key(key_info); DBUG_PRINT("info", ("mroonga: %s", target_index ? "true" : "false")); DBUG_RETURN(target_index); } bool ha_mroonga::wrapper_have_target_index() { MRN_DBUG_ENTER_METHOD(); bool have_target_index = false; uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (wrapper_is_target_index(key_info)) { have_target_index = true; break; } } DBUG_PRINT("info", ("mroonga: %s", have_target_index ? "true" : "false")); DBUG_RETURN(have_target_index); } int ha_mroonga::wrapper_write_row(const uchar *buf) { int error = 0; THD *thd = ha_thd(); MRN_DBUG_ENTER_METHOD(); mrn::Operation operation(operations_, "write", table->s->table_name.str, table->s->table_name.length); operation.record_target(record_id); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); tmp_disable_binlog(thd); error = wrap_handler->ha_write_row(buf); insert_id_for_cur_row = wrap_handler->insert_id_for_cur_row; reenable_binlog(thd); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (!error && wrapper_have_target_index()) { error = wrapper_write_row_index(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_write_row_index(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } mrn_change_encoding(ctx, NULL); GRN_BULK_REWIND(&key_buffer); grn_bulk_space(ctx, &key_buffer, table->key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)), buf, &(table->key_info[table_share->primary_key]), table->key_info[table_share->primary_key].key_length); int added; grn_id record_id; record_id = grn_table_add(ctx, grn_table, GRN_TEXT_VALUE(&key_buffer), GRN_TEXT_LEN(&key_buffer), &added); if (record_id == GRN_ID_NIL) { DBUG_PRINT("info", ("mroonga: failed to add a new record into groonga")); char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to add a new record into groonga: key=<%.*s>", (int)GRN_TEXT_LEN(&key_buffer), GRN_TEXT_VALUE(&key_buffer)); error = ER_ERROR_ON_WRITE; push_warning(ha_thd(), MRN_SEVERITY_WARNING, error, error_message); DBUG_RETURN(0); } mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (!(wrapper_is_target_index(key_info))) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { continue; } uint j; for (j = 0; j < KEY_N_KEY_PARTS(key_info); j++) { Field *field = key_info->key_part[j].field; if (field->is_null()) continue; error = mrn_change_encoding(ctx, field->charset()); if (error) goto err; error = generic_store_bulk(field, &new_value_buffer); if (error) { my_message(error, "mroonga: wrapper: " "failed to get new value for updating index.", MYF(0)); goto err; } grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, j + 1, NULL, &new_value_buffer); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } } err: DBUG_RETURN(error); } int ha_mroonga::storage_write_row(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool unique_indexes_are_processed = false; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } mrn::Operation operation(operations_, "write", table->s->table_name.str, table->s->table_name.length); THD *thd = ha_thd(); int i; int n_columns = table->s->fields; if (table->next_number_field && buf == table->record[0]) { if ((error = update_auto_increment())) DBUG_RETURN(error); } mrn::DebugColumnAccess debug_column_access(table, &table->read_set); for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif if (field->is_null()) continue; mrn::ColumnName column_name(field->field_name); if (strcmp(MRN_COLUMN_NAME_ID, column_name.c_str()) == 0) { push_warning_printf(thd, MRN_SEVERITY_WARNING, WARN_DATA_TRUNCATED, MRN_GET_ERR_MSG(WARN_DATA_TRUNCATED), MRN_COLUMN_NAME_ID, MRN_GET_CURRENT_ROW_FOR_WARNING(thd)); if (MRN_ABORT_ON_WARNING(thd)) { DBUG_RETURN(ER_DATA_TOO_LONG); } } } uint pkey_nr = table->s->primary_key; int added = 0; { mrn::Lock lock(&(share->record_mutex), have_unique_index()); if ((error = storage_write_row_unique_indexes(buf))) { DBUG_RETURN(error); } unique_indexes_are_processed = true; char *pkey; int pkey_size; GRN_BULK_REWIND(&key_buffer); if (pkey_nr == MAX_INDEXES) { pkey = NULL; pkey_size = 0; } else { KEY *key_info = &(table->key_info[pkey_nr]); if (KEY_N_KEY_PARTS(key_info) == 1) { Field *pkey_field = key_info->key_part[0].field; error = mrn_change_encoding(ctx, pkey_field->charset()); if (error) { DBUG_RETURN(error); } generic_store_bulk(pkey_field, &key_buffer); pkey = GRN_TEXT_VALUE(&key_buffer); pkey_size = GRN_TEXT_LEN(&key_buffer); } else { mrn_change_encoding(ctx, NULL); uchar key[MRN_MAX_KEY_SIZE]; key_copy(key, buf, key_info, key_info->key_length); grn_bulk_reserve(ctx, &key_buffer, MRN_MAX_KEY_SIZE); pkey = GRN_TEXT_VALUE(&key_buffer); storage_encode_multiple_column_key(key_info, key, key_info->key_length, (uchar *)pkey, (uint *)&pkey_size); } } if (grn_table->header.type != GRN_TABLE_NO_KEY && pkey_size == 0) { my_message(ER_ERROR_ON_WRITE, "primary key is empty", MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } record_id = grn_table_add(ctx, grn_table, pkey, pkey_size, &added); if (ctx->rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } if (!added) { // duplicated error error = HA_ERR_FOUND_DUPP_KEY; memcpy(dup_ref, &record_id, sizeof(grn_id)); dup_key = pkey_nr; if (!ignoring_duplicated_key) { GRN_LOG(ctx, GRN_LOG_ERROR, "duplicated id on insert: update primary key: <%.*s>", pkey_size, pkey); } uint j; for (j = 0; j < table->s->keys; j++) { if (j == pkey_nr) { continue; } KEY *key_info = &table->key_info[j]; if (key_info->flags & HA_NOSAME) { grn_table_delete_by_id(ctx, grn_index_tables[j], key_id[j]); } } DBUG_RETURN(error); } operation.record_target(record_id); } grn_obj colbuf; GRN_VOID_INIT(&colbuf); for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; if (field->is_null()) continue; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif mrn::ColumnName column_name(field->field_name); #ifdef MRN_HAVE_SPATIAL bool is_null_geometry_value = field->real_type() == MYSQL_TYPE_GEOMETRY && static_cast(field)->get_length() == 0; if (is_null_geometry_value) { continue; } #endif if (strcmp(MRN_COLUMN_NAME_ID, column_name.c_str()) == 0) { continue; } error = mrn_change_encoding(ctx, field->charset()); if (error) { GRN_OBJ_FIN(ctx, &colbuf); goto err; } error = generic_store_bulk(field, &colbuf); if (error) { GRN_OBJ_FIN(ctx, &colbuf); goto err; } grn_obj *column = grn_columns[i]; if (is_foreign_key_field(table->s->table_name.str, field->field_name.str)) { grn_obj value; GRN_RECORD_INIT(&value, 0, grn_obj_get_range(ctx, column)); grn_rc cast_rc = grn_obj_cast(ctx, &colbuf, &value, GRN_FALSE); if (cast_rc != GRN_SUCCESS) { grn_obj inspected; GRN_TEXT_INIT(&inspected, 0); grn_inspect(ctx, &inspected, &colbuf); error = HA_ERR_NO_REFERENCED_ROW; GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "foreign record doesn't exist: <%s>:<%.*s>", field->field_name.str, static_cast(GRN_TEXT_LEN(&inspected)), GRN_TEXT_VALUE(&inspected)); GRN_OBJ_FIN(ctx, &value); GRN_OBJ_FIN(ctx, &colbuf); GRN_OBJ_FIN(ctx, &inspected); goto err; } grn_obj_set_value(ctx, column, record_id, &value, GRN_OBJ_SET); } else { if (added && is_grn_zero_column_value(column, &colbuf)) { // WORKAROUND: groonga can't index newly added '0' value for // fix size column. So we add non-'0' value first then add // real '0' value again. It will be removed when groonga // supports 'null' value. char *bytes = GRN_BULK_HEAD(&colbuf); bytes[0] = '\1'; grn_obj_set_value(ctx, column, record_id, &colbuf, GRN_OBJ_SET); bytes[0] = '\0'; } grn_obj_set_value(ctx, column, record_id, &colbuf, GRN_OBJ_SET); } if (ctx->rc) { GRN_OBJ_FIN(ctx, &colbuf); my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); error = ER_ERROR_ON_WRITE; goto err; } } GRN_OBJ_FIN(ctx, &colbuf); error = storage_write_row_multiple_column_indexes(buf, record_id); if (error) { goto err; } // for UDF last_insert_grn_id() st_mrn_slot_data *slot_data; slot_data = mrn_get_slot_data(thd, true); if (slot_data == NULL) { error = HA_ERR_OUT_OF_MEM; goto err; } slot_data->last_insert_record_id = record_id; grn_db_touch(ctx, grn_ctx_db(ctx)); if (table->found_next_number_field && !table->s->next_number_keypart) { Field_num *field = (Field_num *) table->found_next_number_field; if (field->unsigned_flag || field->val_int() > 0) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; ulonglong nr = (ulonglong) field->val_int(); if (!long_term_share->auto_inc_inited) { storage_info(HA_STATUS_AUTO); } { mrn::Lock lock(&long_term_share->auto_inc_mutex); if (long_term_share->auto_inc_value <= nr) { long_term_share->auto_inc_value = nr + 1; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); } } } } DBUG_RETURN(0); err: if (unique_indexes_are_processed) { uint j; for (j = 0; j < table->s->keys; j++) { if (j == pkey_nr) { continue; } KEY *key_info = &table->key_info[j]; if (key_info->flags & HA_NOSAME) { grn_table_delete_by_id(ctx, grn_index_tables[j], key_id[j]); } } } grn_table_delete_by_id(ctx, grn_table, record_id); DBUG_RETURN(error); } int ha_mroonga::storage_write_row_multiple_column_index(const uchar *buf, grn_id record_id, KEY *key_info, grn_obj *index_column) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn_change_encoding(ctx, NULL); GRN_BULK_REWIND(&key_buffer); grn_bulk_space(ctx, &key_buffer, key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)), buf, key_info, key_info->key_length); GRN_BULK_REWIND(&encoded_key_buffer); grn_bulk_reserve(ctx, &encoded_key_buffer, MRN_MAX_KEY_SIZE); uint encoded_key_length; storage_encode_multiple_column_key(key_info, (uchar *)(GRN_TEXT_VALUE(&key_buffer)), key_info->key_length, (uchar *)(GRN_TEXT_VALUE(&encoded_key_buffer)), &encoded_key_length); grn_bulk_space(ctx, &encoded_key_buffer, encoded_key_length); DBUG_PRINT("info", ("mroonga: key_length=%u", key_info->key_length)); DBUG_PRINT("info", ("mroonga: encoded_key_length=%u", encoded_key_length)); grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, 1, NULL, &encoded_key_buffer); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } int ha_mroonga::storage_write_row_multiple_column_indexes(const uchar *buf, grn_id record_id) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if (KEY_N_KEY_PARTS(key_info) == 1 || (key_info->flags & HA_FULLTEXT)) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { continue; } if ((error = storage_write_row_multiple_column_index(buf, record_id, key_info, index_column))) { goto err; } } err: DBUG_RETURN(error); } int ha_mroonga::storage_write_row_unique_index(const uchar *buf, KEY *key_info, grn_obj *index_table, grn_obj *index_column, grn_id *key_id) { char *ukey = NULL; int error, ukey_size = 0; MRN_DBUG_ENTER_METHOD(); GRN_BULK_REWIND(&key_buffer); if (KEY_N_KEY_PARTS(key_info) == 1) { Field *ukey_field = key_info->key_part[0].field; error = mrn_change_encoding(ctx, ukey_field->charset()); if (error) { DBUG_RETURN(error); } generic_store_bulk(ukey_field, &key_buffer); ukey = GRN_TEXT_VALUE(&key_buffer); ukey_size = GRN_TEXT_LEN(&key_buffer); } else { mrn_change_encoding(ctx, NULL); uchar key[MRN_MAX_KEY_SIZE]; key_copy(key, buf, key_info, key_info->key_length); grn_bulk_reserve(ctx, &key_buffer, MRN_MAX_KEY_SIZE); ukey = GRN_TEXT_VALUE(&key_buffer); storage_encode_multiple_column_key(key_info, key, key_info->key_length, (uchar *)(ukey), (uint *)&ukey_size); } int added; *key_id = grn_table_add(ctx, index_table, ukey, ukey_size, &added); if (ctx->rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } if (!added) { // duplicated error error = HA_ERR_FOUND_DUPP_KEY; grn_id duplicated_record_id = GRN_ID_NIL; { grn_table_cursor *table_cursor; table_cursor = grn_table_cursor_open(ctx, index_table, ukey, ukey_size, ukey, ukey_size, 0, -1, 0); if (table_cursor) { grn_obj *index_cursor; index_cursor = grn_index_cursor_open(ctx, table_cursor, index_column, GRN_ID_NIL, GRN_ID_MAX, 0); if (index_cursor) { grn_posting *posting; posting = grn_index_cursor_next(ctx, index_cursor, NULL); if (posting) { duplicated_record_id = posting->rid; } } grn_obj_unlink(ctx, index_cursor); } grn_table_cursor_close(ctx, table_cursor); } memcpy(dup_ref, &duplicated_record_id, sizeof(grn_id)); if (!ignoring_duplicated_key) { GRN_LOG(ctx, GRN_LOG_ERROR, "duplicated id on insert: update unique index: <%.*s>", ukey_size, ukey); } DBUG_RETURN(error); } DBUG_RETURN(0); } int ha_mroonga::storage_write_row_unique_indexes(const uchar *buf) { int error = 0; uint i; uint n_keys = table->s->keys; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[i]; if (!(key_info->flags & HA_NOSAME)) { continue; } grn_obj *index_table = grn_index_tables[i]; if (!index_table) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { continue; } if ((error = storage_write_row_unique_index(buf, key_info, index_table, index_column, &key_id[i]))) { if (error == HA_ERR_FOUND_DUPP_KEY) { dup_key = i; } goto err; } } DBUG_RETURN(0); err: if (i) { mrn_change_encoding(ctx, NULL); do { i--; if (i == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[i]; if (!(key_info->flags & HA_NOSAME)) { continue; } if (key_info->flags & HA_NOSAME) { grn_table_delete_by_id(ctx, grn_index_tables[i], key_id[i]); } } while (i); } DBUG_RETURN(error); } int ha_mroonga::write_row(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_write_row(buf); } else { error = storage_write_row(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_get_record_id(uchar *data, grn_id *record_id, const char *context) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj key; GRN_TEXT_INIT(&key, 0); mrn_change_encoding(ctx, NULL); grn_bulk_space(ctx, &key, table->key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&key)), data, &(table->key_info[table_share->primary_key]), table->key_info[table_share->primary_key].key_length); *record_id = grn_table_get(ctx, grn_table, GRN_TEXT_VALUE(&key), GRN_TEXT_LEN(&key)); if (*record_id == GRN_ID_NIL) { DBUG_PRINT("info", ("mroonga: %s", context)); char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "%s: key=<%.*s>", context, (int)GRN_TEXT_LEN(&key), GRN_TEXT_VALUE(&key)); error = ER_ERROR_ON_WRITE; push_warning(ha_thd(), MRN_SEVERITY_WARNING, error, error_message); } grn_obj_unlink(ctx, &key); DBUG_RETURN(error); } int ha_mroonga::wrapper_update_row(const uchar *old_data, const uchar *new_data) { MRN_DBUG_ENTER_METHOD(); int error = 0; THD *thd = ha_thd(); mrn::Operation operation(operations_, "update", table->s->table_name.str, table->s->table_name.length); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); tmp_disable_binlog(thd); error = wrap_handler->ha_update_row(old_data, new_data); reenable_binlog(thd); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (!error && wrapper_have_target_index()) { error = wrapper_update_row_index(old_data, new_data); } DBUG_RETURN(error); } int ha_mroonga::wrapper_update_row_index(const uchar *old_data, const uchar *new_data) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } mrn_change_encoding(ctx, NULL); KEY *key_info = &(table->key_info[table_share->primary_key]); GRN_BULK_REWIND(&key_buffer); key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)), new_data, key_info, key_info->key_length); int added; grn_id new_record_id; new_record_id = grn_table_add(ctx, grn_table, GRN_TEXT_VALUE(&key_buffer), table->key_info->key_length, &added); if (new_record_id == GRN_ID_NIL) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to get new record ID for updating from groonga: key=<%.*s>", (int)GRN_TEXT_LEN(&key_buffer), GRN_TEXT_VALUE(&key_buffer)); error = ER_ERROR_ON_WRITE; my_message(error, error_message, MYF(0)); DBUG_RETURN(error); } grn_id old_record_id; my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(old_data, table->record[0]); error = wrapper_get_record_id((uchar *)old_data, &old_record_id, "failed to get old record ID " "for updating from groonga"); if (error) { DBUG_RETURN(0); } mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (!(wrapper_is_target_index(key_info))) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { /* disable keys */ continue; } uint j; for (j = 0; j < KEY_N_KEY_PARTS(key_info); j++) { Field *field = key_info->key_part[j].field; generic_store_bulk(field, &new_value_buffer); field->move_field_offset(ptr_diff); generic_store_bulk(field, &old_value_buffer); field->move_field_offset(-ptr_diff); grn_rc rc; if (old_record_id == new_record_id) { if (added) { rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1, &old_value_buffer, NULL); if (!rc) { rc = grn_column_index_update(ctx, index_column, new_record_id, j + 1, NULL, &new_value_buffer); } } else { rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1, &old_value_buffer, &new_value_buffer); } } else { rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1, &old_value_buffer, NULL); if (!rc) { rc = grn_column_index_update(ctx, index_column, new_record_id, j + 1, NULL, &new_value_buffer); } if (!rc) { rc = grn_table_delete_by_id(ctx, grn_table, old_record_id); } } if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } } err: DBUG_RETURN(error); } int ha_mroonga::storage_update_row(const uchar *old_data, const uchar *new_data) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } mrn::Operation operation(operations_, "update", table->s->table_name.str, table->s->table_name.length); operation.record_target(record_id); grn_obj colbuf; int i; uint j; int n_columns = table->s->fields; THD *thd = ha_thd(); for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif if (!bitmap_is_set(table->write_set, field->field_index)) continue; if (field->is_null()) continue; { mrn::ColumnName column_name(field->field_name); if (strcmp(MRN_COLUMN_NAME_ID, column_name.c_str()) == 0) { push_warning_printf(thd, MRN_SEVERITY_WARNING, WARN_DATA_TRUNCATED, MRN_GET_ERR_MSG(WARN_DATA_TRUNCATED), MRN_COLUMN_NAME_ID, MRN_GET_CURRENT_ROW_FOR_WARNING(thd)); if (MRN_ABORT_ON_WARNING(thd)) { DBUG_RETURN(ER_DATA_TOO_LONG); } } } if (!is_foreign_key_field(table->s->table_name.str, field->field_name.str)) continue; { grn_obj *column = grn_columns[i]; grn_obj new_value; GRN_VOID_INIT(&new_value); { mrn::DebugColumnAccess debug_column_access(table, &table->read_set); generic_store_bulk(field, &new_value); } grn_obj casted_value; GRN_RECORD_INIT(&casted_value, 0, grn_obj_get_range(ctx, column)); grn_rc cast_rc = grn_obj_cast(ctx, &new_value, &casted_value, GRN_FALSE); GRN_OBJ_FIN(ctx, &casted_value); if (cast_rc != GRN_SUCCESS) { grn_obj inspected; GRN_TEXT_INIT(&inspected, 0); grn_inspect(ctx, &inspected, &new_value); GRN_OBJ_FIN(ctx, &new_value); error = HA_ERR_NO_REFERENCED_ROW; GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "foreign record doesn't exist: <%s>:<%.*s>", field->field_name.str, static_cast(GRN_TEXT_LEN(&inspected)), GRN_TEXT_VALUE(&inspected)); GRN_OBJ_FIN(ctx, &inspected); DBUG_RETURN(error); } GRN_OBJ_FIN(ctx, &new_value); } } KEY *pkey_info = NULL; storage_store_fields_for_prep_update(old_data, new_data, record_id); { mrn::Lock lock(&(share->record_mutex), have_unique_index()); mrn::DebugColumnAccess debug_column_access(table, &table->read_set); if ((error = storage_prepare_delete_row_unique_indexes(old_data, record_id))) { DBUG_RETURN(error); } if ((error = storage_update_row_unique_indexes(new_data))) { DBUG_RETURN(error); } } if (table->s->primary_key != MAX_INDEXES) { pkey_info = &(table->key_info[table->s->primary_key]); } GRN_VOID_INIT(&colbuf); for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif if (bitmap_is_set(table->write_set, field->field_index)) { mrn::DebugColumnAccess debug_column_access(table, &table->read_set); DBUG_PRINT("info", ("mroonga: update column %d(%d)",i,field->field_index)); if (field->is_null()) continue; mrn::ColumnName column_name(field->field_name); if (strcmp(MRN_COLUMN_NAME_ID, column_name.c_str()) == 0) { continue; } error = mrn_change_encoding(ctx, field->charset()); if (error) goto err; bool is_pkey = false; bool on_duplicate_key_update = (inserting_with_update && ignoring_duplicated_key); if (pkey_info && !on_duplicate_key_update) { for (j = 0; j < KEY_N_KEY_PARTS(pkey_info); j++) { Field *pkey_field = pkey_info->key_part[j].field; if (strcmp(pkey_field->field_name.str, column_name.c_str()) == 0) { is_pkey = true; break; } } } generic_store_bulk(field, &colbuf); if (is_pkey) { bool is_multiple_column_index = KEY_N_KEY_PARTS(pkey_info) > 1; bool is_same_value; if (is_multiple_column_index) { is_same_value = false; } else { grn_id found_record_id = grn_table_get(ctx, grn_table, GRN_BULK_HEAD(&colbuf), GRN_BULK_VSIZE(&colbuf)); is_same_value = (record_id == found_record_id); } if (!is_same_value && !replacing_) { char message[MRN_BUFFER_SIZE]; snprintf(message, MRN_BUFFER_SIZE, "data truncated for primary key column: <%s>", column_name.c_str()); push_warning(thd, MRN_SEVERITY_WARNING, WARN_DATA_TRUNCATED, message); } continue; } grn_obj_set_value(ctx, grn_columns[i], record_id, &colbuf, GRN_OBJ_SET); if (ctx->rc) { grn_obj_unlink(ctx, &colbuf); my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); error = ER_ERROR_ON_WRITE; goto err; } } } grn_obj_unlink(ctx, &colbuf); if ((error = storage_update_row_index(old_data, new_data))) { goto err; } if ((error = storage_delete_row_unique_indexes())) { DBUG_RETURN(error); } grn_db_touch(ctx, grn_ctx_db(ctx)); if (table->found_next_number_field && !table->s->next_number_keypart && new_data == table->record[0]) { mrn::DebugColumnAccess debug_column_access(table, &table->read_set); Field_num *field = (Field_num *) table->found_next_number_field; if (field->unsigned_flag || field->val_int() > 0) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; ulonglong nr = (ulonglong) field->val_int(); if (!long_term_share->auto_inc_inited) { storage_info(HA_STATUS_AUTO); } { mrn::Lock lock(&long_term_share->auto_inc_mutex); if (long_term_share->auto_inc_value <= nr) { long_term_share->auto_inc_value = nr + 1; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); } } } } DBUG_RETURN(0); err: for (j = 0; j < table->s->keys; j++) { if (j == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[j]; if ((key_info->flags & HA_NOSAME) && key_id[j] != GRN_ID_NIL) { grn_table_delete_by_id(ctx, grn_index_tables[j], key_id[j]); } } if (!error && thd_sql_command(ha_thd()) == SQLCOM_TRUNCATE) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; mrn::Lock lock(&long_term_share->auto_inc_mutex); long_term_share->auto_inc_value = 0; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); long_term_share->auto_inc_inited = false; } DBUG_RETURN(error); } int ha_mroonga::storage_update_row_index(const uchar *old_data, const uchar *new_data) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj old_key, old_encoded_key, new_key, new_encoded_key; GRN_TEXT_INIT(&old_key, 0); GRN_TEXT_INIT(&old_encoded_key, 0); GRN_TEXT_INIT(&new_key, 0); GRN_TEXT_INIT(&new_encoded_key, 0); mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; mrn_change_encoding(ctx, NULL); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if (KEY_N_KEY_PARTS(key_info) == 1 || (key_info->flags & HA_FULLTEXT)) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { /* disable keys */ continue; } GRN_BULK_REWIND(&old_key); grn_bulk_space(ctx, &old_key, key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&old_key)), (uchar *)old_data, key_info, key_info->key_length); GRN_BULK_REWIND(&old_encoded_key); grn_bulk_reserve(ctx, &old_encoded_key, MRN_MAX_KEY_SIZE); uint old_encoded_key_length; storage_encode_multiple_column_key(key_info, (uchar *)(GRN_TEXT_VALUE(&old_key)), key_info->key_length, (uchar *)(GRN_TEXT_VALUE(&old_encoded_key)), &old_encoded_key_length); grn_bulk_space(ctx, &old_encoded_key, old_encoded_key_length); GRN_BULK_REWIND(&new_key); grn_bulk_space(ctx, &new_key, key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&new_key)), (uchar *)new_data, key_info, key_info->key_length); GRN_BULK_REWIND(&new_encoded_key); grn_bulk_reserve(ctx, &new_encoded_key, MRN_MAX_KEY_SIZE); uint new_encoded_key_length; storage_encode_multiple_column_key(key_info, (uchar *)(GRN_TEXT_VALUE(&new_key)), key_info->key_length, (uchar *)(GRN_TEXT_VALUE(&new_encoded_key)), &new_encoded_key_length); grn_bulk_space(ctx, &new_encoded_key, new_encoded_key_length); grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, 1, &old_encoded_key, &new_encoded_key); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } err: grn_obj_unlink(ctx, &old_key); grn_obj_unlink(ctx, &old_encoded_key); grn_obj_unlink(ctx, &new_key); grn_obj_unlink(ctx, &new_encoded_key); DBUG_RETURN(error); } int ha_mroonga::storage_update_row_unique_indexes(const uchar *new_data) { int error; uint i; uint n_keys = table->s->keys; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[i]; if (!(key_info->flags & HA_NOSAME)) { continue; } grn_obj *index_table = grn_index_tables[i]; if (!index_table) { key_id[i] = GRN_ID_NIL; del_key_id[i] = GRN_ID_NIL; continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { key_id[i] = GRN_ID_NIL; del_key_id[i] = GRN_ID_NIL; continue; } if ( KEY_N_KEY_PARTS(key_info) == 1 && !bitmap_is_set(table->write_set, key_info->key_part[0].field->field_index) ) { /* no change */ key_id[i] = GRN_ID_NIL; del_key_id[i] = GRN_ID_NIL; continue; } if ((error = storage_write_row_unique_index(new_data, key_info, index_table, index_column, &key_id[i]))) { if (error == HA_ERR_FOUND_DUPP_KEY) { if (key_id[i] == del_key_id[i]) { /* no change */ key_id[i] = GRN_ID_NIL; del_key_id[i] = GRN_ID_NIL; continue; } dup_key = i; DBUG_PRINT("info", ("mroonga: different key ID: %d record ID: %d,%d", i, key_id[i], del_key_id[i])); } goto err; } } DBUG_RETURN(0); err: if (i) { mrn_change_encoding(ctx, NULL); do { i--; KEY *key_info = &table->key_info[i]; if ((key_info->flags & HA_NOSAME) && key_id[i] != GRN_ID_NIL) { grn_table_delete_by_id(ctx, grn_index_tables[i], key_id[i]); } } while (i); } DBUG_RETURN(error); } int ha_mroonga::update_row(const uchar *old_data, const uchar *new_data) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_update_row(old_data, new_data); } else { error = storage_update_row(old_data, new_data); } DBUG_RETURN(error); } int ha_mroonga::wrapper_delete_row(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; THD *thd= ha_thd(); mrn::Operation operation(operations_, "delete", table->s->table_name.str, table->s->table_name.length); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); tmp_disable_binlog(thd); error = wrap_handler->ha_delete_row(buf); reenable_binlog(thd); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (!error && wrapper_have_target_index()) { error = wrapper_delete_row_index(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_delete_row_index(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } mrn_change_encoding(ctx, NULL); grn_id record_id; error = wrapper_get_record_id((uchar *)buf, &record_id, "failed to get record ID " "for deleting from groonga"); if (error) { DBUG_RETURN(0); } mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (!(wrapper_is_target_index(key_info))) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { /* disable keys */ continue; } uint j; for (j = 0; j < KEY_N_KEY_PARTS(key_info); j++) { Field *field = key_info->key_part[j].field; if (field->is_null()) continue; generic_store_bulk(field, &old_value_buffer); grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, j + 1, &old_value_buffer, NULL); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } } err: grn_table_delete_by_id(ctx, grn_table, record_id); if (ctx->rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } int ha_mroonga::storage_delete_row(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(0); } mrn::Operation operation(operations_, "delete", table->s->table_name.str, table->s->table_name.length); operation.record_target(record_id); { grn_id referencing_child_table_id = GRN_ID_NIL; grn_hash *columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); grn_table_columns(ctx, grn_table, "", 0, reinterpret_cast(columns)); GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { void *key; grn_hash_cursor_get_key(ctx, cursor, &key); grn_id column_id = *static_cast(key); grn_obj *column = grn_ctx_at(ctx, column_id); if (!column) continue; if (column->header.type != GRN_COLUMN_INDEX) continue; grn_ii_cursor *ii_cursor = grn_ii_cursor_open(ctx, reinterpret_cast(column), record_id, GRN_ID_NIL, GRN_ID_MAX, 0, 0); if (!ii_cursor) continue; if (grn_ii_cursor_next(ctx, ii_cursor)) { referencing_child_table_id = grn_obj_get_range(ctx, column); } grn_ii_cursor_close(ctx, ii_cursor); if (referencing_child_table_id != GRN_ID_NIL) break; } GRN_HASH_EACH_END(ctx, cursor); grn_hash_close(ctx, columns); if (referencing_child_table_id != GRN_ID_NIL) { grn_obj *referencing_child_table = grn_ctx_at(ctx, referencing_child_table_id); char name[GRN_TABLE_MAX_KEY_SIZE]; int name_size; name_size = grn_obj_name(ctx, referencing_child_table, name, GRN_TABLE_MAX_KEY_SIZE); error = HA_ERR_ROW_IS_REFERENCED; GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "one or more child rows exist in <%.*s>", name_size, name); DBUG_RETURN(error); } } storage_store_fields_for_prep_update(buf, NULL, record_id); { mrn::Lock lock(&(share->record_mutex), have_unique_index()); if ((error = storage_prepare_delete_row_unique_indexes(buf, record_id))) { DBUG_RETURN(error); } mrn_change_encoding(ctx, NULL); grn_table_delete_by_id(ctx, grn_table, record_id); if (ctx->rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } if ( (error = storage_delete_row_index(buf)) || (error = storage_delete_row_unique_indexes()) ) { DBUG_RETURN(error); } } grn_db_touch(ctx, grn_ctx_db(ctx)); DBUG_RETURN(0); } int ha_mroonga::storage_delete_row_index(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj key, encoded_key; GRN_TEXT_INIT(&key, 0); GRN_TEXT_INIT(&encoded_key, 0); mrn::DebugColumnAccess debug_column_access(table, &table->read_set); uint i; uint n_keys = table->s->keys; mrn_change_encoding(ctx, NULL); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if (KEY_N_KEY_PARTS(key_info) == 1 || (key_info->flags & HA_FULLTEXT)) { continue; } grn_obj *index_column = grn_index_columns[i]; if (!index_column) { /* disable keys */ continue; } GRN_BULK_REWIND(&key); grn_bulk_space(ctx, &key, key_info->key_length); key_copy((uchar *)(GRN_TEXT_VALUE(&key)), (uchar *)buf, key_info, key_info->key_length); GRN_BULK_REWIND(&encoded_key); grn_bulk_reserve(ctx, &encoded_key, MRN_MAX_KEY_SIZE); uint encoded_key_length; storage_encode_multiple_column_key(key_info, (uchar *)(GRN_TEXT_VALUE(&key)), key_info->key_length, (uchar *)(GRN_TEXT_VALUE(&encoded_key)), &encoded_key_length); grn_bulk_space(ctx, &encoded_key, encoded_key_length); grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, 1, &encoded_key, NULL); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } err: grn_obj_unlink(ctx, &encoded_key); grn_obj_unlink(ctx, &key); DBUG_RETURN(error); } int ha_mroonga::storage_delete_row_unique_index(grn_obj *index_table, grn_id del_key_id) { MRN_DBUG_ENTER_METHOD(); grn_rc rc = grn_table_delete_by_id(ctx, index_table, del_key_id); if (rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } DBUG_RETURN(0); } int ha_mroonga::storage_delete_row_unique_indexes() { int error = 0, tmp_error; uint i; uint n_keys = table->s->keys; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[i]; if ((!(key_info->flags & HA_NOSAME)) || del_key_id[i] == GRN_ID_NIL) { continue; } grn_obj *index_table = grn_index_tables[i]; if ((tmp_error = storage_delete_row_unique_index(index_table, del_key_id[i]))) { error = tmp_error; } } DBUG_RETURN(error); } int ha_mroonga::storage_prepare_delete_row_unique_index(const uchar *buf, grn_id record_id, KEY *key_info, grn_obj *index_table, grn_obj *index_column, grn_id *del_key_id) { const void *ukey = NULL; uint32 ukey_size = 0; MRN_DBUG_ENTER_METHOD(); if (KEY_N_KEY_PARTS(key_info) == 1) { GRN_BULK_REWIND(&key_buffer); grn_obj_get_value(ctx, index_column, record_id, &key_buffer); ukey = GRN_TEXT_VALUE(&key_buffer); ukey_size = GRN_TEXT_LEN(&key_buffer); } else { mrn_change_encoding(ctx, NULL); uchar key[MRN_MAX_KEY_SIZE]; key_copy(key, (uchar *) buf, key_info, key_info->key_length); grn_bulk_reserve(ctx, &key_buffer, MRN_MAX_KEY_SIZE); ukey = GRN_TEXT_VALUE(&key_buffer); storage_encode_multiple_column_key(key_info, key, key_info->key_length, (uchar *)ukey, (uint *)&ukey_size); } *del_key_id = grn_table_get(ctx, index_table, ukey, ukey_size); DBUG_RETURN(0); } int ha_mroonga::storage_prepare_delete_row_unique_indexes(const uchar *buf, grn_id record_id) { int error = 0, tmp_error; uint i; uint n_keys = table->s->keys; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &table->key_info[i]; if (!(key_info->flags & HA_NOSAME)) { continue; } grn_obj *index_table = grn_index_tables[i]; if (!index_table) { del_key_id[i] = GRN_ID_NIL; continue; } grn_obj *index_column; if (KEY_N_KEY_PARTS(key_info) == 1) { Field *field = key_info->key_part[0].field; mrn_change_encoding(ctx, field->charset()); index_column = grn_columns[field->field_index]; } else { mrn_change_encoding(ctx, NULL); index_column = grn_index_columns[i]; } if ((tmp_error = storage_prepare_delete_row_unique_index(buf, record_id, key_info, index_table, index_column, &del_key_id[i]))) { error = tmp_error; } } DBUG_RETURN(error); } int ha_mroonga::delete_row(const uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_delete_row(buf); } else { error = storage_delete_row(buf); } DBUG_RETURN(error); } uint ha_mroonga::wrapper_max_supported_key_parts() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(MAX_REF_PARTS); } uint ha_mroonga::storage_max_supported_key_parts() const { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(MAX_REF_PARTS); } uint ha_mroonga::max_supported_key_parts() const { MRN_DBUG_ENTER_METHOD(); uint parts; if (!share && !analyzed_for_create && ( thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE || thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX || thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ) ) { create_share_for_create(); } if (analyzed_for_create && share_for_create.wrapper_mode) { parts = wrapper_max_supported_key_parts(); } else if (wrap_handler && share && share->wrapper_mode) { parts = wrapper_max_supported_key_parts(); } else { parts = storage_max_supported_key_parts(); } DBUG_RETURN(parts); } ha_rows ha_mroonga::wrapper_records_in_range(uint key_nr, const key_range *range_min, const key_range *range_max, page_range *pages) { ha_rows row_count; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->s->key_info[key_nr]); if (mrn_is_geo_key(key_info)) { row_count = generic_records_in_range_geo(key_nr, range_min, range_max); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); row_count = wrap_handler->records_in_range(key_nr, range_min, range_max, pages); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(row_count); } ha_rows ha_mroonga::storage_records_in_range(uint key_nr, const key_range *range_min, const key_range *range_max, page_range *pages) { MRN_DBUG_ENTER_METHOD(); int flags = 0; uint size_min = 0, size_max = 0; ha_rows row_count = 0; uchar *key_min = NULL, *key_max = NULL; uchar key_min_entity[MRN_MAX_KEY_SIZE]; uchar key_max_entity[MRN_MAX_KEY_SIZE]; KEY *key_info = &(table->s->key_info[key_nr]); bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1; if (is_multiple_column_index) { mrn_change_encoding(ctx, NULL); if (range_min && range_max && range_min->length == range_max->length && memcmp(range_min->key, range_max->key, range_min->length) == 0) { flags |= GRN_CURSOR_PREFIX; key_min = key_min_entity; storage_encode_multiple_column_key(key_info, range_min->key, range_min->length, key_min, &size_min); } else { key_min = key_min_entity; key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, range_min, range_max, key_min, &size_min, key_max, &size_max); } } else if (mrn_is_geo_key(key_info)) { mrn_change_encoding(ctx, key_info->key_part->field->charset()); row_count = generic_records_in_range_geo(key_nr, range_min, range_max); DBUG_RETURN(row_count); } else { Field *field = key_info->key_part[0].field; const char *column_name = field->field_name.str; mrn_change_encoding(ctx, field->charset()); if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) { DBUG_RETURN((ha_rows)1); } if (range_min) { key_min = key_min_entity; storage_encode_key(field, range_min->key, key_min, &size_min); if (size_min == 0) { DBUG_RETURN(HA_POS_ERROR); } } if (range_max) { key_max = key_max_entity; storage_encode_key(field, range_max->key, key_max, &size_max); if (size_max == 0) { DBUG_RETURN(HA_POS_ERROR); } } } if (range_min) { DBUG_PRINT("info", ("mroonga: range_min->flag=%u", range_min->flag)); if (range_min->flag == HA_READ_AFTER_KEY) { flags |= GRN_CURSOR_GT; } } if (range_max) { DBUG_PRINT("info", ("mroonga: range_min->flag=%u", range_max->flag)); if (range_max->flag == HA_READ_BEFORE_KEY) { flags |= GRN_CURSOR_LT; } } int cursor_limit = THDVAR(ha_thd(), max_n_records_for_estimate); uint pkey_nr = table->s->primary_key; if (key_nr == pkey_nr) { DBUG_PRINT("info", ("mroonga: use primary key")); grn_table_cursor *cursor; cursor = grn_table_cursor_open(ctx, grn_table, key_min, size_min, key_max, size_max, 0, cursor_limit, flags); while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) { row_count++; } grn_table_cursor_close(ctx, cursor); } else { if (is_multiple_column_index) { DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr)); } else { DBUG_PRINT("info", ("mroonga: use key%u", key_nr)); } grn_table_cursor *cursor; cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr], key_min, size_min, key_max, size_max, 0, cursor_limit, flags); grn_obj *index_column = grn_index_columns[key_nr]; grn_ii *ii = reinterpret_cast(index_column); row_count = grn_ii_estimate_size_for_lexicon_cursor(ctx, ii, cursor); grn_table_cursor_close(ctx, cursor); unsigned int max_n_lexicon_records = grn_table_size(ctx, grn_index_tables[key_nr]); if (cursor_limit >= 0 && static_cast(cursor_limit) < max_n_lexicon_records) { row_count++; } } DBUG_RETURN(row_count); } ha_rows ha_mroonga::generic_records_in_range_geo(uint key_nr, const key_range *range_min, const key_range *range_max) { MRN_DBUG_ENTER_METHOD(); ha_rows row_count; int error; if (!range_min) { DBUG_PRINT("info", ("mroonga: range min is missing for geometry range search")); DBUG_RETURN(HA_POS_ERROR); } if (range_max) { DBUG_PRINT("info", ("mroonga: range max is specified for geometry range search")); DBUG_RETURN(HA_POS_ERROR); } error = mrn_change_encoding(ctx, table->key_info[key_nr].key_part->field->charset()); if (error) DBUG_RETURN(error); if (!(range_min->flag & HA_READ_MBR_CONTAIN)) { push_warning_unsupported_spatial_index_search(range_min->flag); row_count = grn_table_size(ctx, grn_table); DBUG_RETURN(row_count); } geo_store_rectangle(range_min->key); row_count = grn_geo_estimate_in_rectangle(ctx, grn_index_columns[key_nr], &top_left_point, &bottom_right_point); DBUG_RETURN(row_count); } ha_rows ha_mroonga::records_in_range(uint key_nr, const key_range *range_min, const key_range *range_max, page_range *pages) { MRN_DBUG_ENTER_METHOD(); ha_rows row_count = 0; if (share->wrapper_mode) { row_count = wrapper_records_in_range(key_nr, range_min, range_max, pages); } else { row_count = storage_records_in_range(key_nr, range_min, range_max, pages); } DBUG_PRINT("info", ("mroonga: row_count=%" MRN_HA_ROWS_FORMAT, row_count)); DBUG_RETURN(row_count); } int ha_mroonga::wrapper_index_init(uint idx, bool sorted) { MRN_DBUG_ENTER_METHOD(); int error = 0; KEY *key_info = &(table->s->key_info[idx]); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (!mrn_is_geo_key(key_info) && key_info->algorithm != HA_KEY_ALG_FULLTEXT) { error = wrap_handler->ha_index_init(share->wrap_key_nr[idx], sorted); } else { error = wrap_handler->ha_index_init(share->wrap_primary_key, sorted); } MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_index_init(uint idx, bool sorted) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::index_init(uint idx, bool sorted) { MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: idx=%u", idx)); active_index = idx; int error = 0; if (share->wrapper_mode) { error = wrapper_index_init(idx, sorted); } else { error = storage_index_init(idx, sorted); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_end() { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_index_or_rnd_end(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_index_end() { MRN_DBUG_ENTER_METHOD(); clear_cursor(); clear_cursor_geo(); DBUG_RETURN(0); } int ha_mroonga::index_end() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_end(); } else { error = storage_index_end(); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map, enum ha_rkey_function find_flag) { int error = 0; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { clear_cursor_geo(); error = generic_geo_open_cursor(key, find_flag); if (!error) { error = wrapper_get_next_geo_record(buf); } DBUG_RETURN(error); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP error = wrap_handler->ha_index_read_map(buf, key, keypart_map, find_flag); #else error = wrap_handler->index_read_map(buf, key, keypart_map, find_flag); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(error); } int ha_mroonga::storage_index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map, enum ha_rkey_function find_flag) { MRN_DBUG_ENTER_METHOD(); check_count_skip(keypart_map); int error = 0; uint key_nr = active_index; KEY *key_info = &(table->key_info[key_nr]); int flags = 0; uint size_min = 0, size_max = 0; uchar *key_min = NULL, *key_max = NULL; uchar key_min_entity[MRN_MAX_KEY_SIZE]; uchar key_max_entity[MRN_MAX_KEY_SIZE]; clear_cursor(); clear_cursor_geo(); clear_empty_value_records(); switch (find_flag) { case HA_READ_BEFORE_KEY: flags |= GRN_CURSOR_LT | GRN_CURSOR_DESCENDING; break; case HA_READ_PREFIX_LAST: flags |= GRN_CURSOR_PREFIX | GRN_CURSOR_DESCENDING; break; case HA_READ_PREFIX_LAST_OR_PREV: flags |= GRN_CURSOR_LE | GRN_CURSOR_DESCENDING; break; case HA_READ_AFTER_KEY: flags |= GRN_CURSOR_GT | GRN_CURSOR_ASCENDING; break; case HA_READ_KEY_OR_NEXT: flags |= GRN_CURSOR_GE | GRN_CURSOR_ASCENDING; break; case HA_READ_KEY_EXACT: flags |= GRN_CURSOR_LE | GRN_CURSOR_GE; break; default: break; } bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1; if (is_multiple_column_index) { mrn_change_encoding(ctx, NULL); uint key_length = mrn_calculate_key_len(table, active_index, key, keypart_map); DBUG_PRINT("info", ("mroonga: multiple column index: " "search key length=<%u>, " "multiple column index key length=<%u>", key_length, key_info->key_length)); if (key_length == key_info->key_length) { switch (find_flag) { case HA_READ_BEFORE_KEY: case HA_READ_PREFIX_LAST_OR_PREV: key_max = key_max_entity; storage_encode_multiple_column_key(key_info, key, key_length, key_max, &size_max); break; case HA_READ_PREFIX_LAST: key_min = key_min_entity; storage_encode_multiple_column_key(key_info, key, key_length, key_min, &size_min); break; default: key_min = key_min_entity; storage_encode_multiple_column_key(key_info, key, key_length, key_min, &size_min); if (find_flag == HA_READ_KEY_EXACT) { key_max = key_min; size_max = size_min; } break; } } else { const uchar *prev_key = NULL; uint prev_key_length = 0; if ((keypart_map >> 1) > 0) { prev_key = key; prev_key_length = mrn_calculate_key_len(table, active_index, key, keypart_map >> 1); } switch (find_flag) { case HA_READ_BEFORE_KEY: if (prev_key) { flags |= GRN_CURSOR_GE; key_min = key_min_entity; storage_encode_multiple_column_key_range(key_info, prev_key, prev_key_length, NULL, 0, key_min, &size_min, NULL, NULL); } key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, key, key_length, NULL, 0, key_max, &size_max, NULL, NULL); break; case HA_READ_PREFIX_LAST: key_min = key_min_entity; storage_encode_multiple_column_key(key_info, key, key_length, key_min, &size_min); break; case HA_READ_PREFIX_LAST_OR_PREV: if (prev_key) { flags |= GRN_CURSOR_GE; key_min = key_min_entity; storage_encode_multiple_column_key_range(key_info, prev_key, prev_key_length, NULL, 0, key_min, &size_min, NULL, NULL); } key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, NULL, 0, key, key_length, NULL, NULL, key_max, &size_max); break; case HA_READ_AFTER_KEY: key_min = key_min_entity; storage_encode_multiple_column_key_range(key_info, NULL, 0, key, key_length, NULL, NULL, key_min, &size_min); if (prev_key) { flags |= GRN_CURSOR_LE; key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, NULL, 0, prev_key, prev_key_length, NULL, NULL, key_max, &size_max); } break; case HA_READ_KEY_OR_NEXT: key_min = key_min_entity; storage_encode_multiple_column_key_range(key_info, key, key_length, NULL, 0, key_min, &size_min, NULL, NULL); if (prev_key) { flags |= GRN_CURSOR_LE; key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, NULL, 0, prev_key, prev_key_length, NULL, NULL, key_max, &size_max); } break; case HA_READ_KEY_EXACT: key_min = key_min_entity; key_max = key_max_entity; storage_encode_multiple_column_key_range(key_info, key, key_length, key, key_length, key_min, &size_min, key_max, &size_max); default: break; } } } else if (mrn_is_geo_key(key_info)) { error = mrn_change_encoding(ctx, key_info->key_part->field->charset()); if (error) DBUG_RETURN(error); error = generic_geo_open_cursor(key, find_flag); if (!error) { error = storage_get_next_record(buf); } DBUG_RETURN(error); } else { Field *field = key_info->key_part[0].field; error = mrn_change_encoding(ctx, field->charset()); if (error) DBUG_RETURN(error); if (find_flag == HA_READ_KEY_EXACT) { const char *column_name = field->field_name.str; key_min = key_min_entity; key_max = key_min_entity; storage_encode_key(field, key, key_min, &size_min); size_max = size_min; // for _id if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) { grn_id found_record_id = *((grn_id *)key_min); if (grn_table_at(ctx, grn_table, found_record_id) != GRN_ID_NIL) { // found storage_store_fields(buf, found_record_id); table->status = 0; record_id = found_record_id; DBUG_RETURN(0); } else { table->status = STATUS_NOT_FOUND; DBUG_RETURN(HA_ERR_END_OF_FILE); } } } else if (find_flag == HA_READ_BEFORE_KEY || find_flag == HA_READ_PREFIX_LAST_OR_PREV) { key_max = key_max_entity; storage_encode_key(field, key, key_max_entity, &size_max); } else { key_min = key_min_entity; storage_encode_key(field, key, key_min_entity, &size_min); } } uint pkey_nr = table->s->primary_key; if (key_nr == pkey_nr) { DBUG_PRINT("info", ("mroonga: use primary key")); cursor = grn_table_cursor_open(ctx, grn_table, key_min, size_min, key_max, size_max, 0, -1, flags); } else { bool is_empty_value_records_search = false; if (is_multiple_column_index) { DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr)); } else if (flags == 0 && size_min == 0 && size_max == 0) { is_empty_value_records_search = true; DBUG_PRINT("info", ("mroonga: use table scan for searching empty value records")); } else { DBUG_PRINT("info", ("mroonga: use key%u", key_nr)); } if (is_empty_value_records_search) { grn_obj *expression, *expression_variable; GRN_EXPR_CREATE_FOR_QUERY(ctx, grn_table, expression, expression_variable); grn_obj *target_column = grn_columns[key_info->key_part->field->field_index]; grn_expr_append_const(ctx, expression, target_column, GRN_OP_GET_VALUE, 1); grn_obj empty_value; GRN_TEXT_INIT(&empty_value, 0); grn_expr_append_obj(ctx, expression, &empty_value, GRN_OP_PUSH, 1); grn_expr_append_op(ctx, expression, GRN_OP_EQUAL, 2); empty_value_records = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC, grn_table, 0); grn_table_select(ctx, grn_table, expression, empty_value_records, GRN_OP_OR); grn_obj_unlink(ctx, expression); grn_obj_unlink(ctx, &empty_value); empty_value_records_cursor = grn_table_cursor_open(ctx, empty_value_records, NULL, 0, NULL, 0, 0, -1, flags); } else { index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr], key_min, size_min, key_max, size_max, 0, -1, flags); cursor = grn_index_cursor_open(ctx, index_table_cursor, grn_index_columns[key_nr], 0, GRN_ID_MAX, 0); } } if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map, enum ha_rkey_function find_flag) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_read_map(buf, key, keypart_map, find_flag); } else { error = storage_index_read_map(buf, key, keypart_map, find_flag); } DBUG_PRINT("info", ("mroonga: error=%d", error)); DBUG_RETURN(error); } #ifdef MRN_HANDLER_HAVE_INDEX_READ_LAST_MAP int ha_mroonga::wrapper_index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); # ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_LAST_MAP error = wrap_handler->ha_index_read_last_map(buf, key, keypart_map); # else error = wrap_handler->index_read_last_map(buf, key, keypart_map); # endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map) { MRN_DBUG_ENTER_METHOD(); uint key_nr = active_index; KEY *key_info = &(table->key_info[key_nr]); int flags = GRN_CURSOR_DESCENDING, error; uint size_min = 0, size_max = 0; uchar *key_min = NULL, *key_max = NULL; uchar key_min_entity[MRN_MAX_KEY_SIZE]; clear_cursor(); bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1; if (is_multiple_column_index) { mrn_change_encoding(ctx, NULL); flags |= GRN_CURSOR_PREFIX; uint key_length = mrn_calculate_key_len(table, active_index, key, keypart_map); key_min = key_min_entity; storage_encode_multiple_column_key(key_info, key, key_length, key_min, &size_min); } else { Field *field = key_info->key_part[0].field; error = mrn_change_encoding(ctx, field->charset()); if (error) DBUG_RETURN(error); key_min = key_min_entity; key_max = key_min_entity; storage_encode_key(field, key, key_min, &size_min); size_max = size_min; } uint pkey_nr = table->s->primary_key; if (key_nr == pkey_nr) { DBUG_PRINT("info", ("mroonga: use primary key")); cursor = grn_table_cursor_open(ctx, grn_table, key_min, size_min, key_max, size_max, 0, -1, flags); } else { if (is_multiple_column_index) { DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr)); } else { DBUG_PRINT("info", ("mroonga: use key%u", key_nr)); } index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr], key_min, size_min, key_max, size_max, 0, -1, flags); cursor = grn_index_cursor_open(ctx, index_table_cursor, grn_index_columns[key_nr], 0, GRN_ID_MAX, 0); } if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_read_last_map(buf, key, keypart_map); } else { error = storage_index_read_last_map(buf, key, keypart_map); } DBUG_RETURN(error); } #endif int ha_mroonga::wrapper_index_next(uchar *buf) { int error = 0; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = wrapper_get_next_geo_record(buf); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT error = wrap_handler->ha_index_next(buf); #else error = wrap_handler->index_next(buf); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(error); } int ha_mroonga::storage_index_next(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_next(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_next(buf); } else { error = storage_index_next(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_prev(uchar *buf) { int error = 0; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = wrapper_get_next_geo_record(buf); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT error = wrap_handler->ha_index_prev(buf); #else error = wrap_handler->index_prev(buf); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(error); } int ha_mroonga::storage_index_prev(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_prev(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_prev(buf); } else { error = storage_index_prev(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_first(uchar *buf) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_FIRST error = wrap_handler->ha_index_first(buf); #else error = wrap_handler->index_first(buf); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_index_first(uchar *buf) { MRN_DBUG_ENTER_METHOD(); clear_cursor(); int flags = GRN_CURSOR_ASCENDING; uint pkey_nr = table->s->primary_key; mrn_change_encoding(ctx, NULL); if (active_index == pkey_nr) { DBUG_PRINT("info", ("mroonga: use primary key")); cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0, 0, -1, flags); } else { if (KEY_N_KEY_PARTS(&(table->key_info[active_index])) > 1) { DBUG_PRINT("info", ("mroonga: use multiple column key%u", active_index)); } else { DBUG_PRINT("info", ("mroonga: use key%u", active_index)); } index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[active_index], NULL, 0, NULL, 0, 0, -1, flags); cursor = grn_index_cursor_open(ctx, index_table_cursor, grn_index_columns[active_index], 0, GRN_ID_MAX, 0); } if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } int error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_first(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_first(buf); } else { error = storage_index_first(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_last(uchar *buf) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_LAST error = wrap_handler->ha_index_last(buf); #else error = wrap_handler->index_last(buf); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_index_last(uchar *buf) { MRN_DBUG_ENTER_METHOD(); clear_cursor(); int flags = GRN_CURSOR_DESCENDING; uint pkey_nr = table->s->primary_key; mrn_change_encoding(ctx, NULL); if (active_index == pkey_nr) { DBUG_PRINT("info", ("mroonga: use primary key")); cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0, 0, -1, flags); } else { if (KEY_N_KEY_PARTS(&(table->key_info[active_index])) > 1) { DBUG_PRINT("info", ("mroonga: use multiple column key%u", active_index)); } else { DBUG_PRINT("info", ("mroonga: use key%u", active_index)); } index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[active_index], NULL, 0, NULL, 0, 0, -1, flags); cursor = grn_index_cursor_open(ctx, index_table_cursor, grn_index_columns[active_index], 0, GRN_ID_MAX, 0); } if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } int error = storage_get_next_record(buf); DBUG_RETURN(error); } int ha_mroonga::index_last(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_last(buf); } else { error = storage_index_last(buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_index_next_same(uchar *buf, const uchar *key, uint keylen) { MRN_DBUG_ENTER_METHOD(); int error = 0; KEY *key_info = &(table->s->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = wrapper_get_next_geo_record(buf); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); #ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT_SAME error = wrap_handler->ha_index_next_same(buf, key, keylen); #else error = wrap_handler->index_next_same(buf, key, keylen); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(error); } int ha_mroonga::storage_index_next_same(uchar *buf, const uchar *key, uint keylen) { MRN_DBUG_ENTER_METHOD(); int error = storage_get_next_record(count_skip ? NULL : buf); DBUG_RETURN(error); } int ha_mroonga::index_next_same(uchar *buf, const uchar *key, uint keylen) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_index_next_same(buf, key, keylen); } else { error = storage_index_next_same(buf, key, keylen); } DBUG_RETURN(error); } int ha_mroonga::generic_ft_init() { MRN_DBUG_ENTER_METHOD(); struct st_mrn_ft_info *mrn_ft_info = reinterpret_cast(ft_handler); GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding); int error = 0; if (sorted_result) { mrn_ft_info->cursor = grn_table_cursor_open(ctx, sorted_result, NULL, 0, NULL, 0, 0, -1, 0); } else { mrn_ft_info->cursor = grn_table_cursor_open(ctx, mrn_ft_info->result, NULL, 0, NULL, 0, 0, -1, 0); } if (ctx->rc) { error = ER_ERROR_ON_READ; my_message(error, ctx->errbuf, MYF(0)); } else { if (sorted_result) { if (grn_table->header.type == GRN_TABLE_NO_KEY) { mrn_ft_info->id_accessor = grn_obj_column(ctx, sorted_result, MRN_COLUMN_NAME_ID, strlen(MRN_COLUMN_NAME_ID)); } else { mrn_ft_info->key_accessor = grn_obj_column(ctx, sorted_result, MRN_COLUMN_NAME_KEY, strlen(MRN_COLUMN_NAME_KEY)); } } else { mrn_ft_info->key_accessor = grn_obj_column(ctx, mrn_ft_info->result, MRN_COLUMN_NAME_KEY, strlen(MRN_COLUMN_NAME_KEY)); } } DBUG_RETURN(error); } int ha_mroonga::wrapper_ft_init() { MRN_DBUG_ENTER_METHOD(); int error = generic_ft_init(); DBUG_RETURN(error); } int ha_mroonga::storage_ft_init() { MRN_DBUG_ENTER_METHOD(); int error = generic_ft_init(); record_id = GRN_ID_NIL; DBUG_RETURN(error); } int ha_mroonga::ft_init() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_ft_init(); } else { error = storage_ft_init(); } DBUG_RETURN(error); } void ha_mroonga::generic_ft_init_ext_add_conditions_fast_order_limit( struct st_mrn_ft_info *info, grn_obj *expression) { MRN_DBUG_ENTER_METHOD(); Item *where = MRN_SELECT_LEX_GET_WHERE_COND(table->pos_in_table_list->select_lex); bool is_storage_mode = !(share->wrapper_mode); mrn::ConditionConverter converter(info->ctx, grn_table, is_storage_mode); converter.convert(where, expression); DBUG_VOID_RETURN; } grn_rc ha_mroonga::generic_ft_init_ext_prepare_expression_in_boolean_mode( struct st_mrn_ft_info *info, String *key, grn_obj *index_column, grn_obj *match_columns, grn_obj *expression) { MRN_DBUG_ENTER_METHOD(); mrn::QueryParser query_parser(info->ctx, ha_thd(), expression, index_column, KEY_N_KEY_PARTS(info->key_info), match_columns); grn_rc rc = query_parser.parse(key->ptr(), key->length()); DBUG_RETURN(rc); } grn_rc ha_mroonga::generic_ft_init_ext_prepare_expression_in_normal_mode( struct st_mrn_ft_info *info, String *key, grn_obj *index_column, grn_obj *match_columns, grn_obj *expression) { MRN_DBUG_ENTER_METHOD(); grn_rc rc = GRN_SUCCESS; grn_obj query; GRN_TEXT_INIT(&query, GRN_OBJ_DO_SHALLOW_COPY); GRN_TEXT_SET(info->ctx, &query, key->ptr(), key->length()); grn_expr_append_obj(info->ctx, match_columns, index_column, GRN_OP_PUSH, 1); grn_expr_append_obj(info->ctx, expression, match_columns, GRN_OP_PUSH, 1); grn_expr_append_const(info->ctx, expression, &query, GRN_OP_PUSH, 1); grn_expr_append_op(info->ctx, expression, GRN_OP_SIMILAR, 2); grn_obj_unlink(info->ctx, &query); DBUG_RETURN(rc); } struct st_mrn_ft_info *ha_mroonga::generic_ft_init_ext_select(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); struct st_mrn_ft_info *info = new st_mrn_ft_info(); info->mroonga = this; info->ctx = ctx; mrn_change_encoding(info->ctx, table->key_info[key_nr].key_part->field->charset()); info->encoding = GRN_CTX_GET_ENCODING(info->ctx); info->table = grn_table; info->result = grn_table_create(info->ctx, NULL, 0, NULL, GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC, grn_table, 0); if (!info->result) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "[mroonga][ft-init] failed to create a table " "to store matched records for one search: <%s>", ctx->errbuf); my_message(ER_ERROR_ON_READ, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); delete info; DBUG_RETURN(NULL); } info->score_column = grn_obj_column(info->ctx, info->result, MRN_COLUMN_NAME_SCORE, strlen(MRN_COLUMN_NAME_SCORE)); GRN_TEXT_INIT(&(info->key), 0); grn_bulk_space(info->ctx, &(info->key), table->key_info->key_length); GRN_INT32_INIT(&(info->score), 0); info->active_index = key_nr; info->key_info = &(table->key_info[key_nr]); info->primary_key_info = &(table->key_info[table_share->primary_key]); info->cursor = NULL; info->id_accessor = NULL; info->key_accessor = NULL; if (key->length() == 0) { DBUG_RETURN(info); } grn_obj *index_column = grn_index_columns[key_nr]; grn_obj *match_columns, *match_columns_variable; GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->table, match_columns, match_columns_variable); grn_obj *expression, *expression_variable; GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->table, expression, expression_variable); grn_rc rc = GRN_SUCCESS; if (flags & FT_BOOL) { rc = generic_ft_init_ext_prepare_expression_in_boolean_mode(info, key, index_column, match_columns, expression); } else { rc = generic_ft_init_ext_prepare_expression_in_normal_mode(info, key, index_column, match_columns, expression); } if (rc == GRN_SUCCESS) { if (fast_order_limit) { generic_ft_init_ext_add_conditions_fast_order_limit(info, expression); } longlong escalation_threshold = THDVAR(ha_thd(), match_escalation_threshold); mrn::MatchEscalationThresholdScope scope(info->ctx, escalation_threshold); grn_table_select(info->ctx, info->table, expression, info->result, GRN_OP_OR); } grn_obj_unlink(info->ctx, expression); grn_obj_unlink(info->ctx, match_columns); DBUG_RETURN(info); } FT_INFO *ha_mroonga::generic_ft_init_ext(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); check_count_skip(0); mrn_change_encoding(ctx, system_charset_info); grn_operator operation = GRN_OP_OR; if (!matched_record_keys) { matched_record_keys = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC, grn_table, 0); if (!matched_record_keys) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "[mroonga][ft-init] " "failed to create a table to store all matched records: <%s>", ctx->errbuf); my_message(ER_ERROR_ON_READ, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); DBUG_RETURN(NULL); } } grn_table_sort_key *sort_keys = NULL; int n_sort_keys = 0; longlong limit = -1; check_fast_order_limit(&sort_keys, &n_sort_keys, &limit); struct st_mrn_ft_info *info = generic_ft_init_ext_select(flags, key_nr, key); if (!info) { DBUG_RETURN(NULL); } grn_rc rc; rc = grn_table_setoperation(ctx, matched_record_keys, info->result, matched_record_keys, operation); if (rc) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to merge matched record keys: <%s>", ctx->errbuf); my_message(ER_ERROR_ON_READ, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); } if (fast_order_limit) { if (sorted_result) { grn_obj_close(ctx, sorted_result); } sorted_result = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, NULL, matched_record_keys); grn_table_sort(ctx, matched_record_keys, 0, static_cast(limit), sorted_result, sort_keys, n_sort_keys); } else if (flags & FT_SORTED) { grn_table_sort_key score_sort_key; score_sort_key.key = grn_obj_column(ctx, matched_record_keys, MRN_COLUMN_NAME_SCORE, strlen(MRN_COLUMN_NAME_SCORE)); score_sort_key.offset = 0; score_sort_key.flags = GRN_TABLE_SORT_DESC; if (sorted_result) { grn_obj_unlink(ctx, sorted_result); } sorted_result = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, NULL, matched_record_keys); grn_table_sort(ctx, matched_record_keys, 0, -1, sorted_result, &score_sort_key, 1); grn_obj_unlink(ctx, score_sort_key.key); } if (sort_keys) { for (int i = 0; i < n_sort_keys; i++) { grn_obj_unlink(info->ctx, sort_keys[i].key); } my_free(sort_keys); } DBUG_RETURN((FT_INFO *)info); } FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); FT_INFO *info = generic_ft_init_ext(flags, key_nr, key); if (!info) { DBUG_RETURN(NULL); } struct st_mrn_ft_info *mrn_ft_info = (struct st_mrn_ft_info *)info; mrn_ft_info->please = &mrn_wrapper_ft_vft; #ifdef HA_CAN_FULLTEXT_EXT mrn_ft_info->could_you = &mrn_wrapper_ft_vft_ext; #endif ++wrap_ft_init_count; DBUG_RETURN(info); } FT_INFO *ha_mroonga::storage_ft_init_ext(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); FT_INFO *info = generic_ft_init_ext(flags, key_nr, key); if (!info) { DBUG_RETURN(NULL); } struct st_mrn_ft_info *mrn_ft_info = (struct st_mrn_ft_info *)info; mrn_ft_info->please = &mrn_storage_ft_vft; #ifdef HA_CAN_FULLTEXT_EXT mrn_ft_info->could_you = &mrn_storage_ft_vft_ext; #endif DBUG_RETURN(info); } FT_INFO *ha_mroonga::ft_init_ext(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); fulltext_searching = true; FT_INFO *info; if (key_nr == NO_SUCH_KEY) { struct st_mrn_ft_info *mrn_ft_info = new st_mrn_ft_info(); mrn_ft_info->please = &mrn_no_such_key_ft_vft; #ifdef HA_CAN_FULLTEXT_EXT mrn_ft_info->could_you = &mrn_no_such_key_ft_vft_ext; #endif info = (FT_INFO *)mrn_ft_info; } else { if (share->wrapper_mode) { info = wrapper_ft_init_ext(flags, key_nr, key); } else { info = storage_ft_init_ext(flags, key_nr, key); } } DBUG_RETURN(info); } int ha_mroonga::wrapper_ft_read(uchar *buf) { MRN_DBUG_ENTER_METHOD(); if (wrap_ft_init_count) set_pk_bitmap(); struct st_mrn_ft_info *mrn_ft_info = reinterpret_cast(ft_handler); GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding); int error = 0; do { grn_id found_record_id; found_record_id = grn_table_cursor_next(ctx, mrn_ft_info->cursor); if (found_record_id == GRN_ID_NIL) { error = HA_ERR_END_OF_FILE; break; } GRN_BULK_REWIND(&key_buffer); if (mrn_ft_info->key_accessor) { grn_obj_get_value(ctx, mrn_ft_info->key_accessor, found_record_id, &key_buffer); } else { void *key; int key_length; key_length = grn_table_cursor_get_key(ctx, mrn_ft_info->cursor, &key); GRN_TEXT_SET(ctx, &key_buffer, key, key_length); } error = wrapper_get_record(buf, (const uchar *)GRN_TEXT_VALUE(&key_buffer)); } while (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND); DBUG_RETURN(error); } int ha_mroonga::storage_ft_read(uchar *buf) { MRN_DBUG_ENTER_METHOD(); struct st_mrn_ft_info *mrn_ft_info = reinterpret_cast(ft_handler); GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding); grn_id found_record_id; found_record_id = grn_table_cursor_next(ctx, mrn_ft_info->cursor); if (ctx->rc) { my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } if (found_record_id == GRN_ID_NIL) { table->status = STATUS_NOT_FOUND; DBUG_RETURN(HA_ERR_END_OF_FILE); } table->status = 0; if (count_skip && record_id != GRN_ID_NIL) { DBUG_RETURN(0); } GRN_BULK_REWIND(&key_buffer); if (mrn_ft_info->id_accessor) { grn_obj id_buffer; GRN_RECORD_INIT(&id_buffer, 0, grn_obj_id(ctx, grn_table)); grn_obj_get_value(ctx, mrn_ft_info->id_accessor, found_record_id, &id_buffer); record_id = GRN_RECORD_VALUE(&id_buffer); } else if (mrn_ft_info->key_accessor) { grn_obj_get_value(ctx, mrn_ft_info->key_accessor, found_record_id, &key_buffer); record_id = grn_table_get(ctx, grn_table, GRN_TEXT_VALUE(&key_buffer), GRN_TEXT_LEN(&key_buffer)); } else { void *key; grn_table_cursor_get_key(ctx, mrn_ft_info->cursor, &key); if (ctx->rc) { record_id = GRN_ID_NIL; my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_READ); } else { record_id = *((grn_id *)key); } } storage_store_fields(buf, record_id); DBUG_RETURN(0); } int ha_mroonga::ft_read(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_ft_read(buf); } else { error = storage_ft_read(buf); } DBUG_RETURN(error); } const Item *ha_mroonga::wrapper_cond_push(const Item *cond) { const Item *reminder_cond; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); reminder_cond = wrap_handler->cond_push(cond); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(reminder_cond); } const Item *ha_mroonga::storage_cond_push(const Item *cond) { MRN_DBUG_ENTER_METHOD(); const Item *reminder_cond = cond; if (!pushed_cond) { mrn::ConditionConverter converter(ctx, grn_table, true); if (converter.count_match_against(cond) == 1 && converter.is_convertable(cond)) { reminder_cond = NULL; } } DBUG_RETURN(reminder_cond); } const Item *ha_mroonga::cond_push(const Item *cond) { MRN_DBUG_ENTER_METHOD(); const Item *reminder_cond; if (share->wrapper_mode) { reminder_cond = wrapper_cond_push(cond); } else { reminder_cond = storage_cond_push(cond); } DBUG_RETURN(reminder_cond); } void ha_mroonga::wrapper_cond_pop() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->cond_pop(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_cond_pop() { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } void ha_mroonga::cond_pop() { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) wrapper_cond_pop(); else storage_cond_pop(); DBUG_VOID_RETURN; } bool ha_mroonga::wrapper_get_error_message(int error, String *buf) { bool temporary_error; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); temporary_error = wrap_handler->get_error_message(error, buf); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(temporary_error); } bool ha_mroonga::storage_get_error_message(int error, String *buf) { MRN_DBUG_ENTER_METHOD(); bool temporary_error = false; // latest error message buf->copy(ctx->errbuf, (uint) strlen(ctx->errbuf), system_charset_info); DBUG_RETURN(temporary_error); } bool ha_mroonga::get_error_message(int error, String *buf) { MRN_DBUG_ENTER_METHOD(); bool temporary_error; if (share && share->wrapper_mode) { temporary_error = wrapper_get_error_message(error, buf); } else { temporary_error = storage_get_error_message(error, buf); } DBUG_RETURN(temporary_error); } ulonglong ha_mroonga::file_size(const char *path) { MRN_DBUG_ENTER_METHOD(); struct stat file_status; if (stat(path, &file_status) == 0) { DBUG_RETURN(file_status.st_size); } else { DBUG_RETURN(0); } } bool ha_mroonga::have_unique_index() { MRN_DBUG_ENTER_METHOD(); uint n_keys = table->s->keys; for (uint i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if (key_info->flags & HA_NOSAME) { DBUG_RETURN(true); } } DBUG_RETURN(false); } bool ha_mroonga::is_foreign_key_field(const char *table_name, const char *field_name) { MRN_DBUG_ENTER_METHOD(); grn_obj *table = grn_ctx_get(ctx, table_name, -1); if (!table) { DBUG_RETURN(false); } mrn::ColumnName column_name(field_name); grn_obj *column = grn_obj_column(ctx, table, column_name.c_str(), column_name.length()); if (!column) { DBUG_RETURN(false); } grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); if (!range) { grn_obj_unlink(ctx, column); DBUG_RETURN(false); } if (!mrn::grn::is_table(range)) { grn_obj_unlink(ctx, column); DBUG_RETURN(false); } grn_obj *foreign_index_column; mrn::IndexColumnName index_column_name(table_name, field_name); foreign_index_column = grn_obj_column(ctx, range, index_column_name.c_str(), index_column_name.length()); if (foreign_index_column) { grn_obj_unlink(ctx, foreign_index_column); DBUG_RETURN(true); } grn_obj_unlink(ctx, column); DBUG_RETURN(false); } void ha_mroonga::push_warning_unsupported_spatial_index_search(enum ha_rkey_function flag) { char search_name[MRN_BUFFER_SIZE]; if (flag == HA_READ_MBR_INTERSECT) { strcpy(search_name, "intersect"); } else if (flag == HA_READ_MBR_WITHIN) { strcpy(search_name, "within"); } else if (flag & HA_READ_MBR_DISJOINT) { strcpy(search_name, "disjoint"); } else if (flag & HA_READ_MBR_EQUAL) { strcpy(search_name, "equal"); } else { sprintf(search_name, "unknown: %d", flag); } push_warning_printf(ha_thd(), MRN_SEVERITY_WARNING, ER_UNSUPPORTED_EXTENSION, "spatial index search " "except MBRContains aren't supported: <%s>", search_name); } void ha_mroonga::clear_cursor() { MRN_DBUG_ENTER_METHOD(); if (cursor) { grn_obj_unlink(ctx, cursor); cursor = NULL; } if (index_table_cursor) { grn_table_cursor_close(ctx, index_table_cursor); index_table_cursor = NULL; } DBUG_VOID_RETURN; } void ha_mroonga::clear_cursor_geo() { MRN_DBUG_ENTER_METHOD(); if (cursor_geo) { grn_obj_unlink(ctx, cursor_geo); cursor_geo = NULL; } DBUG_VOID_RETURN; } void ha_mroonga::clear_empty_value_records() { MRN_DBUG_ENTER_METHOD(); if (empty_value_records_cursor) { grn_table_cursor_close(ctx, empty_value_records_cursor); empty_value_records_cursor = NULL; } if (empty_value_records) { grn_obj_unlink(ctx, empty_value_records); empty_value_records = NULL; } DBUG_VOID_RETURN; } void ha_mroonga::clear_search_result() { MRN_DBUG_ENTER_METHOD(); clear_cursor(); if (sorted_result) { grn_obj_unlink(ctx, sorted_result); sorted_result = NULL; } if (matched_record_keys) { grn_obj_unlink(ctx, matched_record_keys); matched_record_keys = NULL; } DBUG_VOID_RETURN; } void ha_mroonga::clear_search_result_geo() { MRN_DBUG_ENTER_METHOD(); clear_cursor_geo(); if (grn_source_column_geo) { grn_obj_unlink(ctx, grn_source_column_geo); grn_source_column_geo = NULL; } DBUG_VOID_RETURN; } void ha_mroonga::clear_indexes() { MRN_DBUG_ENTER_METHOD(); uint n_keys = table->s->keys; uint pkey_nr = table->s->primary_key; for (uint i = 0; i < n_keys; i++) { if (i != pkey_nr) { if (grn_index_tables) { grn_obj_unlink(ctx, grn_index_tables[i]); } if (grn_index_columns) { grn_obj_unlink(ctx, grn_index_columns[i]); } } } if (grn_index_tables) { free(grn_index_tables); grn_index_tables = NULL; } if (grn_index_columns) { free(grn_index_columns); grn_index_columns = NULL; } if (key_id) { free(key_id); key_id = NULL; } if (del_key_id) { free(del_key_id); del_key_id = NULL; } DBUG_VOID_RETURN; } int ha_mroonga::add_wrap_hton(const char *path, handlerton *wrap_handlerton) { MRN_DBUG_ENTER_METHOD(); st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), true); if (!slot_data) DBUG_RETURN(HA_ERR_OUT_OF_MEM); st_mrn_wrap_hton *wrap_hton = (st_mrn_wrap_hton *)malloc(sizeof(st_mrn_wrap_hton)); if (!wrap_hton) DBUG_RETURN(HA_ERR_OUT_OF_MEM); wrap_hton->next = NULL; strcpy(wrap_hton->path, path); wrap_hton->hton = wrap_handlerton; if (slot_data->first_wrap_hton) { st_mrn_wrap_hton *tmp_wrap_hton = slot_data->first_wrap_hton; while (tmp_wrap_hton->next) tmp_wrap_hton = tmp_wrap_hton->next; tmp_wrap_hton->next = wrap_hton; } else { slot_data->first_wrap_hton = wrap_hton; } DBUG_RETURN(0); } void ha_mroonga::remove_related_files(const char *base_path) { MRN_DBUG_ENTER_METHOD(); const char *base_directory_name = "."; size_t base_path_length = strlen(base_path); #ifdef WIN32 WIN32_FIND_DATA data; HANDLE finder = FindFirstFile(base_directory_name, &data); if (finder != INVALID_HANDLE_VALUE) { do { if (!(data.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)) { continue; } if (strncmp(data.cFileName, base_path, base_path_length) == 0) { unlink(data.cFileName); } } while (FindNextFile(finder, &data) != 0); FindClose(finder); } #else DIR *dir = opendir(base_directory_name); if (dir) { while (struct dirent *entry = readdir(dir)) { struct stat file_status; if (stat(entry->d_name, &file_status) != 0) { continue; } if (!((file_status.st_mode & S_IFMT) == S_IFREG)) { continue; } if (strncmp(entry->d_name, base_path, base_path_length) == 0) { unlink(entry->d_name); } } closedir(dir); } #endif DBUG_VOID_RETURN; } void ha_mroonga::remove_grn_obj_force(const char *name) { MRN_DBUG_ENTER_METHOD(); grn_obj *obj = grn_ctx_get(ctx, name, strlen(name)); if (obj) { grn_obj_remove(ctx, obj); } else { grn_obj *db = grn_ctx_db(ctx); grn_id id = grn_table_get(ctx, db, name, strlen(name)); if (id) { char path[MRN_MAX_PATH_SIZE]; grn_obj_delete_by_id(ctx, db, id, GRN_TRUE); if (grn_obj_path_by_id(ctx, db, id, path) == GRN_SUCCESS) { remove_related_files(path); } } } DBUG_VOID_RETURN; } int ha_mroonga::drop_index(MRN_SHARE *target_share, uint key_index) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_rc rc = GRN_SUCCESS; char target_name[GRN_TABLE_MAX_KEY_SIZE]; int target_name_length; KEY *key_info = target_share->table_share->key_info; if (!target_share->wrapper_mode && target_share->index_table[key_index]) { const char *table_name = target_share->index_table[key_index]; snprintf(target_name, GRN_TABLE_MAX_KEY_SIZE, "%s.%s", table_name, key_info[key_index].name.str); target_name_length = strlen(target_name); grn_obj *index_column = grn_ctx_get(ctx, target_name, target_name_length); if (index_column) { rc = grn_obj_remove(ctx, index_column); } } else { mrn::PathMapper mapper(target_share->table_name); mrn::IndexTableName index_table_name(mapper.table_name(), key_info[key_index].name.str); grn_obj *index_table = grn_ctx_get(ctx, index_table_name.c_str(), index_table_name.length()); if (!index_table) { index_table = grn_ctx_get(ctx, index_table_name.old_c_str(), index_table_name.old_length()); } if (index_table) { target_name_length = grn_obj_name(ctx, index_table, target_name, GRN_TABLE_MAX_KEY_SIZE); rc = grn_obj_remove(ctx, index_table); } else { target_name_length = 0; } } if (rc != GRN_SUCCESS) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to drop index: <%.*s>: <%s>", target_name_length, target_name, ctx->errbuf); my_message(ER_ERROR_ON_WRITE, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); } DBUG_RETURN(error); } int ha_mroonga::drop_indexes_normal(const char *table_name, grn_obj *table) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_hash *columns_raw = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_OBJ_TABLE_HASH_KEY); mrn::SmartGrnObj columns(ctx, reinterpret_cast(columns_raw)); if (!columns.get()) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to allocate columns buffer: <%s>: <%s>", table_name, ctx->errbuf); error = HA_ERR_OUT_OF_MEM; my_message(ER_ERROR_ON_WRITE, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); DBUG_RETURN(error); } grn_table_columns(ctx, table, "", 0, columns.get()); grn_table_cursor *cursor = grn_table_cursor_open(ctx, columns.get(), NULL, 0, NULL, 0, 0, -1, 0); if (!cursor) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to allocate columns cursor: <%s>: <%s>", table_name, ctx->errbuf); error = HA_ERR_OUT_OF_MEM; my_message(ER_ERROR_ON_WRITE, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); DBUG_RETURN(error); } while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) { void *key; grn_table_cursor_get_key(ctx, cursor, &key); grn_id *id = reinterpret_cast(key); mrn::SmartGrnObj column(ctx, grn_ctx_at(ctx, *id)); if (!column.get()) { continue; } grn_operator index_operators[] = { GRN_OP_EQUAL, GRN_OP_MATCH, GRN_OP_LESS, GRN_OP_REGEXP }; size_t n_index_operators = sizeof(index_operators) / sizeof(grn_operator); for (size_t i = 0; i < n_index_operators; i++) { grn_index_datum index_datum; while (grn_column_find_index_data(ctx, column.get(), index_operators[i], &index_datum, 1) > 0) { grn_id index_table_id = index_datum.index->header.domain; mrn::SmartGrnObj index_table(ctx, grn_ctx_at(ctx, index_table_id)); char index_table_name[GRN_TABLE_MAX_KEY_SIZE]; int index_table_name_length; index_table_name_length = grn_obj_name(ctx, index_table.get(), index_table_name, GRN_TABLE_MAX_KEY_SIZE); if (mrn::IndexTableName::is_custom_name(table_name, strlen(table_name), index_table_name, index_table_name_length)) { char index_column_name[GRN_TABLE_MAX_KEY_SIZE]; int index_column_name_length; index_column_name_length = grn_obj_name(ctx, index_datum.index, index_column_name, GRN_TABLE_MAX_KEY_SIZE); grn_rc rc = grn_obj_remove(ctx, index_datum.index); if (rc != GRN_SUCCESS) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to drop index column: <%.*s>: <%s>", index_column_name_length, index_column_name, ctx->errbuf); error = ER_ERROR_ON_WRITE; my_message(error, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); } } else { grn_rc rc = grn_obj_remove(ctx, index_table.get()); if (rc == GRN_SUCCESS) { index_table.release(); } else { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to drop index table: <%.*s>: <%s>", index_table_name_length, index_table_name, ctx->errbuf); error = ER_ERROR_ON_WRITE; my_message(error, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); } } if (error != 0) { break; } } if (error != 0) { break; } } if (error != 0) { break; } } grn_table_cursor_close(ctx, cursor); DBUG_RETURN(error); } int ha_mroonga::drop_indexes_multiple(const char *table_name, grn_obj *table, const char *index_table_name_separator) { MRN_DBUG_ENTER_METHOD(); int error = 0; char index_table_name_prefix[GRN_TABLE_MAX_KEY_SIZE]; snprintf(index_table_name_prefix, GRN_TABLE_MAX_KEY_SIZE, "%s%s", table_name, index_table_name_separator); grn_table_cursor *cursor = grn_table_cursor_open(ctx, grn_ctx_db(ctx), index_table_name_prefix, strlen(index_table_name_prefix), NULL, 0, 0, -1, GRN_CURSOR_PREFIX); if (!cursor) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to allocate index tables cursor: <%s>: <%s>", table_name, ctx->errbuf); error = HA_ERR_OUT_OF_MEM; my_message(ER_ERROR_ON_WRITE, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); DBUG_RETURN(error); } grn_id table_id = grn_obj_id(ctx, table); grn_id id; while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { mrn::SmartGrnObj object(ctx, grn_ctx_at(ctx, id)); if (!object.get()) { continue; } if (!grn_obj_is_table(ctx, object.get())) { continue; } char multiple_column_index_table_name[GRN_TABLE_MAX_KEY_SIZE]; int multiple_column_index_table_name_length; multiple_column_index_table_name_length = grn_obj_name(ctx, object.get(), multiple_column_index_table_name, GRN_TABLE_MAX_KEY_SIZE); char multiple_column_index_name[GRN_TABLE_MAX_KEY_SIZE]; snprintf(multiple_column_index_name, GRN_TABLE_MAX_KEY_SIZE, "%.*s.%s", multiple_column_index_table_name_length, multiple_column_index_table_name, INDEX_COLUMN_NAME); mrn::SmartGrnObj index_column(ctx, multiple_column_index_name); if (!index_column.get()) { continue; } if (grn_obj_get_range(ctx, index_column.get()) != table_id) { continue; } grn_rc rc = grn_obj_remove(ctx, object.get()); if (rc == GRN_SUCCESS) { object.release(); index_column.release(); } else { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to drop multiple column index table: <%.*s>: <%s>", multiple_column_index_table_name_length, multiple_column_index_table_name, ctx->errbuf); error = ER_ERROR_ON_WRITE; my_message(error, error_message, MYF(0)); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); break; } } grn_table_cursor_close(ctx, cursor); DBUG_RETURN(error); } int ha_mroonga::drop_indexes(const char *table_name) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn::SmartGrnObj table(ctx, table_name); if (!table.get()) { DBUG_RETURN(0); } error = drop_indexes_normal(table_name, table.get()); if (error == 0) { error = drop_indexes_multiple(table_name, table.get(), mrn::IndexTableName::SEPARATOR); } if (error == 0) { error = drop_indexes_multiple(table_name, table.get(), mrn::IndexTableName::OLD_SEPARATOR); } DBUG_RETURN(error); } bool ha_mroonga::find_column_flags(Field *field, MRN_SHARE *mrn_share, int i, grn_obj_flags *column_flags) { MRN_DBUG_ENTER_METHOD(); bool found = false; #ifdef MRN_SUPPORT_CUSTOM_OPTIONS { const char *names = field->option_struct->flags; if (names) { found = mrn_parse_grn_column_create_flags(ha_thd(), ctx, names, strlen(names), column_flags); DBUG_RETURN(found); } } #endif if (mrn_share->col_flags[i]) { found = mrn_parse_grn_column_create_flags(ha_thd(), ctx, mrn_share->col_flags[i], mrn_share->col_flags_length[i], column_flags); DBUG_RETURN(found); } DBUG_RETURN(found); } grn_obj *ha_mroonga::find_column_type(Field *field, MRN_SHARE *mrn_share, int i, int error_code) { MRN_DBUG_ENTER_METHOD(); const char *grn_type_name = NULL; #ifdef MRN_SUPPORT_CUSTOM_OPTIONS grn_type_name = field->option_struct->groonga_type; #endif if (!grn_type_name) { grn_type_name = mrn_share->col_type[i]; } grn_obj *type = NULL; if (grn_type_name) { type = grn_ctx_get(ctx, grn_type_name, -1); if (!type) { char error_message[MRN_BUFFER_SIZE]; snprintf(error_message, MRN_BUFFER_SIZE, "unknown custom Groonga type name for <%s> column: <%s>", field->field_name.str, grn_type_name); GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message); my_message(error_code, error_message, MYF(0)); DBUG_RETURN(NULL); } } else { grn_builtin_type grn_type_id = mrn_grn_type_from_field(ctx, field, false); type = grn_ctx_at(ctx, grn_type_id); } DBUG_RETURN(type); } grn_obj *ha_mroonga::find_tokenizer(KEY *key, MRN_SHARE *mrn_share, int i) { MRN_DBUG_ENTER_METHOD(); grn_obj *tokenizer; const char *tokenizer_name = NULL; uint tokenizer_name_length = 0; #ifdef MRN_SUPPORT_CUSTOM_OPTIONS if (key->option_struct->tokenizer) { tokenizer_name = key->option_struct->tokenizer; tokenizer_name_length = strlen(tokenizer_name); } #endif if (!tokenizer_name) { tokenizer_name = mrn_share->key_tokenizer[i]; tokenizer_name_length = mrn_share->key_tokenizer_length[i]; } tokenizer = find_tokenizer(tokenizer_name, tokenizer_name_length); DBUG_RETURN(tokenizer); } grn_obj *ha_mroonga::find_tokenizer(const char *name, int name_length) { MRN_DBUG_ENTER_METHOD(); if (strncasecmp("off", name, name_length) == 0) { DBUG_RETURN(NULL); } grn_obj *tokenizer; mrn_change_encoding(ctx, system_charset_info); tokenizer = grn_ctx_get(ctx, name, name_length); if (!tokenizer) { char message[MRN_BUFFER_SIZE]; sprintf(message, "specified tokenizer for fulltext index <%.*s> doesn't exist. " "The default tokenizer for fulltext index <%s> is used instead.", name_length, name, MRN_DEFAULT_TOKENIZER); push_warning(ha_thd(), MRN_SEVERITY_WARNING, ER_UNSUPPORTED_EXTENSION, message); tokenizer = grn_ctx_get(ctx, MRN_DEFAULT_TOKENIZER, strlen(MRN_DEFAULT_TOKENIZER)); } if (!tokenizer) { push_warning(ha_thd(), MRN_SEVERITY_WARNING, ER_UNSUPPORTED_EXTENSION, "couldn't find tokenizer for fulltext index. " "Bigram tokenizer is used instead."); tokenizer = grn_ctx_at(ctx, GRN_DB_BIGRAM); } DBUG_RETURN(tokenizer); } bool ha_mroonga::have_custom_normalizer(KEY *key) const { MRN_DBUG_ENTER_METHOD(); #ifdef MRN_SUPPORT_CUSTOM_OPTIONS if (key->option_struct && key->option_struct->normalizer) { DBUG_RETURN(true); } #endif if (key->comment.length > 0) { mrn::ParametersParser parser(key->comment.str, key->comment.length); parser.parse(); DBUG_RETURN(parser["normalizer"] != NULL); } DBUG_RETURN(false); } grn_obj *ha_mroonga::find_normalizer(KEY *key) { MRN_DBUG_ENTER_METHOD(); #ifdef MRN_SUPPORT_CUSTOM_OPTIONS if (key->option_struct->normalizer) { grn_obj *normalizer = find_normalizer(key, key->option_struct->normalizer); DBUG_RETURN(normalizer); } #endif if (key->comment.length > 0) { mrn::ParametersParser parser(key->comment.str, key->comment.length); parser.parse(); grn_obj *normalizer = find_normalizer(key, parser["normalizer"]); DBUG_RETURN(normalizer); } grn_obj *normalizer = find_normalizer(key, NULL); DBUG_RETURN(normalizer); } grn_obj *ha_mroonga::find_normalizer(KEY *key, const char *name) { MRN_DBUG_ENTER_METHOD(); grn_obj *normalizer = NULL; bool use_normalizer = true; if (name) { if (strcmp(name, "none") == 0) { use_normalizer = false; } else { normalizer = grn_ctx_get(ctx, name, -1); } } if (use_normalizer && !normalizer) { Field *field = key->key_part[0].field; mrn::FieldNormalizer field_normalizer(ctx, ha_thd(), field); normalizer = field_normalizer.find_grn_normalizer(); } DBUG_RETURN(normalizer); } bool ha_mroonga::find_index_column_flags(KEY *key, grn_column_flags *index_column_flags) { MRN_DBUG_ENTER_METHOD(); bool found = false; #ifdef MRN_SUPPORT_CUSTOM_OPTIONS { const char *names = key->option_struct->flags; if (names) { found = mrn_parse_grn_index_column_flags(ha_thd(), ctx, names, strlen(names), index_column_flags); DBUG_RETURN(found); } } #endif if (key->comment.length > 0) { mrn::ParametersParser parser(key->comment.str, key->comment.length); parser.parse(); const char *names = parser["flags"]; if (!names) { // Deprecated. It's for backward compatibility. names = parser["index_flags"]; } if (names) { found = mrn_parse_grn_index_column_flags(ha_thd(), ctx, names, strlen(names), index_column_flags); } } DBUG_RETURN(found); } bool ha_mroonga::find_token_filters(KEY *key, grn_obj *token_filters) { MRN_DBUG_ENTER_METHOD(); bool found = false; #ifdef MRN_SUPPORT_CUSTOM_OPTIONS if (key->option_struct->token_filters) { found = find_token_filters_fill(token_filters, key->option_struct->token_filters, strlen(key->option_struct->token_filters)); DBUG_RETURN(found); } #endif if (key->comment.length > 0) { mrn::ParametersParser parser(key->comment.str, key->comment.length); parser.parse(); const char *names = parser["token_filters"]; if (names) { found = find_token_filters_fill(token_filters, names, strlen(names)); } } DBUG_RETURN(found); } bool ha_mroonga::find_token_filters_put(grn_obj *token_filters, const char *token_filter_name, int token_filter_name_length) { grn_obj *token_filter; token_filter = grn_ctx_get(ctx, token_filter_name, token_filter_name_length); if (token_filter) { GRN_PTR_PUT(ctx, token_filters, token_filter); return true; } else { char message[MRN_BUFFER_SIZE]; sprintf(message, "nonexistent token filter: <%.*s>", token_filter_name_length, token_filter_name); push_warning(ha_thd(), MRN_SEVERITY_WARNING, ER_UNSUPPORTED_EXTENSION, message); return false; } } bool ha_mroonga::find_token_filters_fill(grn_obj *token_filters, const char *token_filter_names, int token_filter_names_length) { const char *start, *current, *end; const char *name_start, *name_end; const char *last_name_end; start = token_filter_names; end = start + token_filter_names_length; current = start; name_start = NULL; name_end = NULL; last_name_end = start; while (current < end) { switch (current[0]) { case ' ' : if (name_start && !name_end) { name_end = current; } break; case ',' : if (!name_start) { goto break_loop; } if (!name_end) { name_end = current; } find_token_filters_put(token_filters, name_start, name_end - name_start); last_name_end = name_end + 1; name_start = NULL; name_end = NULL; break; default : if (!name_start) { name_start = current; } break; } current++; } break_loop: if (!name_start) { char message[MRN_BUFFER_SIZE]; sprintf(message, "empty token filter name: " "<%.*s|%.*s|%.*s>", (int)(last_name_end - start), start, (int)(current - last_name_end), last_name_end, (int)(end - current), current); push_warning(ha_thd(), MRN_SEVERITY_WARNING, ER_UNSUPPORTED_EXTENSION, message); return false; } if (!name_end) { name_end = current; } find_token_filters_put(token_filters, name_start, name_end - name_start); return true; } int ha_mroonga::wrapper_get_record(uchar *buf, const uchar *key) { MRN_DBUG_ENTER_METHOD(); int error = 0; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (wrap_handler->inited == NONE) { #ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_IDX_MAP error = wrap_handler->ha_index_read_idx_map(buf, share->wrap_primary_key, key, pk_keypart_map, HA_READ_KEY_EXACT); #else error = wrap_handler->index_read_idx_map(buf, share->wrap_primary_key, key, pk_keypart_map, HA_READ_KEY_EXACT); #endif } else { #ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP error = wrap_handler->ha_index_read_map(buf, key, pk_keypart_map, HA_READ_KEY_EXACT); #else error = wrap_handler->index_read_map(buf, key, pk_keypart_map, HA_READ_KEY_EXACT); #endif } MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::wrapper_get_next_geo_record(uchar *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn_change_encoding(ctx, NULL); do { GRN_BULK_REWIND(&key_buffer); grn_id found_record_id; grn_posting *posting; posting = grn_geo_cursor_next(ctx, cursor_geo); if (!posting) { error = HA_ERR_END_OF_FILE; clear_cursor_geo(); break; } found_record_id = posting->rid; grn_table_get_key(ctx, grn_table, found_record_id, GRN_TEXT_VALUE(&key_buffer), table->key_info->key_length); error = wrapper_get_record(buf, (const uchar *)GRN_TEXT_VALUE(&key_buffer)); } while (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND); DBUG_RETURN(error); } int ha_mroonga::storage_get_next_record(uchar *buf) { MRN_DBUG_ENTER_METHOD(); if (cursor_geo) { grn_posting *posting; posting = grn_geo_cursor_next(ctx, cursor_geo); if (posting) { record_id = posting->rid; } else { record_id = GRN_ID_NIL; } } else if (cursor) { record_id = grn_table_cursor_next(ctx, cursor); } else if (empty_value_records_cursor) { grn_id empty_value_record_id; empty_value_record_id = grn_table_cursor_next(ctx, empty_value_records_cursor); if (empty_value_record_id == GRN_ID_NIL) { record_id = GRN_ID_NIL; } else { grn_table_get_key(ctx, empty_value_records, empty_value_record_id, &record_id, sizeof(grn_id)); } } else { record_id = GRN_ID_NIL; } if (ctx->rc) { int error = ER_ERROR_ON_READ; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } if (record_id == GRN_ID_NIL) { DBUG_PRINT("info", ("mroonga: storage_get_next_record: end-of-file")); table->status = STATUS_NOT_FOUND; DBUG_RETURN(HA_ERR_END_OF_FILE); } if (buf) { if (ignoring_no_key_columns) storage_store_fields_by_index(buf); else storage_store_fields(buf, record_id); if (cursor_geo && grn_source_column_geo) { int latitude, longitude; GRN_GEO_POINT_VALUE(&source_point, latitude, longitude); double latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude); double longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude); if (!((bottom_right_latitude_in_degree <= latitude_in_degree && latitude_in_degree <= top_left_latitude_in_degree) && (top_left_longitude_in_degree <= longitude_in_degree && longitude_in_degree <= bottom_right_longitude_in_degree))) { DBUG_PRINT("info", ("mroonga: remove not contained geo point: " "<%g,%g>(<%d,%d>); key: <%g,%g>(<%d,%d>), <%g,%g>(<%d,%d>)", latitude_in_degree, longitude_in_degree, latitude, longitude, top_left_latitude_in_degree, top_left_longitude_in_degree, GRN_GEO_DEGREE2MSEC(top_left_latitude_in_degree), GRN_GEO_DEGREE2MSEC(top_left_longitude_in_degree), bottom_right_latitude_in_degree, bottom_right_longitude_in_degree, GRN_GEO_DEGREE2MSEC(bottom_right_latitude_in_degree), GRN_GEO_DEGREE2MSEC(bottom_right_longitude_in_degree))); int error = storage_get_next_record(buf); DBUG_RETURN(error); } } } table->status = 0; DBUG_RETURN(0); } void ha_mroonga::geo_store_rectangle(const uchar *rectangle) { MRN_DBUG_ENTER_METHOD(); double locations[4]; for (int i = 0; i < 4; i++) { uchar reversed_value[8]; for (int j = 0; j < 8; j++) { reversed_value[j] = (rectangle + (8 * i))[7 - j]; } mi_float8get(locations[i], reversed_value); } top_left_longitude_in_degree = locations[0]; bottom_right_longitude_in_degree = locations[1]; bottom_right_latitude_in_degree = locations[2]; top_left_latitude_in_degree = locations[3]; int top_left_latitude = GRN_GEO_DEGREE2MSEC(top_left_latitude_in_degree); int top_left_longitude = GRN_GEO_DEGREE2MSEC(top_left_longitude_in_degree); int bottom_right_latitude = GRN_GEO_DEGREE2MSEC(bottom_right_latitude_in_degree); int bottom_right_longitude = GRN_GEO_DEGREE2MSEC(bottom_right_longitude_in_degree); GRN_GEO_POINT_SET(ctx, &top_left_point, top_left_latitude, top_left_longitude); GRN_GEO_POINT_SET(ctx, &bottom_right_point, bottom_right_latitude, bottom_right_longitude); DBUG_VOID_RETURN; } int ha_mroonga::generic_geo_open_cursor(const uchar *key, enum ha_rkey_function find_flag) { MRN_DBUG_ENTER_METHOD(); int error = 0; int flags = 0; if (find_flag & HA_READ_MBR_CONTAIN) { grn_obj *index = grn_index_columns[active_index]; geo_store_rectangle(key); cursor_geo = grn_geo_cursor_open_in_rectangle(ctx, index, &top_left_point, &bottom_right_point, 0, -1); if (cursor_geo) { if (grn_source_column_geo) { grn_obj_unlink(ctx, grn_source_column_geo); } grn_obj sources; GRN_OBJ_INIT(&sources, GRN_BULK, 0, GRN_ID_NIL); grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &sources); grn_source_column_geo = grn_ctx_at(ctx, GRN_RECORD_VALUE(&sources)); grn_obj_unlink(ctx, &sources); } } else { push_warning_unsupported_spatial_index_search(find_flag); cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0, 0, -1, flags); } if (ctx->rc) { error = ER_ERROR_ON_READ; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } bool ha_mroonga::is_dry_write() { MRN_DBUG_ENTER_METHOD(); bool dry_write_p = THDVAR(ha_thd(), dry_write); DBUG_RETURN(dry_write_p); } bool ha_mroonga::is_enable_optimization() { MRN_DBUG_ENTER_METHOD(); bool enable_optimization_p = THDVAR(ha_thd(), enable_optimization); DBUG_RETURN(enable_optimization_p); } bool ha_mroonga::should_normalize(Field *field) const { MRN_DBUG_ENTER_METHOD(); mrn::FieldNormalizer field_normalizer(ctx, ha_thd(), field); bool need_normalize_p = field_normalizer.should_normalize(); DBUG_RETURN(need_normalize_p); } void ha_mroonga::check_count_skip(key_part_map target_key_part_map) { MRN_DBUG_ENTER_METHOD(); if (!is_enable_optimization()) { GRN_LOG(ctx, GRN_LOG_DEBUG, "[mroonga][count-skip][false] optimization is disabled"); count_skip = false; DBUG_VOID_RETURN; } if (thd_sql_command(ha_thd()) != SQLCOM_SELECT) { GRN_LOG(ctx, GRN_LOG_DEBUG, "[mroonga][count-skip][false] not SELECT"); count_skip = false; DBUG_VOID_RETURN; } if (share->wrapper_mode && !(wrap_handler->ha_table_flags() & HA_NO_TRANSACTIONS)) { GRN_LOG(ctx, GRN_LOG_DEBUG, "[mroonga][count-skip][false] wrapped engine is transactional"); count_skip = false; DBUG_VOID_RETURN; } st_select_lex *select_lex = table->pos_in_table_list->select_lex; KEY *key_info = NULL; if (active_index != MAX_KEY) { key_info = &(table->key_info[active_index]); } mrn::CountSkipChecker checker(ctx, table, select_lex, key_info, target_key_part_map, !share->wrapper_mode); if (checker.check()) { count_skip = true; mrn_count_skip++; DBUG_VOID_RETURN; } else { count_skip = false; DBUG_VOID_RETURN; } } bool ha_mroonga::is_grn_zero_column_value(grn_obj *column, grn_obj *value) { MRN_DBUG_ENTER_METHOD(); if (column->header.type != GRN_COLUMN_FIX_SIZE) { DBUG_RETURN(false); } char *bytes = GRN_BULK_HEAD(value); unsigned int size = GRN_BULK_VSIZE(value); for (unsigned int i = 0; i < size; ++i) { if (bytes[i] != '\0') { DBUG_RETURN(false); } } DBUG_RETURN(true); } bool ha_mroonga::is_primary_key_field(Field *field) const { MRN_DBUG_ENTER_METHOD(); if (table->s->primary_key == MAX_INDEXES) { DBUG_RETURN(false); } KEY *key_info = &(table->s->key_info[table->s->primary_key]); if (KEY_N_KEY_PARTS(key_info) != 1) { DBUG_RETURN(false); } if (strcmp(field->field_name.str, key_info->key_part[0].field->field_name.str) == 0) { DBUG_RETURN(true); } else { DBUG_RETURN(false); } } void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys, longlong *limit) { MRN_DBUG_ENTER_METHOD(); if (!is_enable_optimization()) { DBUG_PRINT("info", ("mroonga: fast order limit: optimization is disabled")); fast_order_limit = false; DBUG_VOID_RETURN; } TABLE_LIST *table_list = table->pos_in_table_list; st_select_lex *select_lex = table_list->select_lex; SELECT_LEX_UNIT *unit = MRN_TABLE_LIST_GET_DERIVED(table_list); st_select_lex *first_select_lex; if (unit) { first_select_lex = unit->first_select(); } else { first_select_lex = select_lex; } DBUG_PRINT("info", ("mroonga: first_select_lex->options=%llu", first_select_lex ? MRN_SELECT_LEX_GET_ACTIVE_OPTIONS(first_select_lex) : 0)); if ( thd_sql_command(ha_thd()) == SQLCOM_SELECT && !select_lex->with_sum_func && !select_lex->group_list.elements && !MRN_SELECT_LEX_GET_HAVING_COND(select_lex) && select_lex->table_list.elements == 1 && select_lex->order_list.elements && select_lex->limit_params.explicit_limit && select_lex->limit_params.select_limit && select_lex->limit_params.select_limit->val_int() > 0 ) { if (select_lex->limit_params.offset_limit) { *limit = select_lex->limit_params.offset_limit->val_int(); } else { *limit = 0; } *limit += select_lex->limit_params.select_limit->val_int(); if (*limit > (longlong)INT_MAX) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "too long limit: %lld <= %d is required", *limit, INT_MAX)); fast_order_limit = false; DBUG_VOID_RETURN; } if (first_select_lex && (MRN_SELECT_LEX_GET_ACTIVE_OPTIONS(first_select_lex) & OPTION_FOUND_ROWS)) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "SQL_CALC_FOUND_ROWS is specified")); fast_order_limit = false; DBUG_VOID_RETURN; } bool is_storage_mode = !(share->wrapper_mode); Item *where = MRN_SELECT_LEX_GET_WHERE_COND(select_lex); const Item_func *match_against = NULL; if (where) { mrn::ConditionConverter converter(ctx, grn_table, is_storage_mode); if (!converter.is_convertable(where)) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "not Groonga layer condition search")); fast_order_limit = false; DBUG_VOID_RETURN; } unsigned int n_match_againsts = converter.count_match_against(where); if (n_match_againsts == 0) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "Groonga layer condition but not fulltext search")); fast_order_limit = false; DBUG_VOID_RETURN; } if (n_match_againsts > 1) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "MATCH AGAINST must be only one")); fast_order_limit = false; DBUG_VOID_RETURN; } } int n_max_sort_keys = select_lex->order_list.elements; *n_sort_keys = 0; size_t sort_keys_size = sizeof(grn_table_sort_key) * n_max_sort_keys; *sort_keys = (grn_table_sort_key *)mrn_my_malloc(sort_keys_size, MYF(MY_WME)); memset(*sort_keys, 0, sort_keys_size); ORDER *order; int i; mrn_change_encoding(ctx, system_charset_info); for (order = (ORDER *) select_lex->order_list.first, i = 0; order; order = order->next, i++) { Item *item = *order->item; if (item->type() == Item::FIELD_ITEM) { Field *field = static_cast(item)->field; mrn::ColumnName column_name(field->field_name); if (should_normalize(field)) { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "sort by collated value isn't supported yet.")); fast_order_limit = false; my_free(*sort_keys); *sort_keys = NULL; *n_sort_keys = 0; DBUG_VOID_RETURN; } if (is_storage_mode) { (*sort_keys)[i].key = grn_obj_column(ctx, matched_record_keys, column_name.c_str(), column_name.length()); } else { if (is_primary_key_field(field)) { (*sort_keys)[i].key = grn_obj_column(ctx, matched_record_keys, MRN_COLUMN_NAME_KEY, strlen(MRN_COLUMN_NAME_KEY)); } else { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "sort by not primary key value " "isn't supported in wrapper mode.")); fast_order_limit = false; my_free(*sort_keys); *sort_keys = NULL; *n_sort_keys = 0; DBUG_VOID_RETURN; } } } else if (!match_against || match_against->eq(item, true)) { (*sort_keys)[i].key = grn_obj_column(ctx, matched_record_keys, MRN_COLUMN_NAME_SCORE, strlen(MRN_COLUMN_NAME_SCORE)); } else { DBUG_PRINT("info", ("mroonga: fast_order_limit = false: " "sort by computed value isn't supported.")); fast_order_limit = false; my_free(*sort_keys); *sort_keys = NULL; *n_sort_keys = 0; DBUG_VOID_RETURN; } (*sort_keys)[i].offset = 0; if (MRN_ORDER_IS_ASC(order)) { (*sort_keys)[i].flags = GRN_TABLE_SORT_ASC; } else { (*sort_keys)[i].flags = GRN_TABLE_SORT_DESC; } (*n_sort_keys)++; } DBUG_PRINT("info", ("mroonga: fast_order_limit = true")); fast_order_limit = true; mrn_fast_order_limit++; DBUG_VOID_RETURN; } DBUG_PRINT("info", ("mroonga: fast_order_limit = false")); fast_order_limit = false; DBUG_VOID_RETURN; } int ha_mroonga::generic_store_bulk_fixed_size_string(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0); GRN_TEXT_SET(ctx, buf, field->ptr, field->field_length); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_variable_size_string(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; StringBuffer buffer(field->charset()); auto value = field->val_str(&buffer, &buffer); grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0); DBUG_PRINT("info", ("mroonga: length=%" MRN_FORMAT_STRING_LENGTH, value->length())); DBUG_PRINT("info", ("mroonga: value=%s", value->c_ptr_safe())); GRN_TEXT_SET(ctx, buf, value->ptr(), value->length()); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_integer(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; long long value = field->val_int(); DBUG_PRINT("info", ("mroonga: value=%lld", value)); uint32 size = field->pack_length(); DBUG_PRINT("info", ("mroonga: size=%u", size)); Field_num *field_num = static_cast(field); bool is_unsigned = field_num->unsigned_flag; DBUG_PRINT("info", ("mroonga: is_unsigned=%s", is_unsigned ? "true" : "false")); switch (size) { case 1: if (is_unsigned) { grn_obj_reinit(ctx, buf, GRN_DB_UINT8, 0); GRN_UINT8_SET(ctx, buf, value); } else { grn_obj_reinit(ctx, buf, GRN_DB_INT8, 0); GRN_INT8_SET(ctx, buf, value); } break; case 2: if (is_unsigned) { grn_obj_reinit(ctx, buf, GRN_DB_UINT16, 0); GRN_UINT16_SET(ctx, buf, value); } else { grn_obj_reinit(ctx, buf, GRN_DB_INT16, 0); GRN_INT16_SET(ctx, buf, value); } break; case 3: case 4: if (is_unsigned) { grn_obj_reinit(ctx, buf, GRN_DB_UINT32, 0); GRN_UINT32_SET(ctx, buf, value); } else { grn_obj_reinit(ctx, buf, GRN_DB_INT32, 0); GRN_INT32_SET(ctx, buf, value); } break; case 8: if (is_unsigned) { grn_obj_reinit(ctx, buf, GRN_DB_UINT64, 0); GRN_UINT64_SET(ctx, buf, value); } else { grn_obj_reinit(ctx, buf, GRN_DB_INT64, 0); GRN_INT64_SET(ctx, buf, value); } break; default: // Why!? error = HA_ERR_UNSUPPORTED; char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "unknown integer value size: <%u>: " "available sizes: [1, 2, 3, 4, 8]", size); push_warning(ha_thd(), MRN_SEVERITY_WARNING, error, error_message); break; } DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_unsigned_integer(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; long long signed_value = field->val_int(); unsigned long long unsigned_value = *((unsigned long long *)(&signed_value)); uint32 size = field->pack_length(); switch (size) { case 1: grn_obj_reinit(ctx, buf, GRN_DB_UINT8, 0); GRN_UINT8_SET(ctx, buf, unsigned_value); break; case 2: grn_obj_reinit(ctx, buf, GRN_DB_UINT16, 0); GRN_UINT16_SET(ctx, buf, unsigned_value); break; case 3: case 4: grn_obj_reinit(ctx, buf, GRN_DB_UINT32, 0); GRN_UINT32_SET(ctx, buf, unsigned_value); break; case 8: grn_obj_reinit(ctx, buf, GRN_DB_UINT64, 0); GRN_UINT64_SET(ctx, buf, unsigned_value); break; default: // Why!? error = HA_ERR_UNSUPPORTED; char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "unknown unsigned integer value size: <%u>: " "available sizes: [1, 2, 3, 4, 8]", size); push_warning(ha_thd(), MRN_SEVERITY_WARNING, error, error_message); break; } DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_float(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; double value = field->val_real(); uint32 size = field->pack_length(); switch (size) { case 4: case 8: grn_obj_reinit(ctx, buf, GRN_DB_FLOAT, 0); GRN_FLOAT_SET(ctx, buf, value); break; default: // Why!? error = HA_ERR_UNSUPPORTED; char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "unknown float value size: <%u>: " "available sizes: [4, 8]", size); push_warning(ha_thd(), MRN_SEVERITY_WARNING, error, error_message); break; } DBUG_RETURN(error); } long long int ha_mroonga::get_grn_time_from_timestamp_field(Field_timestamp *field) { MRN_DBUG_ENTER_METHOD(); long long int grn_time = 0; #ifdef MRN_TIMESTAMP_USE_TIMEVAL int warnings = 0; struct timeval time_value; if (field->get_timestamp(&time_value, &warnings)) { // XXX: Should we report warnings or MySQL does? } else { DBUG_PRINT("info", ("mroonga: timeval tv_sec=%ld", time_value.tv_sec)); grn_time = GRN_TIME_PACK(time_value.tv_sec, time_value.tv_usec); } #elif defined(MRN_TIMESTAMP_USE_MY_TIME_T) unsigned long int micro_seconds; my_time_t seconds = field->get_timestamp(µ_seconds); DBUG_PRINT("info", ("mroonga: my_time_t seconds=%ld", seconds)); grn_time = GRN_TIME_PACK(seconds, micro_seconds); #else my_bool is_null_value; long seconds = field->get_timestamp(&is_null_value); DBUG_PRINT("info", ("mroonga: long seconds=%ld", seconds)); grn_time = GRN_TIME_PACK(seconds, 0); #endif DBUG_RETURN(grn_time); } int ha_mroonga::generic_store_bulk_timestamp(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; Field_timestamp *timestamp_field = (Field_timestamp *)field; long long int time = get_grn_time_from_timestamp_field(timestamp_field); grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_date(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; long long int date_value = field->val_int(); struct tm date; memset(&date, 0, sizeof(struct tm)); date.tm_year = date_value / 10000 % 10000 - mrn::TimeConverter::TM_YEAR_BASE; date.tm_mon = date_value / 100 % 100 - 1; date.tm_mday = date_value % 100; int usec = 0; mrn::TimeConverter time_converter; long long int time = time_converter.tm_to_grn_time(&date, usec, &truncated); if (truncated) { field->set_warning(MRN_SEVERITY_WARNING, WARN_DATA_TRUNCATED, 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_time(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_time *time_field = (Field_time *)field; MYSQL_TIME mysql_time; time_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { field->set_warning(MRN_SEVERITY_WARNING, WARN_DATA_TRUNCATED, 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_datetime(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_datetime *datetime_field = (Field_datetime *)field; MYSQL_TIME mysql_time; datetime_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_year(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; int year; if (field->field_length == 2) { year = static_cast(field->val_int() + 2000); } else { year = static_cast(field->val_int()); } DBUG_PRINT("info", ("mroonga: year=%d", year)); struct tm date; memset(&date, 0, sizeof(struct tm)); date.tm_year = year - mrn::TimeConverter::TM_YEAR_BASE; date.tm_mon = 0; date.tm_mday = 1; int usec = 0; mrn::TimeConverter time_converter; long long int time = time_converter.tm_to_grn_time(&date, usec, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 int ha_mroonga::generic_store_bulk_datetime2(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_datetimef *datetimef_field = (Field_datetimef *)field; MYSQL_TIME mysql_time; datetimef_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 int ha_mroonga::generic_store_bulk_time2(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; MYSQL_TIME mysql_time; field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } #endif int ha_mroonga::generic_store_bulk_new_date(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_newdate *newdate_field = (Field_newdate *)field; MYSQL_TIME mysql_date; newdate_field->get_date(&mysql_date, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_date, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0); GRN_TIME_SET(ctx, buf, time); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_new_decimal(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; String value; Field_new_decimal *new_decimal_field = (Field_new_decimal *)field; new_decimal_field->val_str(&value, NULL); grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0); GRN_TEXT_SET(ctx, buf, value.ptr(), value.length()); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_blob(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; StringBuffer buffer(field->charset()); auto value = field->val_str(&buffer, &buffer); grn_obj_reinit(ctx, buf, GRN_DB_TEXT, 0); GRN_TEXT_SET(ctx, buf, value->ptr(), value->length()); DBUG_RETURN(error); } int ha_mroonga::generic_store_bulk_geometry(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; #ifdef MRN_HAVE_SPATIAL String buffer; Field_blob *geometry = (Field_blob *)field; String *value = geometry->val_str(0, &buffer); const char *wkb = value->ptr(); int len = value->length(); error = mrn_set_geometry(ctx, buf, wkb, len); #endif DBUG_RETURN(error); } #ifdef MRN_HAVE_MYSQL_TYPE_JSON int ha_mroonga::generic_store_bulk_json(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; String buffer; Field_json *json = static_cast(field); String *value = json->val_str(&buffer, NULL); grn_obj_reinit(ctx, buf, GRN_DB_TEXT, 0); GRN_TEXT_SET(ctx, buf, value->ptr(), value->length()); DBUG_RETURN(error); } #endif int ha_mroonga::generic_store_bulk(Field *field, grn_obj *buf) { MRN_DBUG_ENTER_METHOD(); int error; error = mrn_change_encoding(ctx, field->charset()); if (error) return error; switch (field->real_type()) { case MYSQL_TYPE_DECIMAL: error = generic_store_bulk_variable_size_string(field, buf); break; case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: error = generic_store_bulk_integer(field, buf); break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: error = generic_store_bulk_float(field, buf); break; case MYSQL_TYPE_NULL: error = generic_store_bulk_unsigned_integer(field, buf); break; case MYSQL_TYPE_TIMESTAMP: error = generic_store_bulk_timestamp(field, buf); break; case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: error = generic_store_bulk_integer(field, buf); break; case MYSQL_TYPE_DATE: error = generic_store_bulk_date(field, buf); break; case MYSQL_TYPE_TIME: error = generic_store_bulk_time(field, buf); break; case MYSQL_TYPE_DATETIME: error = generic_store_bulk_datetime(field, buf); break; case MYSQL_TYPE_YEAR: error = generic_store_bulk_year(field, buf); break; case MYSQL_TYPE_NEWDATE: error = generic_store_bulk_new_date(field, buf); break; case MYSQL_TYPE_VARCHAR: error = generic_store_bulk_variable_size_string(field, buf); break; case MYSQL_TYPE_BIT: error = generic_store_bulk_unsigned_integer(field, buf); break; #ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 case MYSQL_TYPE_TIMESTAMP2: error = generic_store_bulk_timestamp(field, buf); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 case MYSQL_TYPE_DATETIME2: error = generic_store_bulk_datetime2(field, buf); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 case MYSQL_TYPE_TIME2: error = generic_store_bulk_time2(field, buf); break; #endif case MYSQL_TYPE_NEWDECIMAL: error = generic_store_bulk_new_decimal(field, buf); break; case MYSQL_TYPE_ENUM: error = generic_store_bulk_unsigned_integer(field, buf); break; case MYSQL_TYPE_SET: error = generic_store_bulk_unsigned_integer(field, buf); break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: error = generic_store_bulk_blob(field, buf); break; case MYSQL_TYPE_VAR_STRING: error = generic_store_bulk_variable_size_string(field, buf); break; case MYSQL_TYPE_STRING: error = generic_store_bulk_fixed_size_string(field, buf); break; case MYSQL_TYPE_GEOMETRY: error = generic_store_bulk_geometry(field, buf); break; #ifdef MRN_HAVE_MYSQL_TYPE_JSON case MYSQL_TYPE_JSON: error = generic_store_bulk_json(field, buf); break; #endif default: error = HA_ERR_UNSUPPORTED; break; } DBUG_RETURN(error); } void ha_mroonga::storage_store_field_string(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); field->store(value, value_length, field->charset()); DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_integer(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); Field_num *field_num = static_cast(field); bool is_unsigned = field_num->unsigned_flag; switch (value_length) { case 1: { if (is_unsigned) { unsigned char field_value; field_value = *((unsigned char *)value); field->store(field_value, is_unsigned); } else { signed char field_value; field_value = *((signed char *)value); field->store(field_value, is_unsigned); } break; } case 2: { if (is_unsigned) { unsigned short field_value; field_value = *((unsigned short *)value); field->store(field_value, is_unsigned); } else { short field_value; field_value = *((short *)value); field->store(field_value, is_unsigned); } break; } case 4: { if (is_unsigned) { unsigned int field_value; field_value = *((unsigned int *)value); field->store(field_value, is_unsigned); } else { int field_value; field_value = *((int *)value); field->store(field_value, is_unsigned); } break; } case 8: { if (is_unsigned) { unsigned long long int field_value; field_value = *((unsigned long long int *)value); DBUG_PRINT("info", ("mroonga: field_value=%llu", field_value)); field->store(field_value, is_unsigned); } else { long long int field_value; field_value = *((long long int *)value); field->store(field_value, is_unsigned); } break; } default: { // Why!? char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "unknown integer value size: <%d>: " "available sizes: [1, 2, 4, 8]", value_length); push_warning(ha_thd(), MRN_SEVERITY_WARNING, HA_ERR_UNSUPPORTED, error_message); storage_store_field_string(field, value, value_length); break; } } DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_unsigned_integer(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); switch (value_length) { case 1: { unsigned char field_value; field_value = *((unsigned char *)value); field->store(field_value, true); break; } case 2: { unsigned short field_value; field_value = *((unsigned short *)value); field->store(field_value, true); break; } case 4: { unsigned int field_value; field_value = *((unsigned int *)value); field->store(field_value, true); break; } case 8: { unsigned long long int field_value; field_value = *((unsigned long long int *)value); DBUG_PRINT("info", ("mroonga: field_value=%llu", field_value)); field->store(field_value, true); break; } default: { // Why!? char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "unknown integer value size: <%d>: " "available sizes: [1, 2, 4, 8]", value_length); push_warning(ha_thd(), MRN_SEVERITY_WARNING, HA_ERR_UNSUPPORTED, error_message); storage_store_field_string(field, value, value_length); break; } } DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_float(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); double field_value; field_value = *((double *)value); field->store(field_value); DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_timestamp(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); Field_timestamp *timestamp_field = (Field_timestamp *)field; #ifdef MRN_TIMESTAMP_USE_TIMEVAL struct timeval time_value; GRN_TIME_UNPACK(time, time_value.tv_sec, time_value.tv_usec); timestamp_field->store_timestamp(&time_value); #elif defined(MRN_TIMESTAMP_USE_MY_TIME_T) long long int sec, usec; GRN_TIME_UNPACK(time, sec, usec); timestamp_field->store_TIME(static_cast(sec), static_cast(usec)); #else int32 sec, usec __attribute__((unused)); GRN_TIME_UNPACK(time, sec, usec); timestamp_field->store_timestamp(sec); #endif DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_date(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); long long int sec, usec __attribute__((unused)); GRN_TIME_UNPACK(time, sec, usec); struct tm date; time_t sec_t = static_cast(sec); gmtime_r(&sec_t, &date); long long int date_in_mysql = (date.tm_year + mrn::TimeConverter::TM_YEAR_BASE) * 10000 + (date.tm_mon + 1) * 100 + date.tm_mday; field->store(date_in_mysql, false); DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_time(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_time; memset(&mysql_time, 0, sizeof(MYSQL_TIME)); mysql_time.time_type = MYSQL_TIMESTAMP_TIME; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_time); #ifdef MRN_FIELD_STORE_TIME_NEED_TYPE Field_time *time_field = (Field_time *)field; time_field->store_time(&mysql_time, mysql_time.time_type); #else field->store_time(&mysql_time); #endif DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_datetime(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_datetime; memset(&mysql_datetime, 0, sizeof(MYSQL_TIME)); mysql_datetime.time_type = MYSQL_TIMESTAMP_DATETIME; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_datetime); #ifdef MRN_FIELD_STORE_TIME_NEED_TYPE Field_datetime *datetime_field = (Field_datetime *)field; datetime_field->store_time(&mysql_datetime, mysql_datetime.time_type); #else field->store_time(&mysql_datetime); #endif DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_year(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_time; memset(&mysql_time, 0, sizeof(MYSQL_TIME)); mysql_time.time_type = MYSQL_TIMESTAMP_DATE; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_time); DBUG_PRINT("info", ("mroonga: stored %d", mysql_time.year)); field->store(mysql_time.year, false); DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_new_date(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_date; memset(&mysql_date, 0, sizeof(MYSQL_TIME)); mysql_date.time_type = MYSQL_TIMESTAMP_DATE; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_date); #ifdef MRN_FIELD_STORE_TIME_NEED_TYPE Field_newdate *newdate_field = (Field_newdate *)field; newdate_field->store_time(&mysql_date, MYSQL_TIMESTAMP_DATE); #else field->store_time(&mysql_date); #endif DBUG_VOID_RETURN; } #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 void ha_mroonga::storage_store_field_datetime2(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_datetime; memset(&mysql_datetime, 0, sizeof(MYSQL_TIME)); mysql_datetime.time_type = MYSQL_TIMESTAMP_DATETIME; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_datetime); field->store_time(&mysql_datetime); DBUG_VOID_RETURN; } #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 void ha_mroonga::storage_store_field_time2(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); long long int time = *((long long int *)value); MYSQL_TIME mysql_time; memset(&mysql_time, 0, sizeof(MYSQL_TIME)); mysql_time.time_type = MYSQL_TIMESTAMP_TIME; mrn::TimeConverter time_converter; time_converter.grn_time_to_mysql_time(time, &mysql_time); field->store_time(&mysql_time); DBUG_VOID_RETURN; } #endif void ha_mroonga::storage_store_field_blob(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); Field_blob *blob = (Field_blob *)field; String *blob_buffer = &blob_buffers[field->field_index]; blob_buffer->length(0); blob_buffer->reserve(value_length); blob_buffer->q_append(value, value_length); blob->set_ptr((uint32) value_length, (uchar *) blob_buffer->ptr()); DBUG_VOID_RETURN; } void ha_mroonga::storage_store_field_geometry(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); #ifdef MRN_HAVE_SPATIAL uchar wkb[SRID_SIZE + WKB_HEADER_SIZE + POINT_DATA_SIZE]; grn_geo_point *field_value = (grn_geo_point *)value; int latitude, longitude; latitude = field_value->latitude; longitude = field_value->longitude; if (grn_source_column_geo) { GRN_GEO_POINT_SET(ctx, &source_point, latitude, longitude); } memset(wkb, 0, SRID_SIZE); memset(wkb + SRID_SIZE, Geometry::wkb_ndr, 1); // wkb_ndr is meaningless. int4store(wkb + SRID_SIZE + 1, Geometry::wkb_point); double latitude_in_degree, longitude_in_degree; latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude); longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude); float8store(wkb + SRID_SIZE + WKB_HEADER_SIZE, longitude_in_degree); float8store(wkb + SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE, latitude_in_degree); String *geometry_buffer = &blob_buffers[field->field_index]; geometry_buffer->length(0); uint wkb_length = sizeof(wkb) / sizeof(*wkb); Field_blob *geometry= (Field_blob *)field; geometry_buffer->reserve(wkb_length); geometry_buffer->q_append((const char *) wkb, wkb_length); geometry->set_ptr((uint32) wkb_length, (uchar *) geometry_buffer->ptr()); #endif DBUG_VOID_RETURN; } #ifdef MRN_HAVE_MYSQL_TYPE_JSON void ha_mroonga::storage_store_field_json(Field *field, const char *value, uint value_length) { MRN_DBUG_ENTER_METHOD(); Field_json *json = static_cast(field); json->store(value, value_length, field->charset()); DBUG_VOID_RETURN; } #endif void ha_mroonga::storage_store_field(Field *field, const char *value, uint value_length) { field->set_notnull(); switch (field->real_type()) { case MYSQL_TYPE_DECIMAL: storage_store_field_string(field, value, value_length); break; case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: storage_store_field_integer(field, value, value_length); break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: storage_store_field_float(field, value, value_length); break; case MYSQL_TYPE_NULL: storage_store_field_unsigned_integer(field, value, value_length); break; case MYSQL_TYPE_TIMESTAMP: storage_store_field_timestamp(field, value, value_length); break; case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: storage_store_field_integer(field, value, value_length); break; case MYSQL_TYPE_DATE: storage_store_field_date(field, value, value_length); break; case MYSQL_TYPE_TIME: storage_store_field_time(field, value, value_length); break; case MYSQL_TYPE_DATETIME: storage_store_field_datetime(field, value, value_length); break; case MYSQL_TYPE_YEAR: storage_store_field_year(field, value, value_length); break; case MYSQL_TYPE_NEWDATE: storage_store_field_new_date(field, value, value_length); break; case MYSQL_TYPE_VARCHAR: storage_store_field_string(field, value, value_length); break; case MYSQL_TYPE_BIT: storage_store_field_unsigned_integer(field, value, value_length); break; #ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 case MYSQL_TYPE_TIMESTAMP2: storage_store_field_timestamp(field, value, value_length); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 case MYSQL_TYPE_DATETIME2: storage_store_field_datetime2(field, value, value_length); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 case MYSQL_TYPE_TIME2: storage_store_field_time2(field, value, value_length); break; #endif case MYSQL_TYPE_NEWDECIMAL: storage_store_field_string(field, value, value_length); break; case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: storage_store_field_unsigned_integer(field, value, value_length); break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: storage_store_field_blob(field, value, value_length); break; case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: storage_store_field_string(field, value, value_length); break; case MYSQL_TYPE_GEOMETRY: storage_store_field_geometry(field, value, value_length); break; case MYSQL_TYPE_VARCHAR_COMPRESSED: case MYSQL_TYPE_BLOB_COMPRESSED: DBUG_ASSERT(0); #ifdef MRN_HAVE_MYSQL_TYPE_JSON case MYSQL_TYPE_JSON: storage_store_field_json(field, value, value_length); break; #endif } } void ha_mroonga::storage_store_field_column(Field *field, bool is_primary_key, int nth_column, grn_id record_id) { MRN_DBUG_ENTER_METHOD(); if (!grn_columns[nth_column]) { DBUG_VOID_RETURN; } grn_obj *column = grn_columns[nth_column]; grn_id range_id = grn_obj_get_range(ctx, column); grn_obj *range = grn_column_ranges[nth_column]; grn_obj *value = &new_value_buffer; if (mrn::grn::is_table(range)) { if (mrn::grn::is_vector_column(column)) { grn_obj_reinit(ctx, value, range_id, GRN_OBJ_VECTOR); grn_obj_get_value(ctx, column, record_id, value); grn_obj unvectored_value; GRN_TEXT_INIT(&unvectored_value, 0); int n_ids = GRN_BULK_VSIZE(value) / sizeof(grn_id); for (int i = 0; i < n_ids; i++) { grn_id id = GRN_RECORD_VALUE_AT(value, i); if (i > 0) { GRN_TEXT_PUTS(ctx, &unvectored_value, mrn_vector_column_delimiter); } char key[GRN_TABLE_MAX_KEY_SIZE]; int key_length; key_length = grn_table_get_key(ctx, range, id, &key, GRN_TABLE_MAX_KEY_SIZE); GRN_TEXT_PUT(ctx, &unvectored_value, key, key_length); } storage_store_field(field, GRN_TEXT_VALUE(&unvectored_value), GRN_TEXT_LEN(&unvectored_value)); GRN_OBJ_FIN(ctx, &unvectored_value); } else { grn_obj_reinit(ctx, value, range_id, 0); grn_obj_get_value(ctx, column, record_id, value); grn_id id = GRN_RECORD_VALUE(value); char key[GRN_TABLE_MAX_KEY_SIZE]; int key_length; key_length = grn_table_get_key(ctx, range, id, &key, GRN_TABLE_MAX_KEY_SIZE); storage_store_field(field, key, key_length); } } else { grn_obj_reinit(ctx, value, range_id, 0); grn_obj_get_value(ctx, column, record_id, value); if (is_primary_key && GRN_BULK_VSIZE(value) == 0) { char key[GRN_TABLE_MAX_KEY_SIZE]; int key_length; key_length = grn_table_get_key(ctx, grn_table, record_id, &key, GRN_TABLE_MAX_KEY_SIZE); storage_store_field(field, key, key_length); } else { storage_store_field(field, GRN_BULK_HEAD(value), GRN_BULK_VSIZE(value)); } } DBUG_VOID_RETURN; } void ha_mroonga::storage_store_fields(uchar *buf, grn_id record_id) { MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: stored record ID: %d", record_id)); my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(buf, table->record[0]); Field *primary_key_field = NULL; if (table->s->primary_key != MAX_INDEXES) { KEY *key_info = &(table->s->key_info[table->s->primary_key]); if (KEY_N_KEY_PARTS(key_info) == 1) { primary_key_field = key_info->key_part[0].field; } } int i; int n_columns = table->s->fields; for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; if (bitmap_is_set(table->read_set, field->field_index) || bitmap_is_set(table->write_set, field->field_index)) { const char *column_name = field->field_name.str; if (ignoring_no_key_columns) { KEY *key_info = &(table->s->key_info[active_index]); if (strcmp(key_info->key_part[0].field->field_name.str, column_name)) { continue; } } mrn::DebugColumnAccess debug_column_access(table, &table->write_set); DBUG_PRINT("info", ("mroonga: store column %d(%d)",i,field->field_index)); field->move_field_offset(ptr_diff); if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) { // for _id column field->set_notnull(); field->store((int)record_id); } else if (primary_key_field && strcmp(primary_key_field->field_name.str, column_name) == 0) { // for primary key column storage_store_field_column(field, true, i, record_id); } else { storage_store_field_column(field, false, i, record_id); } field->move_field_offset(-ptr_diff); } } DBUG_VOID_RETURN; } void ha_mroonga::storage_store_fields_for_prep_update(const uchar *old_data, const uchar *new_data, grn_id record_id) { MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: stored record ID: %d", record_id)); my_ptrdiff_t ptr_diff_old = PTR_BYTE_DIFF(old_data, table->record[0]); my_ptrdiff_t ptr_diff_new = 0; #ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS if (!written_by_row_based_binlog) { if (check_written_by_row_based_binlog()) { written_by_row_based_binlog = 2; } else { written_by_row_based_binlog = 1; } } bool need_all_columns = (new_data && written_by_row_based_binlog == 2); #endif if (new_data) { ptr_diff_new = PTR_BYTE_DIFF(new_data, table->record[0]); } int i; int n_columns = table->s->fields; for (i = 0; i < n_columns; i++) { Field *field = table->field[i]; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif if ( !bitmap_is_set(table->read_set, field->field_index) && !bitmap_is_set(table->write_set, field->field_index) && #ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS ( need_all_columns || #endif bitmap_is_set(&multiple_column_key_bitmap, field->field_index) #ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS ) #endif ) { mrn::DebugColumnAccess debug_column_access(table, &table->write_set); DBUG_PRINT("info", ("mroonga: store column %d(%d)",i,field->field_index)); grn_obj value; GRN_OBJ_INIT(&value, GRN_BULK, 0, grn_obj_get_range(ctx, grn_columns[i])); grn_obj_get_value(ctx, grn_columns[i], record_id, &value); // old column field->move_field_offset(ptr_diff_old); storage_store_field(field, GRN_BULK_HEAD(&value), GRN_BULK_VSIZE(&value)); field->move_field_offset(-ptr_diff_old); if (new_data) { // new column field->move_field_offset(ptr_diff_new); storage_store_field(field, GRN_BULK_HEAD(&value), GRN_BULK_VSIZE(&value)); field->move_field_offset(-ptr_diff_new); } GRN_OBJ_FIN(ctx, &value); } } DBUG_VOID_RETURN; } void ha_mroonga::storage_store_fields_by_index(uchar *buf) { MRN_DBUG_ENTER_METHOD(); uint key_length; void *key; KEY *key_info = &table->key_info[active_index]; if (table->s->primary_key == active_index) key_length = grn_table_cursor_get_key(ctx, cursor, &key); else key_length = grn_table_cursor_get_key(ctx, index_table_cursor, &key); if (KEY_N_KEY_PARTS(key_info) == 1) { my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(buf, table->record[0]); Field *field = key_info->key_part->field; mrn::DebugColumnAccess debug_column_access(table, &table->write_set); field->move_field_offset(ptr_diff); storage_store_field(field, (const char *)key, key_length); field->move_field_offset(-ptr_diff); } else { uchar enc_buf[MAX_KEY_LENGTH]; uint enc_len; mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info); codec.decode(static_cast(key), key_length, enc_buf, &enc_len); key_restore(buf, enc_buf, key_info, enc_len); } DBUG_VOID_RETURN; } int ha_mroonga::storage_encode_key_normalize_min_sort_chars(Field *field, uchar *buf, uint size) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (size == 0) { DBUG_RETURN(0); } if (!field->has_charset()) { DBUG_RETURN(0); } uint16 raw_min_sort_char = static_cast(field->sort_charset()->min_sort_char); if (raw_min_sort_char <= UINT_MAX8) { uchar min_sort_char = static_cast(raw_min_sort_char); for (uint i = size - 1; i > 0; --i) { if (buf[i] != min_sort_char) { break; } buf[i] = '\0'; } } DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_fixed_size_string(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; memcpy(buf, key, field->field_length); *size = field->field_length; DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_variable_size_string(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; *size = uint2korr(key); memcpy(buf, key + HA_KEY_BLOB_LENGTH, *size); storage_encode_key_normalize_min_sort_chars(field, buf, *size); DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_timestamp(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; long long int time; MYSQL_TIME mysql_time; #ifdef MRN_MARIADB_P if (field->decimals() == 0) { my_time_t my_time = sint4korr(key); mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, my_time); mysql_time.second_part = 0; } else { Field_timestamp_hires *timestamp_hires_field = (Field_timestamp_hires *)field; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; TABLE *table_backup = field->table; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); field->table = table; Temporal::Options opt(TIME_CONV_NONE, current_thd); timestamp_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; field->table = table_backup; } #else my_time_t my_time = uint4korr(key); mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, my_time); #endif mrn::TimeConverter time_converter; time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &time, 8); *size = 8; DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_time(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; long long int time; #ifdef MRN_MARIADB_P MYSQL_TIME mysql_time; bool truncated = false; if (field->decimals() == 0) { long long int packed_time = sint3korr(key); mysql_time.neg = false; if (packed_time < 0) { mysql_time.neg = true; packed_time = -packed_time; } mysql_time.year = 0; mysql_time.month = 0; mysql_time.day = 0; mysql_time.hour = (int)(packed_time / 10000); long long int minute_part = packed_time - mysql_time.hour * 10000; mysql_time.minute = (int)(minute_part / 100); mysql_time.second = (int)(minute_part % 100); mysql_time.second_part = 0; mysql_time.time_type = MYSQL_TIMESTAMP_TIME; } else { Field_time_hires *time_hires_field = (Field_time_hires *)field; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); Temporal::Options opt(TIME_CONV_NONE, current_thd); time_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; } mrn::TimeConverter time_converter; time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } #else int mysql_time = (int)sint3korr(key); int sec = mysql_time / 10000 * 60 * 60 + mysql_time / 100 % 100 * 60 + mysql_time % 60; int usec = 0; time = GRN_TIME_PACK(sec, usec); #endif memcpy(buf, &time, 8); *size = 8; DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_year(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; int year = (int)key[0]; struct tm datetime; memset(&datetime, 0, sizeof(struct tm)); datetime.tm_year = year; datetime.tm_mon = 0; datetime.tm_mday = 1; int usec = 0; mrn::TimeConverter time_converter; long long int time = time_converter.tm_to_grn_time(&datetime, usec, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &time, 8); *size = 8; DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_datetime(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; long long int time; #ifdef MRN_MARIADB_P if (field->decimals() > 0) { Field_datetime_hires *datetime_hires_field = (Field_datetime_hires *)field; MYSQL_TIME mysql_time; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); Temporal::Options opt(TIME_CONV_NONE, current_thd); datetime_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; mrn::TimeConverter time_converter; time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); } else #endif { long long int encoded_datetime = sint8korr(key); uint32 part1 = (uint32)(encoded_datetime / 1000000LL); uint32 part2 = (uint32)(encoded_datetime - (unsigned long long int)part1 * 1000000LL); struct tm date; memset(&date, 0, sizeof(struct tm)); date.tm_year = part1 / 10000 - mrn::TimeConverter::TM_YEAR_BASE; date.tm_mon = part1 / 100 % 100 - 1; date.tm_mday = part1 % 100; date.tm_hour = part2 / 10000; date.tm_min = part2 / 100 % 100; date.tm_sec = part2 % 100; int usec = 0; mrn::TimeConverter time_converter; time = time_converter.tm_to_grn_time(&date, usec, &truncated); } if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &time, 8); *size = 8; DBUG_RETURN(error); } #ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 int ha_mroonga::storage_encode_key_timestamp2(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_timestampf *timestamp2_field = (Field_timestampf *)field; struct timeval tm; my_timestamp_from_binary(&tm, key, timestamp2_field->decimals()); MYSQL_TIME mysql_time; mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, (my_time_t)tm.tv_sec); mysql_time.second_part = tm.tv_usec; mrn::TimeConverter time_converter; long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &grn_time, 8); *size = 8; DBUG_RETURN(error); } #endif #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 int ha_mroonga::storage_encode_key_datetime2(Field *field, bool is_null, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_datetimef *datetime2_field = (Field_datetimef *)field; longlong packed_time = is_null ? 0 : my_datetime_packed_from_binary(key, datetime2_field->decimals()); MYSQL_TIME mysql_time; TIME_from_longlong_datetime_packed(&mysql_time, packed_time); mrn::TimeConverter time_converter; long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &grn_time, 8); *size = 8; DBUG_RETURN(error); } #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 int ha_mroonga::storage_encode_key_time2(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; bool truncated = false; Field_timef *time2_field = (Field_timef *)field; longlong packed_time = my_time_packed_from_binary(key, time2_field->decimals()); MYSQL_TIME mysql_time; TIME_from_longlong_time_packed(&mysql_time, packed_time); mrn::TimeConverter time_converter; long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &grn_time, 8); *size = 8; DBUG_RETURN(error); } #endif int ha_mroonga::storage_encode_key_enum(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (field->pack_length() == 1) { uchar value; value = key[0]; *size = 1; memcpy(buf, &value, *size); } else { uint16 value; mrn::value_decoder::decode(&value, key); *size = 2; memcpy(buf, &value, *size); } DBUG_RETURN(error); } int ha_mroonga::storage_encode_key_set(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error = 0; Field_set unpacker((uchar *)key, field->field_length, (uchar *)(key - 1), field->null_bit, field->unireg_check, &field->field_name, field->pack_length(), static_cast(field)->typelib, static_cast(field)->charset()); switch (field->pack_length()) { case 1: { int8 signed_value = (int8)(unpacker.val_int()); uint8 unsigned_value = *((uint8 *)&signed_value); *size = 1; memcpy(buf, &unsigned_value, *size); } break; case 2: { int16 signed_value = (int16)(unpacker.val_int()); uint16 unsigned_value = *((uint16 *)&signed_value); *size = 2; memcpy(buf, &unsigned_value, *size); } break; case 3: case 4: { int32 signed_value = (int32)(unpacker.val_int()); uint32 unsigned_value = *((uint32 *)&signed_value); *size = 4; memcpy(buf, &unsigned_value, *size); } break; case 8: default: { int64 signed_value = (int64)(unpacker.val_int()); uint64 unsigned_value = *((uint64 *)&signed_value); *size = 8; memcpy(buf, &unsigned_value, *size); } break; } DBUG_RETURN(error); } int ha_mroonga::storage_encode_key(Field *field, const uchar *key, uchar *buf, uint *size) { MRN_DBUG_ENTER_METHOD(); int error; bool truncated = false; bool is_null = false; const uchar *ptr = key; error = mrn_change_encoding(ctx, field->charset()); if (error) DBUG_RETURN(error); if (field->null_bit) { is_null = *ptr; ptr += 1; } switch (field->real_type()) { case MYSQL_TYPE_BIT: case MYSQL_TYPE_TINY: { memcpy(buf, ptr, 1); *size = 1; break; } case MYSQL_TYPE_SHORT: { memcpy(buf, ptr, 2); *size = 2; break; } case MYSQL_TYPE_INT24: { memcpy(buf, ptr, 3); buf[3] = 0; *size = 4; break; } case MYSQL_TYPE_LONG: { memcpy(buf, ptr, 4); *size = 4; break; } case MYSQL_TYPE_TIMESTAMP: error = storage_encode_key_timestamp(field, ptr, buf, size); break; case MYSQL_TYPE_LONGLONG: { memcpy(buf, ptr, 8); *size = 8; break; } case MYSQL_TYPE_FLOAT: { float float_value; double double_value; mrn::value_decoder::decode(&float_value, ptr); double_value = float_value; memcpy(buf, &double_value, 8); *size = 8; break; } case MYSQL_TYPE_DOUBLE: { double val; mrn::value_decoder::decode(&val, ptr); memcpy(buf, &val, 8); *size = 8; break; } case MYSQL_TYPE_TIME: error = storage_encode_key_time(field, ptr, buf, size); break; case MYSQL_TYPE_YEAR: error = storage_encode_key_year(field, ptr, buf, size); break; case MYSQL_TYPE_DATETIME: error = storage_encode_key_datetime(field, ptr, buf, size); break; case MYSQL_TYPE_NEWDATE: { uint32 encoded_date = uint3korr(ptr); struct tm date; memset(&date, 0, sizeof(struct tm)); date.tm_year = encoded_date / (16 * 32) - mrn::TimeConverter::TM_YEAR_BASE; date.tm_mon = encoded_date / 32 % 16 - 1; date.tm_mday = encoded_date % 32; int usec = 0; mrn::TimeConverter time_converter; long long int time = time_converter.tm_to_grn_time(&date, usec, &truncated); if (truncated) { if (MRN_ABORT_ON_WARNING(ha_thd())) { error = MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()); } field->set_warning(MRN_SEVERITY_WARNING, MRN_ERROR_CODE_DATA_TRUNCATE(ha_thd()), 1); } memcpy(buf, &time, 8); *size = 8; break; } #ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 case MYSQL_TYPE_TIMESTAMP2: error = storage_encode_key_timestamp2(field, ptr, buf, size); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 case MYSQL_TYPE_DATETIME2: error = storage_encode_key_datetime2(field, is_null, ptr, buf, size); break; #endif #ifdef MRN_HAVE_MYSQL_TYPE_TIME2 case MYSQL_TYPE_TIME2: error = storage_encode_key_time2(field, ptr, buf, size); break; #endif case MYSQL_TYPE_STRING: error = storage_encode_key_fixed_size_string(field, ptr, buf, size); break; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_BLOB: error = storage_encode_key_variable_size_string(field, ptr, buf, size); break; case MYSQL_TYPE_ENUM: error = storage_encode_key_enum(field, ptr, buf, size); break; case MYSQL_TYPE_SET: error = storage_encode_key_set(field, ptr, buf, size); break; default: error = HA_ERR_UNSUPPORTED; break; } DBUG_RETURN(error); } int ha_mroonga::storage_encode_multiple_column_key(KEY *key_info, const uchar *key, uint key_length, uchar *buffer, uint *encoded_length) { MRN_DBUG_ENTER_METHOD(); mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info); int error = codec.encode(key, key_length, buffer, encoded_length); DBUG_RETURN(error); } int ha_mroonga::storage_encode_multiple_column_key_range(KEY *key_info, const uchar *start, uint start_size, const uchar *end, uint end_size, uchar *min_buffer, uint *min_encoded_size, uchar *max_buffer, uint *max_encoded_size) { MRN_DBUG_ENTER_METHOD(); int error = 0; mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info); uint encoded_key_size = codec.size(); if (start) { memset(min_buffer, 0, encoded_key_size); error = codec.encode(start, start_size, min_buffer, min_encoded_size); // TODO: handle error? *min_encoded_size = encoded_key_size; } if (end) { memset(max_buffer, 0xff, encoded_key_size); error = codec.encode(end, end_size, max_buffer, max_encoded_size); // TODO: handle error? *max_encoded_size = encoded_key_size; } DBUG_RETURN(error); } int ha_mroonga::storage_encode_multiple_column_key_range(KEY *key_info, const key_range *start, const key_range *end, uchar *min_buffer, uint *min_encoded_size, uchar *max_buffer, uint *max_encoded_size) { MRN_DBUG_ENTER_METHOD(); const uchar *start_data = NULL; uint start_size = 0; const uchar *end_data = NULL; uint end_size = 0; if (start) { start_data = start->key; start_size = start->length; } if (end) { end_data = end->key; end_size = end->length; } int error = storage_encode_multiple_column_key_range(key_info, start_data, start_size, end_data, end_size, min_buffer, min_encoded_size, max_buffer, max_encoded_size); DBUG_RETURN(error); } int ha_mroonga::generic_reset() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (thd_sql_command(ha_thd()) != SQLCOM_SELECT) { DBUG_RETURN(error); } TABLE_LIST *table_list = table->pos_in_table_list; if (!table_list) { DBUG_RETURN(error); } st_select_lex *select_lex = table_list->select_lex; if (!select_lex) { DBUG_RETURN(error); } List_iterator iterator(*(select_lex->ftfunc_list)); Item_func_match *item; while ((item = iterator++)) { if (item->ft_handler) { mrn_generic_ft_clear(item->ft_handler); } } DBUG_RETURN(error); } int ha_mroonga::wrapper_reset() { MRN_DBUG_ENTER_METHOD(); int error = 0; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_reset(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER if (alter_key_info_buffer) { my_free(alter_key_info_buffer); alter_key_info_buffer = NULL; } #else if (wrap_alter_key_info) { my_free(wrap_alter_key_info); wrap_alter_key_info = NULL; } #endif wrap_ft_init_count = 0; int generic_error = generic_reset(); if (error == 0) { error = generic_error; } DBUG_RETURN(error); } int ha_mroonga::storage_reset() { MRN_DBUG_ENTER_METHOD(); int error; error = generic_reset(); DBUG_RETURN(error); } int ha_mroonga::reset() { int error = 0; THD *thd = ha_thd(); MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: this=%p", this)); clear_empty_value_records(); clear_search_result(); clear_search_result_geo(); if (share->wrapper_mode) error = wrapper_reset(); else error = storage_reset(); ignoring_no_key_columns = false; inserting_with_update = false; ignoring_duplicated_key = false; fulltext_searching = false; replacing_ = false; written_by_row_based_binlog = 0; mrn_lock_type = F_UNLCK; mrn_clear_slot_data(thd); current_ft_item = NULL; DBUG_RETURN(error); } handler *ha_mroonga::wrapper_clone(const char *name, MEM_ROOT *mem_root) { handler *cloned_handler; MRN_DBUG_ENTER_METHOD(); if (!(cloned_handler = get_new_handler(table->s, mem_root, table->s->db_type()))) DBUG_RETURN(NULL); ((ha_mroonga *) cloned_handler)->is_clone = true; ((ha_mroonga *) cloned_handler)->parent_for_clone = this; ((ha_mroonga *) cloned_handler)->mem_root_for_clone = mem_root; if (cloned_handler->ha_open(table, table->s->normalized_path.str, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) { delete cloned_handler; DBUG_RETURN(NULL); } DBUG_RETURN(cloned_handler); } handler *ha_mroonga::storage_clone(const char *name, MEM_ROOT *mem_root) { MRN_DBUG_ENTER_METHOD(); handler *cloned_handler; cloned_handler = handler::clone(name, mem_root); DBUG_RETURN(cloned_handler); } handler *ha_mroonga::clone(const char *name, MEM_ROOT *mem_root) { MRN_DBUG_ENTER_METHOD(); handler *cloned_handler; if (share->wrapper_mode) { cloned_handler = wrapper_clone(name, mem_root); } else { cloned_handler = storage_clone(name, mem_root); } DBUG_RETURN(cloned_handler); } uint8 ha_mroonga::wrapper_table_cache_type() { uint8 res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->table_cache_type(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } uint8 ha_mroonga::storage_table_cache_type() { MRN_DBUG_ENTER_METHOD(); uint8 type = handler::table_cache_type(); DBUG_RETURN(type); } uint8 ha_mroonga::table_cache_type() { MRN_DBUG_ENTER_METHOD(); uint8 type; if (share->wrapper_mode) { type = wrapper_table_cache_type(); } else { type = storage_table_cache_type(); } DBUG_RETURN(type); } #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ ha_rows ha_mroonga::wrapper_multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows; KEY *key_info = &(table->key_info[keyno]); if (mrn_is_geo_key(key_info)) { rows = handler::multi_range_read_info_const(keyno, seq, seq_init_param, n_ranges, bufsz, flags, cost); DBUG_RETURN(rows); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); rows = wrap_handler->multi_range_read_info_const(keyno, seq, seq_init_param, n_ranges, bufsz, flags, cost); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(rows); } ha_rows ha_mroonga::storage_multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows = handler::multi_range_read_info_const(keyno, seq, seq_init_param, n_ranges, bufsz, flags, cost); DBUG_RETURN(rows); } ha_rows ha_mroonga::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows; if (share->wrapper_mode) { rows = wrapper_multi_range_read_info_const(keyno, seq, seq_init_param, n_ranges, bufsz, flags, cost); } else { rows = storage_multi_range_read_info_const(keyno, seq, seq_init_param, n_ranges, bufsz, flags, cost); } DBUG_RETURN(rows); } ha_rows ha_mroonga::wrapper_multi_range_read_info(uint keyno, uint n_ranges, uint keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS uint key_parts, #endif uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows; KEY *key_info = &(table->key_info[keyno]); if (mrn_is_geo_key(key_info)) { rows = handler::multi_range_read_info(keyno, n_ranges, keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS key_parts, #endif bufsz, flags, cost); DBUG_RETURN(rows); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); rows = wrap_handler->multi_range_read_info(keyno, n_ranges, keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS key_parts, #endif bufsz, flags, cost); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(rows); } ha_rows ha_mroonga::storage_multi_range_read_info(uint keyno, uint n_ranges, uint keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS uint key_parts, #endif uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows = handler::multi_range_read_info(keyno, n_ranges, keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS key_parts, #endif bufsz, flags, cost); DBUG_RETURN(rows); } ha_rows ha_mroonga::multi_range_read_info(uint keyno, uint n_ranges, uint keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS uint key_parts, #endif uint *bufsz, uint *flags, Cost_estimate *cost) { MRN_DBUG_ENTER_METHOD(); ha_rows rows; if (share->wrapper_mode) { rows = wrapper_multi_range_read_info(keyno, n_ranges, keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS key_parts, #endif bufsz, flags, cost); } else { rows = storage_multi_range_read_info(keyno, n_ranges, keys, #ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS key_parts, #endif bufsz, flags, cost); } DBUG_RETURN(rows); } int ha_mroonga::wrapper_multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint mode, HANDLER_BUFFER *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = handler::multi_range_read_init(seq, seq_init_param, n_ranges, mode, buf); DBUG_RETURN(error); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); error = wrap_handler->multi_range_read_init(seq, seq_init_param, n_ranges, mode, buf); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint mode, HANDLER_BUFFER *buf) { MRN_DBUG_ENTER_METHOD(); int error = handler::multi_range_read_init(seq, seq_init_param, n_ranges, mode, buf); DBUG_RETURN(error); } int ha_mroonga::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, uint n_ranges, uint mode, HANDLER_BUFFER *buf) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_multi_range_read_init(seq, seq_init_param, n_ranges, mode, buf); } else { error = storage_multi_range_read_init(seq, seq_init_param, n_ranges, mode, buf); } DBUG_RETURN(error); } int ha_mroonga::wrapper_multi_range_read_next(range_id_t *range_info) { MRN_DBUG_ENTER_METHOD(); int error = 0; KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = handler::multi_range_read_next(range_info); DBUG_RETURN(error); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); error = wrap_handler->multi_range_read_next(range_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_multi_range_read_next(range_id_t *range_info) { MRN_DBUG_ENTER_METHOD(); int error = handler::multi_range_read_next(range_info); DBUG_RETURN(error); } int ha_mroonga::multi_range_read_next(range_id_t *range_info) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_multi_range_read_next(range_info); } else { error = storage_multi_range_read_next(range_info); } DBUG_RETURN(error); } #else // MRN_HANDLER_HAVE_MULTI_RANGE_READ int ha_mroonga::wrapper_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer) { int error = 0; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = handler::read_multi_range_first(found_range_p, ranges, range_count, sorted, buffer); DBUG_RETURN(error); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); error = wrap_handler->read_multi_range_first(found_range_p, ranges, range_count, sorted, buffer); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer) { MRN_DBUG_ENTER_METHOD(); int error = handler::read_multi_range_first(found_range_p, ranges, range_count, sorted, buffer); DBUG_RETURN(error); } int ha_mroonga::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_read_multi_range_first(found_range_p, ranges, range_count, sorted, buffer); } else { error = storage_read_multi_range_first(found_range_p, ranges, range_count, sorted, buffer); } DBUG_RETURN(error); } int ha_mroonga::wrapper_read_multi_range_next(KEY_MULTI_RANGE **found_range_p) { int error = 0; MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[active_index]); if (mrn_is_geo_key(key_info)) { error = handler::read_multi_range_next(found_range_p); DBUG_RETURN(error); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); if (fulltext_searching) set_pk_bitmap(); error = wrap_handler->read_multi_range_next(found_range_p); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_read_multi_range_next(KEY_MULTI_RANGE **found_range_p) { MRN_DBUG_ENTER_METHOD(); int error = handler::read_multi_range_next(found_range_p); DBUG_RETURN(error); } int ha_mroonga::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_read_multi_range_next(found_range_p); } else { error = storage_read_multi_range_next(found_range_p); } DBUG_RETURN(error); } #endif // MRN_HANDLER_HAVE_MULTI_RANGE_READ #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS void ha_mroonga::wrapper_start_bulk_insert(ha_rows rows, uint flags) #else void ha_mroonga::wrapper_start_bulk_insert(ha_rows rows) #endif { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS wrap_handler->ha_start_bulk_insert(rows, flags); #else wrap_handler->ha_start_bulk_insert(rows); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS void ha_mroonga::storage_start_bulk_insert(ha_rows rows, uint flags) #else void ha_mroonga::storage_start_bulk_insert(ha_rows rows) #endif { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS void ha_mroonga::start_bulk_insert(ha_rows rows, uint flags) #else void ha_mroonga::start_bulk_insert(ha_rows rows) #endif { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS wrapper_start_bulk_insert(rows, flags); #else wrapper_start_bulk_insert(rows); #endif } else { #ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS storage_start_bulk_insert(rows, flags); #else storage_start_bulk_insert(rows); #endif } DBUG_VOID_RETURN; } int ha_mroonga::wrapper_end_bulk_insert() { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_end_bulk_insert(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_end_bulk_insert() { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::end_bulk_insert() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_end_bulk_insert(); } else { error = storage_end_bulk_insert(); } DBUG_RETURN(error); } int ha_mroonga::generic_delete_all_rows(grn_obj *target_grn_table, const char *function_name) { MRN_DBUG_ENTER_METHOD(); int error = 0; error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: %s::%s", MRN_CLASS_NAME, function_name)); DBUG_RETURN(error); } grn_table_cursor *cursor; cursor = grn_table_cursor_open(ctx, target_grn_table, NULL, 0, NULL, 0, 0, -1, 0); if (cursor) { while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) { grn_table_cursor_delete(ctx, cursor); } grn_table_cursor_close(ctx, cursor); } else { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } int ha_mroonga::wrapper_delete_all_rows() { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_delete_all_rows(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (error) { DBUG_RETURN(error); } if (!wrapper_have_target_index()) { DBUG_RETURN(error); } uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (!(wrapper_is_target_index(key_info))) { continue; } if (!grn_index_tables[i]) { /* disable keys */ continue; } error = generic_delete_all_rows(grn_index_tables[i], __FUNCTION__); if (error) { break; } } int grn_table_error; grn_table_error = generic_delete_all_rows(grn_table, __FUNCTION__); if (!error) { error = grn_table_error; } DBUG_RETURN(error); } int ha_mroonga::storage_delete_all_rows() { MRN_DBUG_ENTER_METHOD(); int error = generic_delete_all_rows(grn_table, __FUNCTION__); if (!error) { uint n_keys = table->s->keys; for (uint i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if (!(key_info->flags & HA_NOSAME)) { continue; } grn_obj *index_table = grn_index_tables[i]; if (!index_table) { continue; } error = generic_delete_all_rows(index_table, __FUNCTION__); if (error) { break; } } } DBUG_RETURN(error); } int ha_mroonga::delete_all_rows() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_delete_all_rows(); } else { error = storage_delete_all_rows(); } DBUG_RETURN(error); } int ha_mroonga::wrapper_truncate() { int error = 0; MRN_SHARE *tmp_share; MRN_DBUG_ENTER_METHOD(); if (!(tmp_share = mrn_get_share(table->s->table_name.str, table, &error))) DBUG_RETURN(error); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = parse_engine_table_options(ha_thd(), tmp_share->hton, table->s) ? MRN_GET_ERROR_NUMBER : wrap_handler->ha_truncate(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); mrn_free_share(tmp_share); if (!error && wrapper_have_target_index()) { error = wrapper_truncate_index(); } DBUG_RETURN(error); } int ha_mroonga::wrapper_truncate_index() { MRN_DBUG_ENTER_METHOD(); int error = 0; error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: %s::%s", MRN_CLASS_NAME, __FUNCTION__)); DBUG_RETURN(error); } grn_rc rc; uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { KEY *key_info = &(table->key_info[i]); if (!(wrapper_is_target_index(key_info))) { continue; } if (!grn_index_tables[i]) { /* disable keys */ continue; } rc = grn_table_truncate(ctx, grn_index_tables[i]); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } err: rc = grn_table_truncate(ctx, grn_table); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } int ha_mroonga::storage_truncate() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (is_dry_write()) { DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__)); DBUG_RETURN(error); } grn_rc rc; rc = grn_table_truncate(ctx, grn_table); if (rc) { my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0)); DBUG_RETURN(ER_ERROR_ON_WRITE); } error = storage_truncate_index(); if (!error && thd_sql_command(ha_thd()) == SQLCOM_TRUNCATE) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; mrn::Lock lock(&long_term_share->auto_inc_mutex); long_term_share->auto_inc_value = 0; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); long_term_share->auto_inc_inited = false; } DBUG_RETURN(error); } int ha_mroonga::storage_truncate_index() { MRN_DBUG_ENTER_METHOD(); int error = 0; error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); grn_rc rc; uint i; uint n_keys = table->s->keys; for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } KEY *key_info = &(table->key_info[i]); if ( !(key_info->flags & HA_NOSAME) && (KEY_N_KEY_PARTS(key_info) == 1 || (key_info->flags & HA_FULLTEXT)) ) { continue; } if (!grn_index_tables[i]) { /* disable keys */ continue; } rc = grn_table_truncate(ctx, grn_index_tables[i]); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); goto err; } } err: DBUG_RETURN(error); } int ha_mroonga::truncate() { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_truncate(); } else { error = storage_truncate(); } if (!error) { operations_->clear(table->s->table_name.str, table->s->table_name.length); } DBUG_RETURN(error); } double ha_mroonga::wrapper_scan_time() { double res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->scan_time(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } double ha_mroonga::storage_scan_time() { MRN_DBUG_ENTER_METHOD(); double time = handler::scan_time(); DBUG_RETURN(time); } double ha_mroonga::scan_time() { MRN_DBUG_ENTER_METHOD(); double time; if (share->wrapper_mode) { time = wrapper_scan_time(); } else { time = storage_scan_time(); } DBUG_RETURN(time); } double ha_mroonga::wrapper_read_time(uint index, uint ranges, ha_rows rows) { double res; MRN_DBUG_ENTER_METHOD(); if (index < MAX_KEY) { KEY *key_info = &(table->key_info[index]); if (mrn_is_geo_key(key_info)) { res = handler::read_time(index, ranges, rows); DBUG_RETURN(res); } MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->read_time(share->wrap_key_nr[index], ranges, rows); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } else { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->read_time(index, ranges, rows); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } DBUG_RETURN(res); } double ha_mroonga::storage_read_time(uint index, uint ranges, ha_rows rows) { MRN_DBUG_ENTER_METHOD(); double time = handler::read_time(index, ranges, rows); DBUG_RETURN(time); } double ha_mroonga::read_time(uint index, uint ranges, ha_rows rows) { MRN_DBUG_ENTER_METHOD(); double time; if (share->wrapper_mode) { time = wrapper_read_time(index, ranges, rows); } else { time = storage_read_time(index, ranges, rows); } DBUG_RETURN(time); } #ifdef MRN_HANDLER_HAVE_KEYS_TO_USE_FOR_SCANNING const key_map *ha_mroonga::wrapper_keys_to_use_for_scanning() { const key_map *res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->keys_to_use_for_scanning(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } const key_map *ha_mroonga::storage_keys_to_use_for_scanning() { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(&key_map_full); } const key_map *ha_mroonga::keys_to_use_for_scanning() { MRN_DBUG_ENTER_METHOD(); const key_map *key_map; if (share->wrapper_mode) { key_map = wrapper_keys_to_use_for_scanning(); } else { key_map = storage_keys_to_use_for_scanning(); } DBUG_RETURN(key_map); } #endif ha_rows ha_mroonga::wrapper_estimate_rows_upper_bound() { ha_rows res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->estimate_rows_upper_bound(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } ha_rows ha_mroonga::storage_estimate_rows_upper_bound() { MRN_DBUG_ENTER_METHOD(); ha_rows rows = handler::estimate_rows_upper_bound(); DBUG_RETURN(rows); } ha_rows ha_mroonga::estimate_rows_upper_bound() { MRN_DBUG_ENTER_METHOD(); ha_rows rows; if (share->wrapper_mode) { rows = wrapper_estimate_rows_upper_bound(); } else { rows = storage_estimate_rows_upper_bound(); } DBUG_RETURN(rows); } void ha_mroonga::wrapper_update_create_info(HA_CREATE_INFO* create_info) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->update_create_info(create_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_update_create_info(HA_CREATE_INFO* create_info) { MRN_DBUG_ENTER_METHOD(); handler::update_create_info(create_info); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; if (!long_term_share->auto_inc_inited) { storage_info(HA_STATUS_AUTO); } create_info->auto_increment_value = long_term_share->auto_inc_value; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); } DBUG_VOID_RETURN; } void ha_mroonga::update_create_info(HA_CREATE_INFO* create_info) { MRN_DBUG_ENTER_METHOD(); if (!create_info->connect_string.str) { create_info->connect_string.str = table->s->connect_string.str; create_info->connect_string.length = table->s->connect_string.length; } if (share->wrapper_mode) wrapper_update_create_info(create_info); else storage_update_create_info(create_info); st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), true); if (slot_data) { slot_data->alter_create_info = create_info; if (slot_data->alter_connect_string) { my_free(slot_data->alter_connect_string); slot_data->alter_connect_string = NULL; } if (create_info->connect_string.str) { slot_data->alter_connect_string = mrn_my_strndup(create_info->connect_string.str, create_info->connect_string.length, MYF(MY_WME)); } if (slot_data->alter_comment) { my_free(slot_data->alter_comment); slot_data->alter_comment = NULL; } if (create_info->comment.str) { slot_data->alter_comment = mrn_my_strndup(create_info->comment.str, create_info->comment.length, MYF(MY_WME)); } if (share && share->disable_keys) { slot_data->disable_keys_create_info = create_info; } } DBUG_VOID_RETURN; } int ha_mroonga::wrapper_rename_table(const char *from, const char *to, MRN_SHARE *tmp_share, const char *from_table_name, const char *to_table_name) { int error = 0; handler *hnd; MRN_DBUG_ENTER_METHOD(); hnd = get_new_handler(tmp_share->table_share, current_thd->mem_root, tmp_share->hton); if (!hnd) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); } if ((error = hnd->ha_rename_table(from, to))) { delete hnd; DBUG_RETURN(error); } error = wrapper_rename_index(from, to, tmp_share, from_table_name, to_table_name); delete hnd; DBUG_RETURN(error); } int ha_mroonga::wrapper_rename_index(const char *from, const char *to, MRN_SHARE *tmp_share, const char *from_table_name, const char *to_table_name) { int error; grn_rc rc; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); error = ensure_database_open(from); if (error) DBUG_RETURN(error); TABLE_SHARE *tmp_table_share = tmp_share->table_share; uint i; for (i = 0; i < tmp_table_share->keys; i++) { const char *mysql_index_name = tmp_table_share->key_info[i].name.str; mrn::IndexTableName from_index_table_name(from_table_name, mysql_index_name); mrn::IndexTableName to_index_table_name(to_table_name, mysql_index_name); grn_obj *index_table; index_table = grn_ctx_get(ctx, from_index_table_name.c_str(), from_index_table_name.length()); if (!index_table) { index_table = grn_ctx_get(ctx, from_index_table_name.old_c_str(), from_index_table_name.old_length()); } if (index_table) { rc = grn_table_rename(ctx, index_table, to_index_table_name.c_str(), to_index_table_name.length()); if (rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } } } grn_obj *table = grn_ctx_get(ctx, from_table_name, strlen(from_table_name)); if (ctx->rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } rc = grn_table_rename(ctx, table, to_table_name, strlen(to_table_name)); if (rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } DBUG_RETURN(0); } int ha_mroonga::storage_rename_table(const char *from, const char *to, MRN_SHARE *tmp_share, const char *from_table_name, const char *to_table_name) { int error; grn_rc rc; TABLE_SHARE *tmp_table_share = tmp_share->table_share; MRN_LONG_TERM_SHARE *from_long_term_share = tmp_share->long_term_share, *to_long_term_share; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); error = ensure_database_open(from); if (error) DBUG_RETURN(error); if (!(to_long_term_share = mrn_get_long_term_share(to, strlen(to), &error))) DBUG_RETURN(error); to_long_term_share->auto_inc_value = from_long_term_share->auto_inc_value; DBUG_PRINT("info", ("mroonga: to_auto_inc_value=%llu", to_long_term_share->auto_inc_value)); to_long_term_share->auto_inc_inited = from_long_term_share->auto_inc_inited; uint i; for (i = 0; i < tmp_table_share->keys; i++) { const char *mysql_index_name = tmp_table_share->key_info[i].name.str; mrn::IndexTableName from_index_table_name(from_table_name, mysql_index_name); mrn::IndexTableName to_index_table_name(to_table_name, mysql_index_name); grn_obj *index_table; index_table = grn_ctx_get(ctx, from_index_table_name.c_str(), from_index_table_name.length()); if (!index_table) { index_table = grn_ctx_get(ctx, from_index_table_name.old_c_str(), from_index_table_name.old_length()); } if (index_table) { rc = grn_table_rename(ctx, index_table, to_index_table_name.c_str(), to_index_table_name.length()); if (rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error_end; } } } #ifdef MRN_SUPPORT_FOREIGN_KEYS error = storage_rename_foreign_key(tmp_share, from_table_name, to_table_name); if (error) { goto error_end; } #endif { grn_obj *table_obj = grn_ctx_get(ctx, from_table_name, strlen(from_table_name)); if (ctx->rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error_end; } rc = grn_table_rename(ctx, table_obj, to_table_name, strlen(to_table_name)); if (rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); goto error_end; } } DBUG_RETURN(0); error_end: mrn_free_long_term_share(to_long_term_share); DBUG_RETURN(error); } #ifdef MRN_SUPPORT_FOREIGN_KEYS int ha_mroonga::storage_rename_foreign_key(MRN_SHARE *tmp_share, const char *from_table_name, const char *to_table_name) { int error; uint i; grn_obj *column, *ref_column; grn_rc rc; TABLE_SHARE *tmp_table_share = tmp_share->table_share; uint n_columns = tmp_table_share->fields; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_columns; ++i) { Field *field = tmp_table_share->field[i]; if (!is_foreign_key_field(from_table_name, field->field_name.str)) { continue; } grn_obj *grn_from_table = grn_ctx_get(ctx, from_table_name, -1); mrn::ColumnName column_name(field->field_name); column = grn_obj_column(ctx, grn_from_table, column_name.c_str(), column_name.length()); if (!column) { continue; } grn_id ref_table_id = grn_obj_get_range(ctx, column); grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id); mrn::IndexColumnName from_index_column_name(from_table_name, column_name.c_str()); ref_column = grn_obj_column(ctx, ref_table, from_index_column_name.c_str(), from_index_column_name.length()); if (!ref_column) { continue; } mrn::IndexColumnName to_index_column_name(to_table_name, column_name.c_str()); rc = grn_column_rename(ctx, ref_column, to_index_column_name.c_str(), to_index_column_name.length()); if (rc != GRN_SUCCESS) { error = ER_CANT_OPEN_FILE; my_message(error, ctx->errbuf, MYF(0)); DBUG_RETURN(error); } } DBUG_RETURN(0); } #endif int ha_mroonga::rename_table(const char *from, const char *to) { int error = 0; TABLE_LIST table_list; TABLE_SHARE *tmp_table_share; TABLE tmp_table; MRN_SHARE *tmp_share; MRN_DBUG_ENTER_METHOD(); mrn::PathMapper to_mapper(to); mrn::PathMapper from_mapper(from); if (strcmp(from_mapper.db_name(), to_mapper.db_name())) DBUG_RETURN(HA_ERR_WRONG_COMMAND); LEX_CSTRING db_name= { from_mapper.db_name(), strlen(from_mapper.db_name()) }; LEX_CSTRING table_name= { from_mapper.mysql_table_name(), strlen(from_mapper.mysql_table_name()) }; table_list.init_one_table(&db_name, &table_name, 0,TL_WRITE); mrn_open_mutex_lock(NULL); tmp_table_share = mrn_create_tmp_table_share(&table_list, from, &error); mrn_open_mutex_unlock(NULL); if (!tmp_table_share) { DBUG_RETURN(error); } tmp_table.s = tmp_table_share; #ifdef WITH_PARTITION_STORAGE_ENGINE tmp_table.part_info = NULL; #endif if (!(tmp_share = mrn_get_share(from, &tmp_table, &error))) { mrn_open_mutex_lock(NULL); mrn_free_tmp_table_share(tmp_table_share); mrn_open_mutex_unlock(NULL); DBUG_RETURN(error); } if (tmp_share->wrapper_mode) { error = wrapper_rename_table(from, to, tmp_share, from_mapper.table_name(), to_mapper.table_name()); } else { error = storage_rename_table(from, to, tmp_share, from_mapper.table_name(), to_mapper.table_name()); } if (!error && to_mapper.table_name()[0] == '#') { error = add_wrap_hton(to, tmp_share->hton); } else if (error && from_mapper.table_name()[0] == '#') { add_wrap_hton(from, tmp_share->hton); } if (!error) { mrn_free_long_term_share(tmp_share->long_term_share); tmp_share->long_term_share = NULL; } mrn_free_share(tmp_share); mrn_open_mutex_lock(NULL); mrn_free_tmp_table_share(tmp_table_share); mrn_open_mutex_unlock(NULL); DBUG_RETURN(error); } bool ha_mroonga::wrapper_is_crashed() const { bool res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->is_crashed(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_is_crashed() const { MRN_DBUG_ENTER_METHOD(); mrn::DatabaseRepairer repairer(ctx, ha_thd()); bool crashed = repairer.is_crashed(); DBUG_RETURN(crashed); } bool ha_mroonga::is_crashed() const { MRN_DBUG_ENTER_METHOD(); bool crashed; if (share->wrapper_mode) { crashed = wrapper_is_crashed(); } else { crashed = storage_is_crashed(); } DBUG_RETURN(crashed); } bool ha_mroonga::wrapper_auto_repair(int error) const { bool repaired; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_AUTO_REPAIR_HAVE_ERROR repaired = wrap_handler->auto_repair(error); #else repaired = wrap_handler->auto_repair(); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(repaired); } bool ha_mroonga::storage_auto_repair(int error) const { MRN_DBUG_ENTER_METHOD(); bool repaired; #ifdef MRN_HANDLER_AUTO_REPAIR_HAVE_ERROR repaired = handler::auto_repair(error); #else repaired = handler::auto_repair(); #endif DBUG_RETURN(repaired); } bool ha_mroonga::auto_repair(int error) const { MRN_DBUG_ENTER_METHOD(); bool repaired; // TODO: We should consider about creating share for error = // ER_CANT_OPEN_FILE. The following code just ignores the error. if (share && share->wrapper_mode) { repaired = wrapper_auto_repair(error); } else { repaired = storage_auto_repair(error); } DBUG_RETURN(repaired); } bool ha_mroonga::auto_repair() const { MRN_DBUG_ENTER_METHOD(); bool repaired = auto_repair(HA_ERR_CRASHED_ON_USAGE); DBUG_RETURN(repaired); } int ha_mroonga::generic_disable_index(int i, KEY *key_info) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->index_table[i]) { char index_column_name[GRN_TABLE_MAX_KEY_SIZE]; snprintf(index_column_name, GRN_TABLE_MAX_KEY_SIZE - 1, "%s.%s", share->index_table[i], key_info[i].name.str); grn_obj *index_column = grn_ctx_get(ctx, index_column_name, strlen(index_column_name)); if (index_column) { grn_obj_remove(ctx, index_column); } } else { mrn::PathMapper mapper(share->table_name); mrn::IndexTableName index_table_name(mapper.table_name(), key_info[i].name.str); grn_obj *index_table = grn_ctx_get(ctx, index_table_name.c_str(), index_table_name.length()); if (!index_table) { index_table = grn_ctx_get(ctx, index_table_name.old_c_str(), index_table_name.old_length()); } if (index_table) { grn_obj_remove(ctx, index_table); } } if (ctx->rc == GRN_SUCCESS) { grn_index_tables[i] = NULL; grn_index_columns[i] = NULL; } else { // TODO: Implement ctx->rc to error converter and use it. error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); } DBUG_RETURN(error); } int ha_mroonga::wrapper_disable_indexes_mroonga(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); uint i; for (i = 0; i < table_share->keys; i++) { if (i == table->s->primary_key) { continue; } if (share->wrap_key_nr[i] < MAX_KEY) { continue; } if (!grn_index_tables[i]) { DBUG_PRINT("info", ("mroonga: keys are disabled already %u", i)); DBUG_RETURN(0); } } KEY *key_info = table_share->key_info; for (i = 0; i < table_share->keys; i++) { if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) { continue; } int sub_error = generic_disable_index(i, key_info); if (error != 0 && sub_error != 0) { error = sub_error; } } DBUG_RETURN(error); } int ha_mroonga::wrapper_disable_indexes(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_disable_indexes(map, persist); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (error == HA_ERR_WRONG_COMMAND) { error = 0; } if (!error) { error = wrapper_disable_indexes_mroonga(map, persist); } DBUG_RETURN(error); } int ha_mroonga::storage_disable_indexes(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); uint i; for (i = 0; i < table_share->keys; i++) { if (i == table->s->primary_key) { continue; } if (!grn_index_tables[i]) { DBUG_PRINT("info", ("mroonga: keys are disabled already %u", i)); DBUG_RETURN(0); } } KEY *key_info = table_share->key_info; for (i = 0; i < table_share->keys; i++) { if (i == table->s->primary_key) { continue; } if (map.is_set(i)) { continue; } int sub_error = generic_disable_index(i, key_info); if (error != 0 && sub_error != 0) { error = sub_error; } } DBUG_RETURN(error); } int ha_mroonga::disable_indexes(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { error = wrapper_disable_indexes(map, persist); } else { error = storage_disable_indexes(map, persist); } DBUG_RETURN(error); } int ha_mroonga::wrapper_enable_indexes_mroonga(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); uint i, j; for (i = 0; i < table_share->keys; i++) { if (i == table->s->primary_key) { continue; } if (share->wrap_key_nr[i] < MAX_KEY) { continue; } if (!grn_index_columns[i]) { break; } } if (i == table_share->keys) { DBUG_PRINT("info", ("mroonga: keys are enabled already")); DBUG_RETURN(0); } KEY *p_key_info = &table->key_info[table_share->primary_key]; KEY *key_info = table_share->key_info; uint n_keys = table_share->keys; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, n_keys); bitmap_clear_all(table->read_set); mrn_set_bitmap_by_key(table->read_set, p_key_info); mrn::PathMapper mapper(share->table_name); for (i = 0, j = 0; i < n_keys; i++) { if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) { j++; continue; } if ((error = mrn_add_index_param(share, &key_info[i], i))) { break; } index_tables[i] = NULL; index_columns[i] = NULL; if (!grn_index_columns[i]) { if ( (key_info[i].flags & HA_FULLTEXT) && (error = wrapper_create_index_fulltext(mapper.table_name(), i, &key_info[i], index_tables, index_columns, share)) ) { break; } else if ( mrn_is_geo_key(&key_info[i]) && (error = wrapper_create_index_geo(mapper.table_name(), i, &key_info[i], index_tables, index_columns, share)) ) { break; } grn_index_columns[i] = index_columns[i]; } mrn_set_bitmap_by_key(table->read_set, &key_info[i]); } if (!error && i > j) { error = wrapper_fill_indexes(ha_thd(), table->key_info, index_columns, n_keys); } bitmap_set_all(table->read_set); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(error); } int ha_mroonga::wrapper_enable_indexes(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); int mroonga_error = wrapper_enable_indexes_mroonga(map, persist); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_enable_indexes(map, persist); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (error == HA_ERR_WRONG_COMMAND) { error = mroonga_error; } DBUG_RETURN(error); } int ha_mroonga::storage_enable_indexes(key_map map, bool persist) { int error = 0; uint n_keys = table_share->keys; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, n_keys); bool have_multiple_column_index = false; MRN_DBUG_ENTER_METHOD(); uint i; for (i = 0; i < n_keys; i++) { if (i == table->s->primary_key) { continue; } if (!grn_index_columns[i]) { break; } } if (i == n_keys) { DBUG_PRINT("info", ("mroonga: keys are enabled already")); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(0); } KEY *key_info = table->key_info; bitmap_clear_all(table->read_set); mrn::PathMapper mapper(share->table_name); for (; i < n_keys; i++) { if (!map.is_set(i)) { continue; } if ((error = mrn_add_index_param(share, &key_info[i], i))) { break; } index_tables[i] = NULL; if (!grn_index_columns[i]) { if ((error = storage_create_index(table, mapper.table_name(), grn_table, share, &key_info[i], index_tables, index_columns, i))) { break; } if ( KEY_N_KEY_PARTS(&(key_info[i])) != 1 && !(key_info[i].flags & HA_FULLTEXT) ) { mrn_set_bitmap_by_key(table->read_set, &key_info[i]); have_multiple_column_index = true; } grn_index_tables[i] = index_tables[i]; grn_index_columns[i] = index_columns[i]; } else { index_columns[i] = NULL; } } if (!error && have_multiple_column_index) { bool skip_unique_key= !table->s->keys_in_use.is_clear_all(); error = storage_add_index_multiple_columns(key_info, n_keys, index_tables, index_columns, skip_unique_key); } bitmap_set_all(table->read_set); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(error); } int ha_mroonga::enable_indexes(key_map map, bool persist) { int error = 0; MRN_DBUG_ENTER_METHOD(); share->disable_keys = false; if (share->wrapper_mode) { error = wrapper_enable_indexes(map, persist); } else { error = storage_enable_indexes(map, persist); } DBUG_RETURN(error); } int ha_mroonga::wrapper_check(THD* thd, HA_CHECK_OPT* check_opt) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_check(thd, check_opt); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_check(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); mrn::DatabaseRepairer repairer(ctx, thd); if (repairer.is_corrupt()) { DBUG_RETURN(HA_ADMIN_CORRUPT); } else { DBUG_RETURN(HA_ADMIN_OK); } } int ha_mroonga::check(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_check(thd, check_opt); } else { error = storage_check(thd, check_opt); } DBUG_RETURN(error); } int ha_mroonga::wrapper_fill_indexes(THD *thd, KEY *key_info, grn_obj **index_columns, uint n_keys) { int error = 0; KEY *p_key_info = &table->key_info[table_share->primary_key]; KEY *tmp_key_info; #ifdef MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK int wrapper_lock_type_backup = wrap_handler->get_lock_type(); #endif MRN_DBUG_ENTER_METHOD(); DBUG_PRINT("info", ("mroonga: n_keys=%u", n_keys)); grn_bool need_lock = true; if (mrn_lock_type != F_UNLCK) { need_lock = false; } #ifdef MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK if (wrapper_lock_type_backup != F_UNLCK) { need_lock = false; } #endif if (need_lock) { error = wrapper_external_lock(thd, F_WRLCK); } if (!error) { if ( !(error = wrapper_start_stmt(thd, thr_lock_data.type)) && !(error = wrapper_rnd_init(true)) ) { grn_obj key; GRN_TEXT_INIT(&key, 0); grn_bulk_space(ctx, &key, p_key_info->key_length); while (!(error = wrapper_rnd_next(table->record[0]))) { key_copy((uchar *)(GRN_TEXT_VALUE(&key)), table->record[0], p_key_info, p_key_info->key_length); int added; grn_id record_id; mrn_change_encoding(ctx, NULL); record_id = grn_table_add(ctx, grn_table, GRN_TEXT_VALUE(&key), p_key_info->key_length, &added); if (record_id == GRN_ID_NIL) { char error_message[MRN_MESSAGE_BUFFER_SIZE]; snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, "failed to add a new record into groonga: key=<%.*s>", (int) p_key_info->key_length, GRN_TEXT_VALUE(&key)); error = ER_ERROR_ON_WRITE; my_message(error, error_message, MYF(0)); } if (error) break; uint k; for (k = 0; k < n_keys; k++) { tmp_key_info = &key_info[k]; if (!(tmp_key_info->flags & HA_FULLTEXT) && !mrn_is_geo_key(tmp_key_info)) { continue; } if (!index_columns[k]) { continue; } DBUG_PRINT("info", ("mroonga: key_num=%u", k)); uint l; for (l = 0; l < KEY_N_KEY_PARTS(tmp_key_info); l++) { Field *field = tmp_key_info->key_part[l].field; if (field->is_null()) continue; error = mrn_change_encoding(ctx, field->charset()); if (error) break; error = generic_store_bulk(field, &new_value_buffer); if (error) { my_message(error, "mroonga: wrapper: " "failed to get new value for updating index.", MYF(0)); break; } grn_obj *index_column = index_columns[k]; grn_rc rc; rc = grn_column_index_update(ctx, index_column, record_id, l + 1, NULL, &new_value_buffer); grn_obj_unlink(ctx, index_column); if (rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); break; } } if (error) break; } if (error) break; } grn_obj_unlink(ctx, &key); if (error != HA_ERR_END_OF_FILE) wrapper_rnd_end(); else error = wrapper_rnd_end(); } if (need_lock) { wrapper_external_lock(thd, F_UNLCK); } } DBUG_RETURN(error); } int ha_mroonga::wrapper_recreate_indexes(THD *thd) { int error; uint i, n_keys = table_share->keys; KEY *p_key_info = &table->key_info[table_share->primary_key]; KEY *key_info = table->key_info; MRN_DBUG_ENTER_METHOD(); mrn::PathMapper mapper(table_share->normalized_path.str); bitmap_clear_all(table->read_set); clear_indexes(); remove_grn_obj_force(mapper.table_name()); grn_table = NULL; mrn_set_bitmap_by_key(table->read_set, p_key_info); for (i = 0; i < n_keys; i++) { if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) { continue; } mrn::IndexTableName index_table_name(mapper.table_name(), table_share->key_info[i].name.str); char index_column_full_name[MRN_MAX_PATH_SIZE]; snprintf(index_column_full_name, MRN_MAX_PATH_SIZE, "%s.%s", index_table_name.c_str(), INDEX_COLUMN_NAME); remove_grn_obj_force(index_column_full_name); remove_grn_obj_force(index_table_name.c_str()); char index_column_full_old_name[MRN_MAX_PATH_SIZE]; snprintf(index_column_full_old_name, MRN_MAX_PATH_SIZE, "%s.%s", index_table_name.old_c_str(), INDEX_COLUMN_NAME); remove_grn_obj_force(index_column_full_old_name); remove_grn_obj_force(index_table_name.old_c_str()); mrn_set_bitmap_by_key(table->read_set, &key_info[i]); } error = wrapper_create_index(table_share->normalized_path.str, table, share); if (error) DBUG_RETURN(error); error = wrapper_open_indexes(table_share->normalized_path.str); if (error) DBUG_RETURN(error); error = wrapper_fill_indexes(thd, key_info, grn_index_columns, n_keys); bitmap_set_all(table->read_set); DBUG_RETURN(error); } int ha_mroonga::storage_recreate_indexes(THD *thd) { MRN_DBUG_ENTER_METHOD(); if (share->disable_keys) DBUG_RETURN(HA_ADMIN_OK); clear_indexes(); int n_columns = table->s->fields; for (int i = 0; i < n_columns; i++) { grn_obj *column = grn_columns[i]; if (!column) continue; int n_hooks = grn_obj_get_nhooks(ctx, column, GRN_HOOK_SET); for (int j = 0; j < n_hooks; j++) { grn_obj_delete_hook(ctx, column, GRN_HOOK_SET, j); } } uint n_keys = table_share->keys; mrn::PathMapper mapper(table_share->normalized_path.str); for (uint i = 0; i < n_keys; i++) { if (share->index_table && share->index_table[i]) continue; if (i == table_share->primary_key) continue; mrn::IndexTableName index_table_name(mapper.table_name(), table_share->key_info[i].name.str); char index_column_full_name[MRN_MAX_PATH_SIZE]; snprintf(index_column_full_name, MRN_MAX_PATH_SIZE, "%s.%s", index_table_name.c_str(), INDEX_COLUMN_NAME); remove_grn_obj_force(index_column_full_name); remove_grn_obj_force(index_table_name.c_str()); char index_column_full_old_name[MRN_MAX_PATH_SIZE]; snprintf(index_column_full_old_name, MRN_MAX_PATH_SIZE, "%s.%s", index_table_name.old_c_str(), INDEX_COLUMN_NAME); remove_grn_obj_force(index_column_full_old_name); remove_grn_obj_force(index_table_name.old_c_str()); } int error; error = storage_create_indexes(table, mapper.table_name(), grn_table, share); if (error) DBUG_RETURN(HA_ADMIN_FAILED); error = storage_open_indexes(table_share->normalized_path.str); if (error) DBUG_RETURN(HA_ADMIN_FAILED); DBUG_RETURN(HA_ADMIN_OK); } int ha_mroonga::wrapper_repair(THD* thd, HA_CHECK_OPT* check_opt) { int error; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_repair(thd, check_opt); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (error && error != HA_ADMIN_NOT_IMPLEMENTED) DBUG_RETURN(error); error = wrapper_recreate_indexes(thd); DBUG_RETURN(error); } int ha_mroonga::storage_repair(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); int error = storage_recreate_indexes(thd); DBUG_RETURN(error); } int ha_mroonga::repair(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); int error = 0; share->disable_keys = false; if (share->wrapper_mode) { error = wrapper_repair(thd, check_opt); } else { error = storage_repair(thd, check_opt); } DBUG_RETURN(error); } bool ha_mroonga::wrapper_check_and_repair(THD *thd) { bool is_error_or_not_supported; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); is_error_or_not_supported = wrap_handler->ha_check_and_repair(thd); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(is_error_or_not_supported); } bool ha_mroonga::storage_check_and_repair(THD *thd) { MRN_DBUG_ENTER_METHOD(); bool is_error = false; mrn::DatabaseRepairer repairer(ctx, thd); is_error = !repairer.repair(); DBUG_RETURN(is_error); } bool ha_mroonga::check_and_repair(THD *thd) { MRN_DBUG_ENTER_METHOD(); bool is_error_or_not_supported; if (share->wrapper_mode) { is_error_or_not_supported = wrapper_check_and_repair(thd); } else { is_error_or_not_supported = storage_check_and_repair(thd); } DBUG_RETURN(is_error_or_not_supported); } int ha_mroonga::wrapper_analyze(THD* thd, HA_CHECK_OPT* check_opt) { int error = 0; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->ha_analyze(thd, check_opt); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_analyze(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); } int ha_mroonga::analyze(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_analyze(thd, check_opt); } else { error = storage_analyze(thd, check_opt); } DBUG_RETURN(error); } int ha_mroonga::wrapper_optimize(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(HA_ADMIN_TRY_ALTER); } int ha_mroonga::storage_optimize(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED); } int ha_mroonga::optimize(THD* thd, HA_CHECK_OPT* check_opt) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (share->wrapper_mode) { error = wrapper_optimize(thd, check_opt); } else { error = storage_optimize(thd, check_opt); } DBUG_RETURN(error); } bool ha_mroonga::wrapper_is_fatal_error(int error_num, uint flags) { bool res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_IS_FATAL_ERROR_HAVE_FLAGS res = wrap_handler->is_fatal_error(error_num, flags); #else res = wrap_handler->is_fatal_error(error_num); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_is_fatal_error(int error_num, uint flags) { MRN_DBUG_ENTER_METHOD(); #ifdef MRN_HANDLER_IS_FATAL_ERROR_HAVE_FLAGS bool is_fatal_error = handler::is_fatal_error(error_num, flags); #else bool is_fatal_error = handler::is_fatal_error(error_num); #endif DBUG_RETURN(is_fatal_error); } bool ha_mroonga::is_fatal_error(int error_num, uint flags) { MRN_DBUG_ENTER_METHOD(); bool is_fatal_error; if (share->wrapper_mode) { is_fatal_error = wrapper_is_fatal_error(error_num, flags); } else { is_fatal_error = storage_is_fatal_error(error_num, flags); } DBUG_RETURN(is_fatal_error); } bool ha_mroonga::wrapper_check_if_incompatible_data( HA_CREATE_INFO *create_info, uint table_changes) { bool res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->check_if_incompatible_data(create_info, table_changes); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_check_if_incompatible_data( HA_CREATE_INFO *create_info, uint table_changes) { MRN_DBUG_ENTER_METHOD(); uint n = table_share->fields; for (uint i = 0; i < n; i++) { Field *field = table->field[i]; if (field->flags & FIELD_IS_RENAMED) { DBUG_RETURN(COMPATIBLE_DATA_NO); } } DBUG_RETURN(COMPATIBLE_DATA_YES); } bool ha_mroonga::check_if_incompatible_data( HA_CREATE_INFO *create_info, uint table_changes) { MRN_DBUG_ENTER_METHOD(); bool res; if ( create_info->comment.str != table_share->comment.str || create_info->connect_string.str != table_share->connect_string.str ) { DBUG_RETURN(COMPATIBLE_DATA_NO); } if (share->wrapper_mode) { res = wrapper_check_if_incompatible_data(create_info, table_changes); } else { res = storage_check_if_incompatible_data(create_info, table_changes); } DBUG_RETURN(res); } int ha_mroonga::storage_add_index_multiple_columns(KEY *key_info, uint num_of_keys, grn_obj **index_tables, grn_obj **index_columns, bool skip_unique_key) { MRN_DBUG_ENTER_METHOD(); int error = 0; if (!(error = storage_rnd_init(true))) { while (!(error = storage_rnd_next(table->record[0]))) { for (uint i = 0; i < num_of_keys; i++) { KEY *current_key_info = key_info + i; if ( KEY_N_KEY_PARTS(current_key_info) == 1 || (current_key_info->flags & HA_FULLTEXT) ) { continue; } if (skip_unique_key && (key_info[i].flags & HA_NOSAME)) { continue; } if (!index_columns[i]) { continue; } /* fix key_info.key_length */ for (uint j = 0; j < KEY_N_KEY_PARTS(current_key_info); j++) { if ( !current_key_info->key_part[j].null_bit && current_key_info->key_part[j].field->null_bit ) { current_key_info->key_length++; current_key_info->key_part[j].null_bit = current_key_info->key_part[j].field->null_bit; } } if (key_info[i].flags & HA_NOSAME) { grn_id key_id; if ((error = storage_write_row_unique_index(table->record[0], current_key_info, index_tables[i], index_columns[i], &key_id))) { if (error == HA_ERR_FOUND_DUPP_KEY) { error = HA_ERR_FOUND_DUPP_UNIQUE; } break; } } if ((error = storage_write_row_multiple_column_index(table->record[0], record_id, current_key_info, index_columns[i]))) { break; } } if (error) break; } if (error != HA_ERR_END_OF_FILE) { storage_rnd_end(); } else { error = storage_rnd_end(); } } DBUG_RETURN(error); } #ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER bool ha_mroonga::wrapper_is_comment_changed(TABLE *table1, TABLE *table2) { MRN_DBUG_ENTER_METHOD(); if (table1->s->comment.length != table2->s->comment.length) { DBUG_RETURN(true); } if (strncmp(table1->s->comment.str, table2->s->comment.str, table1->s->comment.length) == 0) { DBUG_RETURN(false); } else { DBUG_RETURN(true); } } enum_alter_inplace_result ha_mroonga::wrapper_check_if_supported_inplace_alter( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); uint n_keys; uint i; enum_alter_inplace_result result_mroonga = HA_ALTER_INPLACE_NO_LOCK; DBUG_PRINT("info", ("mroonga: handler_flags=%lu", static_cast(ha_alter_info->handler_flags))); if (wrapper_is_comment_changed(table, altered_table)) { DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } if ( (ha_alter_info->handler_flags & ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX) && (ha_alter_info->handler_flags & ( ALTER_ADD_COLUMN | ALTER_DROP_COLUMN | MRN_ALTER_INPLACE_INFO_ALTER_STORED_COLUMN_TYPE | MRN_ALTER_INPLACE_INFO_ALTER_STORED_COLUMN_ORDER | ALTER_COLUMN_NULLABLE | ALTER_COLUMN_NOT_NULLABLE | ALTER_COLUMN_STORAGE_TYPE | ALTER_ADD_STORED_GENERATED_COLUMN | ALTER_COLUMN_COLUMN_FORMAT ) ) ) { DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } if (ha_alter_info->handler_flags & ALTER_RENAME) { DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } DBUG_ASSERT(ha_alter_info->key_count == altered_table->s->keys); alter_key_count = 0; alter_index_drop_count = 0; alter_index_add_count = 0; alter_handler_flags = ha_alter_info->handler_flags; if (!(alter_key_info_buffer = (KEY *) mrn_my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &alter_key_info_buffer, sizeof(KEY) * ha_alter_info->key_count, &alter_index_drop_buffer, sizeof(KEY) * ha_alter_info->index_drop_count, &alter_index_add_buffer, sizeof(uint) * ha_alter_info->index_add_count, &wrap_altered_table, sizeof(TABLE), &wrap_altered_table_key_info, sizeof(KEY) * altered_table->s->keys, &wrap_altered_table_share, sizeof(TABLE_SHARE), &wrap_altered_table_share_key_info, sizeof(KEY) * altered_table->s->keys, NullS)) ) { DBUG_RETURN(HA_ALTER_ERROR); } *wrap_altered_table= *altered_table; *wrap_altered_table_share= *altered_table->s; mrn_init_sql_alloc(ha_thd(), &(wrap_altered_table_share->mem_root)); n_keys = ha_alter_info->index_drop_count; for (i = 0; i < n_keys; ++i) { const KEY *key = ha_alter_info->index_drop_buffer[i]; if (key->flags & HA_FULLTEXT || mrn_is_geo_key(key)) { result_mroonga = HA_ALTER_INPLACE_EXCLUSIVE_LOCK; } else { memcpy(&alter_index_drop_buffer[alter_index_drop_count], ha_alter_info->index_drop_buffer[i], sizeof(KEY)); ++alter_index_drop_count; } } if (!alter_index_drop_count) { alter_handler_flags &= ~ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX; } n_keys = ha_alter_info->index_add_count; for (i = 0; i < n_keys; ++i) { const KEY *key = &altered_table->key_info[ha_alter_info->index_add_buffer[i]]; if (key->flags & HA_FULLTEXT || mrn_is_geo_key(key)) { result_mroonga = HA_ALTER_INPLACE_EXCLUSIVE_LOCK; } else { alter_index_add_buffer[alter_index_add_count] = ha_alter_info->index_add_buffer[i]; ++alter_index_add_count; } } if (!alter_index_add_count) { alter_handler_flags &= ~ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX; } uint add_index_pos = 0; n_keys = ha_alter_info->key_count; for (i = 0; i < n_keys; ++i) { const KEY *key = &altered_table->key_info[i]; if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) { memcpy(&alter_key_info_buffer[alter_key_count], &ha_alter_info->key_info_buffer[i], sizeof(KEY)); memcpy(&wrap_altered_table_key_info[alter_key_count], &altered_table->key_info[i], sizeof(KEY)); memcpy(&wrap_altered_table_share_key_info[alter_key_count], &altered_table->s->key_info[i], sizeof(KEY)); if (add_index_pos < alter_index_add_count && alter_index_add_buffer[add_index_pos] == i) { alter_index_add_buffer[add_index_pos] = alter_key_count; ++add_index_pos; } ++alter_key_count; } } wrap_altered_table->key_info = wrap_altered_table_key_info; wrap_altered_table_share->key_info = wrap_altered_table_share_key_info; wrap_altered_table_share->keys = alter_key_count; wrap_altered_table->s = wrap_altered_table_share; if (!alter_handler_flags) { DBUG_RETURN(result_mroonga); } enum_alter_inplace_result result; MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); result = wrap_handler->check_if_supported_inplace_alter(wrap_altered_table, ha_alter_info); MRN_SET_BASE_ALTER_KEY(this, ha_alter_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); if (result_mroonga > result) DBUG_RETURN(result); DBUG_RETURN(result_mroonga); } enum_alter_inplace_result ha_mroonga::storage_check_if_supported_inplace_alter( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); alter_table_operations explicitly_unsupported_flags = ALTER_ADD_FOREIGN_KEY | ALTER_DROP_FOREIGN_KEY; alter_table_operations supported_flags = ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX | ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX | ALTER_ADD_UNIQUE_INDEX | ALTER_DROP_UNIQUE_INDEX | MRN_ALTER_INPLACE_INFO_ADD_VIRTUAL_COLUMN | MRN_ALTER_INPLACE_INFO_ADD_STORED_BASE_COLUMN | ALTER_DROP_COLUMN | ALTER_INDEX_ORDER | ALTER_INDEX_IGNORABILITY | ALTER_COLUMN_NAME; ; if (ha_alter_info->handler_flags & explicitly_unsupported_flags) { DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } else if ((ha_alter_info->handler_flags & supported_flags) == ha_alter_info->handler_flags) { DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK); } else { DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } } enum_alter_inplace_result ha_mroonga::check_if_supported_inplace_alter( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); enum_alter_inplace_result result; if (share->wrapper_mode) { result = wrapper_check_if_supported_inplace_alter(altered_table, ha_alter_info); } else { result = storage_check_if_supported_inplace_alter(altered_table, ha_alter_info); } DBUG_RETURN(result); } bool ha_mroonga::wrapper_prepare_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { bool result; MRN_DBUG_ENTER_METHOD(); if (!alter_handler_flags) { DBUG_RETURN(false); } #ifdef MRN_SUPPORT_CUSTOM_OPTIONS int error = 0; MRN_SHARE *tmp_share; tmp_share = mrn_get_share(altered_table->s->table_name.str, altered_table, &error); if (error != 0) { DBUG_RETURN(true); } if (parse_engine_table_options(ha_thd(), tmp_share->hton, wrap_altered_table->s)) { mrn_free_share(tmp_share); DBUG_RETURN(true); } #endif MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); result = wrap_handler->ha_prepare_inplace_alter_table(wrap_altered_table, ha_alter_info); MRN_SET_BASE_ALTER_KEY(this, ha_alter_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); #ifdef MRN_SUPPORT_CUSTOM_OPTIONS mrn_free_share(tmp_share); #endif DBUG_RETURN(result); } bool ha_mroonga::storage_prepare_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(false); } bool ha_mroonga::prepare_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool result; if (share->wrapper_mode) { result = wrapper_prepare_inplace_alter_table(altered_table, ha_alter_info); } else { result = storage_prepare_inplace_alter_table(altered_table, ha_alter_info); } DBUG_RETURN(result); } bool ha_mroonga::wrapper_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { int error; bool result = false; uint n_keys; uint i, j = 0; KEY *key_info = table_share->key_info; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(true); DBUG_PRINT("info", ("mroonga: table_name=%s", share->table_name)); mrn::PathMapper mapper(share->table_name); n_keys = ha_alter_info->index_drop_count; for (i = 0; i < n_keys; ++i) { const KEY *key = ha_alter_info->index_drop_buffer[i]; if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) { continue; } while (strcmp(key_info[j].name.str, key->name.str)) { ++j; } DBUG_PRINT("info", ("mroonga: key_name=%s", key->name.str)); error = drop_index(share, j); if (error) DBUG_RETURN(true); grn_index_tables[j] = NULL; grn_index_columns[j] = NULL; } MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, ha_alter_info->key_count); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, ha_alter_info->key_count); MRN_SHARE *tmp_share; TABLE_SHARE tmp_table_share; char **key_tokenizer; uint *key_tokenizer_length; KEY *p_key_info = &table->key_info[table_share->primary_key]; bool need_fill_index = false; memset(index_tables, 0, sizeof(grn_obj *) * ha_alter_info->key_count); memset(index_columns, 0, sizeof(grn_obj *) * ha_alter_info->key_count); tmp_table_share.keys = ha_alter_info->key_count; tmp_table_share.fields = 0; if (!(tmp_share = (MRN_SHARE *) mrn_my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &tmp_share, sizeof(*tmp_share), &key_tokenizer, sizeof(char *) * (tmp_table_share.keys), &key_tokenizer_length, sizeof(uint) * (tmp_table_share.keys), NullS)) ) { MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(true); } tmp_share->engine = NULL; tmp_share->table_share = &tmp_table_share; tmp_share->index_table = NULL; tmp_share->index_table_length = NULL; tmp_share->key_tokenizer = key_tokenizer; tmp_share->key_tokenizer_length = key_tokenizer_length; bitmap_clear_all(table->read_set); mrn_set_bitmap_by_key(table->read_set, p_key_info); n_keys = ha_alter_info->index_add_count; for (i = 0; i < n_keys; ++i) { uint key_pos = ha_alter_info->index_add_buffer[i]; KEY *key = &altered_table->key_info[key_pos]; if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) { continue; } if (share->disable_keys) { continue; } if ((error = mrn_add_index_param(tmp_share, key, key_pos))) { break; } DBUG_PRINT("info", ("mroonga: add key pos=%u", key_pos)); if ( (key->flags & HA_FULLTEXT) && (error = wrapper_create_index_fulltext(mapper.table_name(), key_pos, key, index_tables, NULL, tmp_share)) ) { break; } else if ( mrn_is_geo_key(key) && (error = wrapper_create_index_geo(mapper.table_name(), key_pos, key, index_tables, NULL, tmp_share)) ) { break; } mrn_set_bitmap_by_key(table->read_set, key); index_columns[key_pos] = grn_obj_column(ctx, index_tables[key_pos], INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME)); need_fill_index = true; } if (!error && need_fill_index) { mrn::FieldTableChanger changer(altered_table, table); error = wrapper_fill_indexes(ha_thd(), altered_table->key_info, index_columns, ha_alter_info->key_count); } bitmap_set_all(table->read_set); if (!error && alter_handler_flags) { #ifdef MRN_SUPPORT_CUSTOM_OPTIONS { MRN_SHARE *alter_tmp_share; alter_tmp_share = mrn_get_share(altered_table->s->table_name.str, altered_table, &error); if (alter_tmp_share) { if (parse_engine_table_options(ha_thd(), alter_tmp_share->hton, wrap_altered_table->s)) { error = MRN_GET_ERROR_NUMBER; } mrn_free_share(alter_tmp_share); } } #endif if (!error) { MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); result = wrap_handler->ha_inplace_alter_table(wrap_altered_table, ha_alter_info); MRN_SET_BASE_ALTER_KEY(this, ha_alter_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } } if (result || error) { n_keys = ha_alter_info->index_add_count; for (i = 0; i < n_keys; ++i) { uint key_pos = ha_alter_info->index_add_buffer[i]; KEY *key = &altered_table->key_info[key_pos]; if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) { continue; } if (share->disable_keys) { continue; } if (index_tables[key_pos]) { grn_obj_remove(ctx, index_tables[key_pos]); } } result = true; } mrn_free_share_alloc(tmp_share); my_free(tmp_share); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(result); } bool ha_mroonga::storage_inplace_alter_table_add_index( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, ha_alter_info->key_count); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, ha_alter_info->key_count); MRN_SHARE *tmp_share; TABLE_SHARE tmp_table_share; char **index_table, **key_tokenizer, **col_flags, **col_type; uint *index_table_length, *key_tokenizer_length, *col_flags_length, *col_type_length; bool have_multiple_column_index = false; memset(index_tables, 0, sizeof(grn_obj *) * ha_alter_info->key_count); memset(index_columns, 0, sizeof(grn_obj *) * ha_alter_info->key_count); tmp_table_share.keys = ha_alter_info->key_count; tmp_table_share.fields = 0; if (!(tmp_share = (MRN_SHARE *) mrn_my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &tmp_share, sizeof(*tmp_share), &index_table, sizeof(char *) * tmp_table_share.keys, &index_table_length, sizeof(uint) * tmp_table_share.keys, &key_tokenizer, sizeof(char *) * tmp_table_share.keys, &key_tokenizer_length, sizeof(uint) * tmp_table_share.keys, &col_flags, sizeof(char *) * tmp_table_share.fields, &col_flags_length, sizeof(uint) * tmp_table_share.fields, &col_type, sizeof(char *) * tmp_table_share.fields, &col_type_length, sizeof(uint) * tmp_table_share.fields, NullS)) ) { MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(true); } tmp_share->engine = NULL; tmp_share->table_share = &tmp_table_share; tmp_share->index_table = index_table; tmp_share->index_table_length = index_table_length; tmp_share->key_tokenizer = key_tokenizer; tmp_share->key_tokenizer_length = key_tokenizer_length; tmp_share->col_flags = col_flags; tmp_share->col_flags_length = col_flags_length; tmp_share->col_type = col_type; tmp_share->col_type_length = col_type_length; bitmap_clear_all(table->read_set); if (table_share->primary_key != MAX_KEY) { KEY *p_key_info = &table->key_info[table_share->primary_key]; mrn_set_bitmap_by_key(table->read_set, p_key_info); } int error = 0; uint n_keys = ha_alter_info->index_add_count; for (uint i = 0; i < n_keys; ++i) { uint key_pos = ha_alter_info->index_add_buffer[i]; KEY *key = &altered_table->key_info[key_pos]; if (share->disable_keys && !(key->flags & HA_NOSAME)) { continue; // key is disabled } if ((error = mrn_add_index_param(tmp_share, key, key_pos))) { break; } DBUG_PRINT("info", ("mroonga: add key pos=%u", key_pos)); mrn::PathMapper mapper(share->table_name); if ((error = storage_create_index(table, mapper.table_name(), grn_table, tmp_share, key, index_tables, index_columns, key_pos))) { break; } if ( KEY_N_KEY_PARTS(key) == 1 && (key->flags & HA_NOSAME) && grn_table_size(ctx, grn_table) != grn_table_size(ctx, index_tables[key_pos]) ) { error = HA_ERR_FOUND_DUPP_UNIQUE; my_printf_error(ER_DUP_UNIQUE, ER(ER_DUP_UNIQUE), MYF(0), table_share->table_name); ++i; break; } if ( KEY_N_KEY_PARTS(key) != 1 && !(key->flags & HA_FULLTEXT) ) { mrn_set_bitmap_by_key(table->read_set, key); have_multiple_column_index = true; } } if (!error && have_multiple_column_index) { mrn::FieldTableChanger changer(altered_table, table); error = storage_add_index_multiple_columns(altered_table->key_info, ha_alter_info->key_count, index_tables, index_columns, false); if (error == HA_ERR_FOUND_DUPP_UNIQUE) { my_printf_error(ER_DUP_UNIQUE, ER(ER_DUP_UNIQUE), MYF(0), table_share->table_name); } else if (error) { my_message(error, "failed to create multiple column index", MYF(0)); } } bitmap_set_all(table->read_set); bool have_error = false; if (error) { n_keys = ha_alter_info->index_add_count; for (uint i = 0; i < n_keys; ++i) { uint key_pos = ha_alter_info->index_add_buffer[i]; KEY *key = &altered_table->key_info[key_pos]; if (share->disable_keys && !(key->flags & HA_NOSAME)) { continue; } if (index_tables[key_pos]) { grn_obj_remove(ctx, index_columns[key_pos]); grn_obj_remove(ctx, index_tables[key_pos]); } } have_error = true; } mrn_free_share_alloc(tmp_share); my_free(tmp_share); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(have_error); } bool ha_mroonga::storage_inplace_alter_table_drop_index( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool have_error = false; uint n_keys; uint i, j = 0; KEY *key_info = table_share->key_info; mrn::PathMapper mapper(share->table_name); n_keys = ha_alter_info->index_drop_count; for (i = 0; i < n_keys; ++i) { KEY *key = ha_alter_info->index_drop_buffer[i]; while (strcmp(key_info[j].name.str, key->name.str) != 0) { ++j; } int error = drop_index(share, j); if (error != 0) DBUG_RETURN(true); grn_index_tables[j] = NULL; grn_index_columns[j] = NULL; } DBUG_RETURN(have_error); } bool ha_mroonga::storage_inplace_alter_table_add_column( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool have_error = false; MRN_SHARE *tmp_share; TABLE_SHARE tmp_table_share; char **index_table, **key_tokenizer, **col_flags, **col_type; uint *index_table_length, *key_tokenizer_length, *col_flags_length, *col_type_length; tmp_table_share.keys = 0; tmp_table_share.fields = altered_table->s->fields; tmp_share = (MRN_SHARE *)mrn_my_multi_malloc( MYF(MY_WME | MY_ZEROFILL), &tmp_share, sizeof(*tmp_share), &index_table, sizeof(char *) * tmp_table_share.keys, &index_table_length, sizeof(uint) * tmp_table_share.keys, &key_tokenizer, sizeof(char *) * tmp_table_share.keys, &key_tokenizer_length, sizeof(uint) * tmp_table_share.keys, &col_flags, sizeof(char *) * tmp_table_share.fields, &col_flags_length, sizeof(uint) * tmp_table_share.fields, &col_type, sizeof(char *) * tmp_table_share.fields, &col_type_length, sizeof(uint) * tmp_table_share.fields, NullS); if (!tmp_share) { have_error = true; DBUG_RETURN(have_error); } tmp_share->engine = NULL; tmp_share->table_share = &tmp_table_share; tmp_share->index_table = index_table; tmp_share->index_table_length = index_table_length; tmp_share->key_tokenizer = key_tokenizer; tmp_share->key_tokenizer_length = key_tokenizer_length; tmp_share->col_flags = col_flags; tmp_share->col_flags_length = col_flags_length; tmp_share->col_type = col_type; tmp_share->col_type_length = col_type_length; mrn::PathMapper mapper(share->table_name); grn_obj *table_obj; table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name())); Alter_info *alter_info = ha_alter_info->alter_info; List_iterator_fast create_fields(alter_info->create_list); for (uint i = 0; Create_field *create_field = create_fields++; i++) { if (create_field->field) { continue; } Field *field = altered_table->s->field[i]; #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_VIRTUAL(field)) { continue; } #endif mrn::ColumnName column_name(field->field_name); int error = mrn_add_column_param(tmp_share, field, i); if (error) { have_error = true; break; } grn_obj_flags col_flags = GRN_OBJ_PERSISTENT; if (!find_column_flags(field, tmp_share, i, &col_flags)) { col_flags |= GRN_OBJ_COLUMN_SCALAR; } grn_obj *col_type; { int column_type_error_code = ER_WRONG_FIELD_SPEC; col_type = find_column_type(field, tmp_share, i, column_type_error_code); if (!col_type) { error = column_type_error_code; have_error = true; break; } } char *col_path = NULL; // we don't specify path grn_obj *column_obj = grn_column_create(ctx, table_obj, column_name.c_str(), column_name.length(), col_path, col_flags, col_type); if (ctx->rc) { error = ER_WRONG_COLUMN_NAME; my_message(error, ctx->errbuf, MYF(0)); have_error = true; break; } #ifdef MRN_SUPPORT_GENERATED_COLUMNS if (MRN_GENERATED_COLUMNS_FIELD_IS_STORED(field)) { # ifndef MRN_MARIADB_P MY_BITMAP generated_column_bitmap; if (bitmap_init(&generated_column_bitmap, NULL, altered_table->s->fields, false)) { error = HA_ERR_OUT_OF_MEM; my_message(ER_OUTOFMEMORY, "mroonga: storage: " "failed to allocate memory for getting generated value", MYF(0)); have_error = true; grn_obj_remove(ctx, column_obj); break; } mrn::SmartBitmap smart_generated_column_bitmap(&generated_column_bitmap); bitmap_set_bit(&generated_column_bitmap, field->field_index); # endif mrn::FieldTableChanger changer(altered_table, table); error = storage_rnd_init(true); if (error) { have_error = true; grn_obj_remove(ctx, column_obj); break; } Field *altered_field = altered_table->field[i]; grn_obj new_value; GRN_VOID_INIT(&new_value); mrn::SmartGrnObj smart_new_value(ctx, &new_value); while (!have_error) { int next_error = storage_rnd_next(table->record[0]); if (next_error == HA_ERR_END_OF_FILE) { break; } else if (next_error != 0) { error = next_error; have_error = true; grn_obj_remove(ctx, column_obj); break; } # ifdef MRN_MARIADB_P MRN_GENERATED_COLUMNS_UPDATE_VIRTUAL_FIELD(altered_table, altered_field); # else if (update_generated_write_fields(&generated_column_bitmap, altered_table)) { error = ER_ERROR_ON_WRITE; my_message(error, "mroonga: storage: " "failed to update generated value for updating column", MYF(0)); have_error = true; grn_obj_remove(ctx, column_obj); break; } # endif error = mrn_change_encoding(ctx, altered_field->charset()); if (error) { my_message(error, "mroonga: storage: " "failed to change encoding to store generated value", MYF(0)); have_error = true; grn_obj_remove(ctx, column_obj); break; } error = generic_store_bulk(altered_field, &new_value); if (error) { my_message(error, "mroonga: storage: " "failed to get generated value for updating column", MYF(0)); have_error = true; grn_obj_remove(ctx, column_obj); break; } grn_obj_set_value(ctx, column_obj, record_id, &new_value, GRN_OBJ_SET); if (ctx->rc) { error = ER_ERROR_ON_WRITE; my_message(error, ctx->errbuf, MYF(0)); break; } } int end_error = storage_rnd_end(); if (end_error != 0 && error == 0) { error = end_error; grn_obj_remove(ctx, column_obj); break; } } #endif } grn_obj_unlink(ctx, table_obj); mrn_free_share_alloc(tmp_share); my_free(tmp_share); DBUG_RETURN(have_error); } bool ha_mroonga::storage_inplace_alter_table_drop_column( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool have_error = false; mrn::PathMapper mapper(share->table_name); grn_obj *table_obj; table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name())); Alter_info *alter_info = ha_alter_info->alter_info; uint n_fields = table->s->fields; for (uint i = 0; i < n_fields; i++) { Field *field = table->field[i]; bool dropped = true; List_iterator_fast create_fields(alter_info->create_list); while (Create_field *create_field = create_fields++) { if (create_field->field == field) { dropped = false; break; } } if (!dropped) { continue; } const char *column_name = field->field_name.str; int column_name_size = field->field_name.length; grn_obj *column_obj; column_obj = grn_obj_column(ctx, table_obj, column_name, column_name_size); if (column_obj) { grn_obj_remove(ctx, column_obj); } if (ctx->rc) { int error = ER_WRONG_COLUMN_NAME; my_message(error, ctx->errbuf, MYF(0)); have_error = true; break; } } grn_obj_unlink(ctx, table_obj); DBUG_RETURN(have_error); } bool ha_mroonga::storage_inplace_alter_table_rename_column( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool have_error = false; mrn::PathMapper mapper(share->table_name); grn_obj *table_obj; table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name())); Alter_info *alter_info = ha_alter_info->alter_info; uint n_fields = table->s->fields; for (uint i = 0; i < n_fields; i++) { Field *field = table->field[i]; if (!(field->flags & FIELD_IS_RENAMED)) { continue; } LEX_CSTRING new_name; new_name.str= 0; List_iterator_fast create_fields(alter_info->create_list); while (Create_field *create_field = create_fields++) { if (create_field->field == field) { new_name = create_field->field_name; break; } } if (!new_name.str) { continue; } const char *old_name = field->field_name.str; grn_obj *column_obj; column_obj = grn_obj_column(ctx, table_obj, old_name, field->field_name.length); if (column_obj) { grn_column_rename(ctx, column_obj, new_name.str, new_name.length); if (ctx->rc) { int error = ER_WRONG_COLUMN_NAME; my_message(error, ctx->errbuf, MYF(0)); have_error = true; } grn_obj_unlink(ctx, column_obj); } if (have_error) { break; } } grn_obj_unlink(ctx, table_obj); DBUG_RETURN(have_error); } bool ha_mroonga::storage_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool have_error = false; int error = mrn_change_encoding(ctx, system_charset_info); if (error) { have_error = true; } alter_table_operations drop_index_related_flags = ALTER_DROP_INDEX | ALTER_DROP_NON_UNIQUE_NON_PRIM_INDEX | ALTER_DROP_UNIQUE_INDEX | ALTER_DROP_PK_INDEX; if (!have_error && (ha_alter_info->handler_flags & drop_index_related_flags)) { have_error = storage_inplace_alter_table_drop_index(altered_table, ha_alter_info); } alter_table_operations add_column_related_flags = ALTER_ADD_COLUMN; if (!have_error && (ha_alter_info->handler_flags & add_column_related_flags)) { have_error = storage_inplace_alter_table_add_column(altered_table, ha_alter_info); } alter_table_operations drop_column_related_flags = ALTER_DROP_COLUMN; if (!have_error && (ha_alter_info->handler_flags & drop_column_related_flags)) { have_error = storage_inplace_alter_table_drop_column(altered_table, ha_alter_info); } alter_table_operations rename_column_related_flags = ALTER_COLUMN_NAME; if (!have_error && (ha_alter_info->handler_flags & rename_column_related_flags)) { have_error = storage_inplace_alter_table_rename_column(altered_table, ha_alter_info); } alter_table_operations add_index_related_flags = ALTER_ADD_INDEX | ALTER_ADD_NON_UNIQUE_NON_PRIM_INDEX | ALTER_ADD_UNIQUE_INDEX | ALTER_ADD_PK_INDEX; if (!have_error && (ha_alter_info->handler_flags & add_index_related_flags)) { have_error = storage_inplace_alter_table_add_index(altered_table, ha_alter_info); } DBUG_RETURN(have_error); } bool ha_mroonga::inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info) { MRN_DBUG_ENTER_METHOD(); bool result; if (share->wrapper_mode) { result = wrapper_inplace_alter_table(altered_table, ha_alter_info); } else { result = storage_inplace_alter_table(altered_table, ha_alter_info); } DBUG_RETURN(result); } bool ha_mroonga::wrapper_commit_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) { bool result; MRN_DBUG_ENTER_METHOD(); if (!alter_handler_flags) { free_root(&(wrap_altered_table_share->mem_root), MYF(0)); my_free(alter_key_info_buffer); alter_key_info_buffer = NULL; DBUG_RETURN(false); } MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); result = wrap_handler->ha_commit_inplace_alter_table(wrap_altered_table, ha_alter_info, commit); MRN_SET_BASE_ALTER_KEY(this, ha_alter_info); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); free_root(&(wrap_altered_table_share->mem_root), MYF(0)); my_free(alter_key_info_buffer); alter_key_info_buffer = NULL; DBUG_RETURN(result); } bool ha_mroonga::storage_commit_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(false); } bool ha_mroonga::commit_inplace_alter_table( TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) { MRN_DBUG_ENTER_METHOD(); bool result; if (share->wrapper_mode) { result = wrapper_commit_inplace_alter_table(altered_table, ha_alter_info, commit); } else { result = storage_commit_inplace_alter_table(altered_table, ha_alter_info, commit); } DBUG_RETURN(result); } #else alter_table_operations ha_mroonga::wrapper_alter_table_flags(alter_table_operations flags) { alter_table_operations res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->alter_table_flags(flags); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } alter_table_operations ha_mroonga::storage_alter_table_flags(alter_table_operations flags) { MRN_DBUG_ENTER_METHOD(); alter_table_operations res = handler::alter_table_flags(flags); DBUG_RETURN(res); } alter_table_operations ha_mroonga::alter_table_flags(alter_table_operations flags) { MRN_DBUG_ENTER_METHOD(); alter_table_operations res; if (share->wrapper_mode) { res = wrapper_alter_table_flags(flags); } else { res = storage_alter_table_flags(flags); } DBUG_RETURN(res); } #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX int ha_mroonga::wrapper_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, handler_add_index **add) #else int ha_mroonga::wrapper_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) #endif { int error = 0; uint i, j, k; uint n_keys = table->s->keys; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, num_of_keys + n_keys); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, num_of_keys + n_keys); THD *thd = ha_thd(); MRN_SHARE *tmp_share; TABLE_SHARE tmp_table_share; char **key_tokenizer; uint *key_tokenizer_length; MRN_DBUG_ENTER_METHOD(); if (!(wrap_alter_key_info = (KEY *) mrn_my_malloc(sizeof(KEY) * num_of_keys, MYF(MY_WME)))) { MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(HA_ERR_OUT_OF_MEM); } KEY *p_key_info = &table->key_info[table_share->primary_key], *tmp_key_info; tmp_table_share.keys = n_keys + num_of_keys; tmp_table_share.fields = 0; if (!(tmp_share = (MRN_SHARE *) mrn_my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &tmp_share, sizeof(*tmp_share), &key_tokenizer, sizeof(char *) * (n_keys + num_of_keys), &key_tokenizer_length, sizeof(uint) * (n_keys + num_of_keys), NullS)) ) { MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(HA_ERR_OUT_OF_MEM); } tmp_share->engine = NULL; tmp_share->table_share = &tmp_table_share; tmp_share->index_table = NULL; tmp_share->index_table_length = NULL; tmp_share->key_tokenizer = key_tokenizer; tmp_share->key_tokenizer_length = key_tokenizer_length; tmp_share->col_flags = NULL; tmp_share->col_type = NULL; #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX hnd_add_index = NULL; #endif bitmap_clear_all(table->read_set); mrn_set_bitmap_by_key(table->read_set, p_key_info); mrn::PathMapper mapper(share->table_name); for (i = 0, j = 0; i < num_of_keys; i++) { if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) { wrap_alter_key_info[j] = key_info[i]; j++; continue; } if (share->disable_keys) { continue; } if ((error = mrn_add_index_param(tmp_share, &key_info[i], i + n_keys))) { break; } index_tables[i + n_keys] = NULL; if ( (key_info[i].flags & HA_FULLTEXT) && (error = wrapper_create_index_fulltext(mapper.table_name(), i + n_keys, &key_info[i], index_tables, NULL, tmp_share)) ) { break; } else if ( mrn_is_geo_key(&key_info[i]) && (error = wrapper_create_index_geo(mapper.table_name(), i + n_keys, &key_info[i], index_tables, NULL, tmp_share)) ) { break; } mrn_set_bitmap_by_key(table->read_set, &key_info[i]); } if (!error && i > j && !share->disable_keys) { for (k = 0; k < num_of_keys; k++) { tmp_key_info = &key_info[k]; if (!(tmp_key_info->flags & HA_FULLTEXT) && !mrn_is_geo_key(tmp_key_info)) { continue; } index_columns[k + n_keys] = grn_obj_column(ctx, index_tables[k + n_keys], INDEX_COLUMN_NAME, strlen(INDEX_COLUMN_NAME)); } error = wrapper_fill_indexes(thd, key_info, &index_columns[n_keys], num_of_keys); } bitmap_set_all(table->read_set); if (!error && j) { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX error = wrap_handler->add_index(table_arg, wrap_alter_key_info, j, &hnd_add_index); #else error = wrap_handler->add_index(table_arg, wrap_alter_key_info, j); #endif MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } if (error) { for (k = 0; k < i; k++) { if (!(key_info[k].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[k])) { continue; } if (index_tables[k + n_keys]) { grn_obj_remove(ctx, index_tables[k + n_keys]); } } } #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX else { *add = new handler_add_index(table_arg, key_info, num_of_keys); } #endif mrn_free_share_alloc(tmp_share); my_free(tmp_share); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(error); } #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX int ha_mroonga::storage_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, handler_add_index **add) #else int ha_mroonga::storage_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) #endif { int error = 0; uint i; uint n_keys = table->s->keys; MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, num_of_keys + n_keys); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, num_of_keys + n_keys); MRN_SHARE *tmp_share; TABLE_SHARE tmp_table_share; char **index_table, **key_tokenizer, **col_flags, **col_type; uint *index_table_length, *key_tokenizer_length, *col_flags_length, *col_type_length; bool have_multiple_column_index = false; MRN_DBUG_ENTER_METHOD(); tmp_table_share.keys = n_keys + num_of_keys; tmp_table_share.fields = 0; if (!(tmp_share = (MRN_SHARE *) mrn_my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &tmp_share, sizeof(*tmp_share), &index_table, sizeof(char*) * tmp_table_share.keys, &index_table_length, sizeof(uint) * tmp_table_share.keys, &key_tokenizer, sizeof(char *) * tmp_table_share.keys, &key_tokenizer_length, sizeof(uint) * tmp_table_share.keys, &col_flags, sizeof(char *) * tmp_table_share.fields, &col_flags_length, sizeof(uint) * tmp_table_share.fields, &col_type, sizeof(char *) * tmp_table_share.fields, &col_type_length, sizeof(uint) * tmp_table_share.fields, NullS)) ) { MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(HA_ERR_OUT_OF_MEM); } tmp_share->engine = NULL; tmp_share->table_share = &tmp_table_share; tmp_share->index_table = index_table; tmp_share->index_table_length = index_table_length; tmp_share->key_tokenizer = key_tokenizer; tmp_share->key_tokenizer_length = key_tokenizer_length; tmp_share->col_flags = col_flags; tmp_share->col_flags_length = col_flags_length; tmp_share->col_type = col_type; tmp_share->col_type_length = col_type_length; bitmap_clear_all(table->read_set); mrn::PathMapper mapper(share->table_name); for (i = 0; i < num_of_keys; i++) { if (share->disable_keys && !(key_info[i].flags & HA_NOSAME)) { continue; // key is disabled } index_tables[i + n_keys] = NULL; index_columns[i + n_keys] = NULL; if ((error = mrn_add_index_param(tmp_share, &key_info[i], i + n_keys))) { break; } if ((error = storage_create_index(table, mapper.table_name(), grn_table, tmp_share, &key_info[i], index_tables, index_columns, i + n_keys))) { break; } if ( KEY_N_KEY_PARTS(&(key_info[i])) == 1 && (key_info[i].flags & HA_NOSAME) && grn_table_size(ctx, grn_table) != grn_table_size(ctx, index_tables[i + n_keys]) ) { error = HA_ERR_FOUND_DUPP_UNIQUE; i++; break; } if ( KEY_N_KEY_PARTS(&(key_info[i])) != 1 && !(key_info[i].flags & HA_FULLTEXT) ) { mrn_set_bitmap_by_key(table->read_set, &key_info[i]); have_multiple_column_index = true; } } if (!error && have_multiple_column_index) { error = storage_add_index_multiple_columns(key_info, num_of_keys, index_tables + n_keys, index_columns + n_keys, false); } bitmap_set_all(table->read_set); if (error) { for (uint j = 0; j < i; j++) { if (index_tables[j + n_keys]) { grn_obj_remove(ctx, index_columns[j + n_keys]); grn_obj_remove(ctx, index_tables[j + n_keys]); } } } #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX else { *add = new handler_add_index(table_arg, key_info, num_of_keys); } #endif mrn_free_share_alloc(tmp_share); my_free(tmp_share); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); DBUG_RETURN(error); } #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX int ha_mroonga::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, handler_add_index **add) { MRN_DBUG_ENTER_METHOD(); int error; if (share->wrapper_mode) { error = wrapper_add_index(table_arg, key_info, num_of_keys, add); } else { error = storage_add_index(table_arg, key_info, num_of_keys, add); } DBUG_RETURN(error); } #else int ha_mroonga::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) { MRN_DBUG_ENTER_METHOD(); int error; if (share->wrapper_mode) { error = wrapper_add_index(table_arg, key_info, num_of_keys); } else { error = storage_add_index(table_arg, key_info, num_of_keys); } DBUG_RETURN(error); } #endif #ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX int ha_mroonga::wrapper_final_add_index(handler_add_index *add, bool commit) { int error = 0; MRN_DBUG_ENTER_METHOD(); if (hnd_add_index) { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); error = wrap_handler->final_add_index(hnd_add_index, commit); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } if (add) { delete add; } DBUG_RETURN(error); } int ha_mroonga::storage_final_add_index(handler_add_index *add, bool commit) { MRN_DBUG_ENTER_METHOD(); if (add) { delete add; } DBUG_RETURN(0); } int ha_mroonga::final_add_index(handler_add_index *add, bool commit) { MRN_DBUG_ENTER_METHOD(); int error; if (share->wrapper_mode) { error = wrapper_final_add_index(add, commit); } else { error = storage_final_add_index(add, commit); } DBUG_RETURN(error); } #endif int ha_mroonga::wrapper_prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys) { int res = 0; uint i, j; KEY *key_info = table_share->key_info; MRN_DBUG_ENTER_METHOD(); res = mrn_change_encoding(ctx, system_charset_info); if (res) DBUG_RETURN(res); MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(uint, wrap_key_num, num_of_keys); for (i = 0, j = 0; i < num_of_keys; i++) { uint key_index = key_num[i]; if (!(key_info[key_index].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[key_index])) { wrap_key_num[j] = share->wrap_key_nr[key_index]; j++; continue; } res = drop_index(share, key_index); if (res) DBUG_RETURN(res); grn_index_tables[key_index] = NULL; grn_index_columns[key_index] = NULL; } if (j) { MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->prepare_drop_index(table_arg, wrap_key_num, j); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); } MRN_FREE_VARIABLE_LENGTH_ARRAYS(wrap_key_num); DBUG_RETURN(res); } int ha_mroonga::storage_prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys) { int error; uint i; MRN_DBUG_ENTER_METHOD(); error = mrn_change_encoding(ctx, system_charset_info); if (error) DBUG_RETURN(error); for (i = 0; i < num_of_keys; i++) { uint key_index = key_num[i]; error = drop_index(share, key_index); if (error) break; grn_index_tables[key_index] = NULL; grn_index_columns[key_index] = NULL; } DBUG_RETURN(error); } int ha_mroonga::prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys) { MRN_DBUG_ENTER_METHOD(); int res; if (share->wrapper_mode) { res = wrapper_prepare_drop_index(table_arg, key_num, num_of_keys); } else { res = storage_prepare_drop_index(table_arg, key_num, num_of_keys); } DBUG_RETURN(res); } int ha_mroonga::wrapper_final_drop_index(TABLE *table_arg) { uint res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->final_drop_index(table_arg); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } int ha_mroonga::storage_final_drop_index(TABLE *table_arg) { MRN_DBUG_ENTER_METHOD(); DBUG_RETURN(0); } int ha_mroonga::final_drop_index(TABLE *table_arg) { MRN_DBUG_ENTER_METHOD(); uint res; if (share->wrapper_mode) { res = wrapper_final_drop_index(table_arg); } else { res = storage_final_drop_index(table_arg); } DBUG_RETURN(res); } #endif int ha_mroonga::wrapper_update_auto_increment() { int res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->update_auto_increment(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } int ha_mroonga::storage_update_auto_increment() { MRN_DBUG_ENTER_METHOD(); int res = handler::update_auto_increment(); DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", table->next_number_field->val_int())); DBUG_RETURN(res); } int ha_mroonga::update_auto_increment() { MRN_DBUG_ENTER_METHOD(); int res; if (share->wrapper_mode) { res = wrapper_update_auto_increment(); } else { res = storage_update_auto_increment(); } DBUG_RETURN(res); } void ha_mroonga::wrapper_set_next_insert_id(ulonglong id) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->set_next_insert_id(id); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_set_next_insert_id(ulonglong id) { MRN_DBUG_ENTER_METHOD(); handler::set_next_insert_id(id); DBUG_VOID_RETURN; } void ha_mroonga::set_next_insert_id(ulonglong id) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_set_next_insert_id(id); } else { storage_set_next_insert_id(id); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, ulonglong *nb_reserved_values) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->get_auto_increment(offset, increment, nb_desired_values, first_value, nb_reserved_values); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, ulonglong *nb_reserved_values) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; MRN_DBUG_ENTER_METHOD(); if (table->found_next_number_field && !table->s->next_number_keypart) { if (long_term_share->auto_inc_inited) { *first_value = long_term_share->auto_inc_value; DBUG_PRINT("info", ("mroonga: *first_value(auto_inc_value)=%llu", *first_value)); *nb_reserved_values = UINT_MAX64; } else { handler::get_auto_increment(offset, increment, nb_desired_values, first_value, nb_reserved_values); long_term_share->auto_inc_value = *first_value; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); long_term_share->auto_inc_inited = true; } } else { handler::get_auto_increment(offset, increment, nb_desired_values, first_value, nb_reserved_values); } DBUG_VOID_RETURN; } void ha_mroonga::get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values, ulonglong *first_value, ulonglong *nb_reserved_values) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_get_auto_increment(offset, increment, nb_desired_values, first_value, nb_reserved_values); } else { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; mrn::Lock lock(&long_term_share->auto_inc_mutex); storage_get_auto_increment(offset, increment, nb_desired_values, first_value, nb_reserved_values); long_term_share->auto_inc_value += nb_desired_values * increment; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_restore_auto_increment(ulonglong prev_insert_id) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->restore_auto_increment(prev_insert_id); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_restore_auto_increment(ulonglong prev_insert_id) { MRN_DBUG_ENTER_METHOD(); handler::restore_auto_increment(prev_insert_id); DBUG_VOID_RETURN; } void ha_mroonga::restore_auto_increment(ulonglong prev_insert_id) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_restore_auto_increment(prev_insert_id); } else { storage_restore_auto_increment(prev_insert_id); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_release_auto_increment() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->ha_release_auto_increment(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_release_auto_increment() { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } void ha_mroonga::release_auto_increment() { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_release_auto_increment(); } else { storage_release_auto_increment(); } DBUG_VOID_RETURN; } int ha_mroonga::wrapper_check_for_upgrade(HA_CHECK_OPT *check_opt) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); int error = wrap_handler->ha_check_for_upgrade(check_opt); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(error); } int ha_mroonga::storage_check_for_upgrade(HA_CHECK_OPT *check_opt) { MRN_DBUG_ENTER_METHOD(); for (uint i = 0; i < table->s->fields; ++i) { grn_obj *column = grn_columns[i]; if (!column) { continue; } Field *field = table->field[i]; grn_id column_range = grn_obj_get_range(ctx, column); switch (field->real_type()) { case MYSQL_TYPE_ENUM: if (column_range != GRN_DB_UINT16) { DBUG_RETURN(HA_ADMIN_NEEDS_ALTER); } break; case MYSQL_TYPE_SET: if (column_range != GRN_DB_UINT64) { DBUG_RETURN(HA_ADMIN_NEEDS_ALTER); } break; default: break; } } DBUG_RETURN(HA_ADMIN_OK); } int ha_mroonga::check_for_upgrade(HA_CHECK_OPT *check_opt) { MRN_DBUG_ENTER_METHOD(); int error; if (share->wrapper_mode) { error = wrapper_check_for_upgrade(check_opt); } else { error = storage_check_for_upgrade(check_opt); } DBUG_RETURN(error); } #ifdef MRN_HANDLER_HAVE_RESET_AUTO_INCREMENT int ha_mroonga::wrapper_reset_auto_increment(ulonglong value) { int res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->ha_reset_auto_increment(value); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } int ha_mroonga::storage_reset_auto_increment(ulonglong value) { MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share; MRN_DBUG_ENTER_METHOD(); mrn::Lock lock(&long_term_share->auto_inc_mutex); long_term_share->auto_inc_value = value; DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu", long_term_share->auto_inc_value)); long_term_share->auto_inc_inited = true; DBUG_RETURN(0); } int ha_mroonga::reset_auto_increment(ulonglong value) { MRN_DBUG_ENTER_METHOD(); int res; if (share->wrapper_mode) { res = wrapper_reset_auto_increment(value); } else { res = storage_reset_auto_increment(value); } DBUG_RETURN(res); } #endif void ha_mroonga::set_pk_bitmap() { MRN_DBUG_ENTER_METHOD(); KEY *key_info = &(table->key_info[table_share->primary_key]); uint j; for (j = 0; j < KEY_N_KEY_PARTS(key_info); j++) { Field *field = key_info->key_part[j].field; bitmap_set_bit(table->read_set, field->field_index); } DBUG_VOID_RETURN; } bool ha_mroonga::wrapper_was_semi_consistent_read() { bool res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->was_semi_consistent_read(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_was_semi_consistent_read() { bool res; MRN_DBUG_ENTER_METHOD(); res = handler::was_semi_consistent_read(); DBUG_RETURN(res); } bool ha_mroonga::was_semi_consistent_read() { bool res; MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { res = wrapper_was_semi_consistent_read(); } else { res = storage_was_semi_consistent_read(); } DBUG_RETURN(res); } void ha_mroonga::wrapper_try_semi_consistent_read(bool yes) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->try_semi_consistent_read(yes); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_try_semi_consistent_read(bool yes) { MRN_DBUG_ENTER_METHOD(); handler::try_semi_consistent_read(yes); DBUG_VOID_RETURN; } void ha_mroonga::try_semi_consistent_read(bool yes) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_try_semi_consistent_read(yes); } else { storage_try_semi_consistent_read(yes); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_unlock_row() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->unlock_row(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_unlock_row() { MRN_DBUG_ENTER_METHOD(); handler::unlock_row(); DBUG_VOID_RETURN; } void ha_mroonga::unlock_row() { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_unlock_row(); } else { storage_unlock_row(); } DBUG_VOID_RETURN; } int ha_mroonga::wrapper_start_stmt(THD *thd, thr_lock_type lock_type) { int res; MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->start_stmt(thd, lock_type); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } int ha_mroonga::storage_start_stmt(THD *thd, thr_lock_type lock_type) { int res; MRN_DBUG_ENTER_METHOD(); res = handler::start_stmt(thd, lock_type); DBUG_RETURN(res); } int ha_mroonga::start_stmt(THD *thd, thr_lock_type lock_type) { int res; MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { res = wrapper_start_stmt(thd, lock_type); } else { res = storage_start_stmt(thd, lock_type); } DBUG_RETURN(res); } void ha_mroonga::wrapper_change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->change_table_ptr(table_arg, share->wrap_table_share); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg) { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } void ha_mroonga::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg) { MRN_DBUG_ENTER_METHOD(); handler::change_table_ptr(table_arg, share_arg); if (share && share->wrapper_mode) { wrapper_change_table_ptr(table_arg, share_arg); } else { storage_change_table_ptr(table_arg, share_arg); } DBUG_VOID_RETURN; } bool ha_mroonga::wrapper_is_fk_defined_on_table_or_index(uint index) { MRN_DBUG_ENTER_METHOD(); bool res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->is_fk_defined_on_table_or_index(index); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_is_fk_defined_on_table_or_index(uint index) { MRN_DBUG_ENTER_METHOD(); bool res = handler::is_fk_defined_on_table_or_index(index); DBUG_RETURN(res); } bool ha_mroonga::is_fk_defined_on_table_or_index(uint index) { MRN_DBUG_ENTER_METHOD(); bool res; if (share->wrapper_mode) { res = wrapper_is_fk_defined_on_table_or_index(index); } else { res = storage_is_fk_defined_on_table_or_index(index); } DBUG_RETURN(res); } char *ha_mroonga::wrapper_get_foreign_key_create_info() { MRN_DBUG_ENTER_METHOD(); char *res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->get_foreign_key_create_info(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } #ifdef MRN_SUPPORT_FOREIGN_KEYS char *ha_mroonga::storage_get_foreign_key_create_info() { int error; uint i; grn_obj *column; uint n_columns = table_share->fields; char create_info_buff[2048], *create_info; String create_info_str(create_info_buff, sizeof(create_info_buff), system_charset_info); MRN_DBUG_ENTER_METHOD(); create_info_str.length(0); for (i = 0; i < n_columns; ++i) { Field *field = table_share->field[i]; if (!is_foreign_key_field(table_share->table_name.str, field->field_name.str)) { continue; } mrn::ColumnName column_name(field->field_name); column = grn_obj_column(ctx, grn_table, column_name.c_str(), column_name.length()); if (!column) { continue; } grn_id ref_table_id = grn_obj_get_range(ctx, column); grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id); char ref_table_buff[NAME_LEN + 1]; int ref_table_name_length = grn_obj_name(ctx, ref_table, ref_table_buff, NAME_LEN); ref_table_buff[ref_table_name_length] = '\0'; if (create_info_str.reserve(15)) { DBUG_RETURN(NULL); } create_info_str.q_append(",\n CONSTRAINT ", 15); append_identifier(ha_thd(), &create_info_str, column_name.c_str(), column_name.length()); if (create_info_str.reserve(14)) { DBUG_RETURN(NULL); } create_info_str.q_append(" FOREIGN KEY (", 14); append_identifier(ha_thd(), &create_info_str, column_name.c_str(), column_name.length()); if (create_info_str.reserve(13)) { DBUG_RETURN(NULL); } create_info_str.q_append(") REFERENCES ", 13); append_identifier(ha_thd(), &create_info_str, table_share->db.str, table_share->db.length); if (create_info_str.reserve(1)) { DBUG_RETURN(NULL); } create_info_str.q_append(".", 1); append_identifier(ha_thd(), &create_info_str, ref_table_buff, ref_table_name_length); if (create_info_str.reserve(2)) { DBUG_RETURN(NULL); } create_info_str.q_append(" (", 2); char ref_path[FN_REFLEN + 1]; TABLE_LIST table_list; TABLE_SHARE *tmp_ref_table_share; build_table_filename(ref_path, sizeof(ref_path) - 1, table_share->db.str, ref_table_buff, "", 0); DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path)); LEX_CSTRING table_name= { ref_table_buff, (size_t) ref_table_name_length }; table_list.init_one_table(&table_share->db, &table_name, 0, TL_WRITE); mrn_open_mutex_lock(table_share); tmp_ref_table_share = mrn_create_tmp_table_share(&table_list, ref_path, &error); mrn_open_mutex_unlock(table_share); if (!tmp_ref_table_share) { DBUG_RETURN(NULL); } uint ref_pkey_nr = tmp_ref_table_share->primary_key; KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr]; Field *ref_field = &ref_key_info->key_part->field[0]; append_identifier(ha_thd(), &create_info_str, ref_field->field_name.str, ref_field->field_name.length); mrn_open_mutex_lock(table_share); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table_share); if (create_info_str.reserve(39)) { DBUG_RETURN(NULL); } create_info_str.q_append(") ON DELETE RESTRICT ON UPDATE RESTRICT", 39); } if (!(create_info = (char *) mrn_my_malloc(create_info_str.length() + 1, MYF(MY_WME)))) { DBUG_RETURN(NULL); } memcpy(create_info, create_info_str.ptr(), create_info_str.length()); create_info[create_info_str.length()] = '\0'; DBUG_RETURN(create_info); } #else char *ha_mroonga::storage_get_foreign_key_create_info() { MRN_DBUG_ENTER_METHOD(); char *res = handler::get_foreign_key_create_info(); DBUG_RETURN(res); } #endif char *ha_mroonga::get_foreign_key_create_info() { MRN_DBUG_ENTER_METHOD(); char *res; if (share->wrapper_mode) { res = wrapper_get_foreign_key_create_info(); } else { res = storage_get_foreign_key_create_info(); } DBUG_RETURN(res); } #ifdef MRN_HANDLER_HAVE_GET_TABLESPACE_NAME char *ha_mroonga::wrapper_get_tablespace_name(THD *thd, char *name, uint name_len) { MRN_DBUG_ENTER_METHOD(); char *res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->get_tablespace_name(thd, name, name_len); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } char *ha_mroonga::storage_get_tablespace_name(THD *thd, char *name, uint name_len) { MRN_DBUG_ENTER_METHOD(); char *res = handler::get_tablespace_name(thd, name, name_len); DBUG_RETURN(res); } char *ha_mroonga::get_tablespace_name(THD *thd, char *name, uint name_len) { MRN_DBUG_ENTER_METHOD(); char *res; if (share->wrapper_mode) { res = wrapper_get_tablespace_name(thd, name, name_len); } else { res = storage_get_tablespace_name(thd, name, name_len); } DBUG_RETURN(res); } #endif bool ha_mroonga::wrapper_can_switch_engines() { MRN_DBUG_ENTER_METHOD(); bool res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->can_switch_engines(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } bool ha_mroonga::storage_can_switch_engines() { MRN_DBUG_ENTER_METHOD(); bool res = handler::can_switch_engines(); DBUG_RETURN(res); } bool ha_mroonga::can_switch_engines() { MRN_DBUG_ENTER_METHOD(); bool res; if (share->wrapper_mode) { res = wrapper_can_switch_engines(); } else { res = storage_can_switch_engines(); } DBUG_RETURN(res); } int ha_mroonga::wrapper_get_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->get_foreign_key_list(thd, f_key_list); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } #ifdef MRN_SUPPORT_FOREIGN_KEYS int ha_mroonga::storage_get_foreign_key_list(THD *thd, List *f_key_list) { int error; uint i; grn_obj *column; uint n_columns = table_share->fields; MRN_DBUG_ENTER_METHOD(); for (i = 0; i < n_columns; ++i) { Field *field = table_share->field[i]; if (!is_foreign_key_field(table_share->table_name.str, field->field_name.str)) { continue; } mrn::ColumnName column_name(field->field_name); column = grn_obj_column(ctx, grn_table, column_name.c_str(), column_name.length()); if (!column) { continue; } grn_id ref_table_id = grn_obj_get_range(ctx, column); grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id); FOREIGN_KEY_INFO f_key_info; f_key_info.foreign_id = thd_make_lex_string(thd, NULL, column_name.c_str(), column_name.length(), TRUE); f_key_info.foreign_db = thd_make_lex_string(thd, NULL, table_share->db.str, table_share->db.length, TRUE); f_key_info.foreign_table = thd_make_lex_string(thd, NULL, table_share->table_name.str, table_share->table_name.length, TRUE); f_key_info.referenced_db = f_key_info.foreign_db; char ref_table_buff[NAME_LEN + 1]; int ref_table_name_length = grn_obj_name(ctx, ref_table, ref_table_buff, NAME_LEN); ref_table_buff[ref_table_name_length] = '\0'; DBUG_PRINT("info", ("mroonga: ref_table_buff=%s", ref_table_buff)); DBUG_PRINT("info", ("mroonga: ref_table_name_length=%d", ref_table_name_length)); f_key_info.referenced_table = thd_make_lex_string(thd, NULL, ref_table_buff, ref_table_name_length, TRUE); f_key_info.update_method = FK_OPTION_RESTRICT; f_key_info.delete_method = FK_OPTION_RESTRICT; f_key_info.referenced_key_name = thd_make_lex_string(thd, NULL, "PRIMARY", 7, TRUE); LEX_CSTRING *field_name = thd_make_lex_string(thd, NULL, column_name.c_str(), column_name.length(), TRUE); f_key_info.foreign_fields.push_back(field_name); char ref_path[FN_REFLEN + 1]; TABLE_LIST table_list; TABLE_SHARE *tmp_ref_table_share; build_table_filename(ref_path, sizeof(ref_path) - 1, table_share->db.str, ref_table_buff, "", 0); DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path)); LEX_CSTRING table_name= { ref_table_buff, (size_t) ref_table_name_length }; table_list.init_one_table(&table_share->db, &table_name, 0, TL_WRITE); mrn_open_mutex_lock(table_share); tmp_ref_table_share = mrn_create_tmp_table_share(&table_list, ref_path, &error); mrn_open_mutex_unlock(table_share); if (!tmp_ref_table_share) { DBUG_RETURN(error); } uint ref_pkey_nr = tmp_ref_table_share->primary_key; KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr]; Field *ref_field = &ref_key_info->key_part->field[0]; LEX_CSTRING *ref_col_name = thd_make_lex_string(thd, NULL, ref_field->field_name.str, ref_field->field_name.length, TRUE); f_key_info.referenced_fields.push_back(ref_col_name); mrn_open_mutex_lock(table_share); mrn_free_tmp_table_share(tmp_ref_table_share); mrn_open_mutex_unlock(table_share); FOREIGN_KEY_INFO *p_f_key_info = (FOREIGN_KEY_INFO *) thd_memdup(thd, &f_key_info, sizeof(FOREIGN_KEY_INFO)); if (!p_f_key_info) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); } f_key_list->push_back(p_f_key_info); } DBUG_RETURN(0); } #else int ha_mroonga::storage_get_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res = handler::get_foreign_key_list(thd, f_key_list); DBUG_RETURN(res); } #endif int ha_mroonga::get_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res; if (share->wrapper_mode) { res = wrapper_get_foreign_key_list(thd, f_key_list); } else { res = storage_get_foreign_key_list(thd, f_key_list); } DBUG_RETURN(res); } int ha_mroonga::wrapper_get_parent_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->get_parent_foreign_key_list(thd, f_key_list); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } int ha_mroonga::storage_get_parent_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res = handler::get_parent_foreign_key_list(thd, f_key_list); DBUG_RETURN(res); } int ha_mroonga::get_parent_foreign_key_list(THD *thd, List *f_key_list) { MRN_DBUG_ENTER_METHOD(); int res; if (share->wrapper_mode) { res = wrapper_get_parent_foreign_key_list(thd, f_key_list); } else { res = storage_get_parent_foreign_key_list(thd, f_key_list); } DBUG_RETURN(res); } uint ha_mroonga::wrapper_referenced_by_foreign_key() { MRN_DBUG_ENTER_METHOD(); uint res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->referenced_by_foreign_key(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } uint ha_mroonga::storage_referenced_by_foreign_key() { MRN_DBUG_ENTER_METHOD(); uint res = handler::referenced_by_foreign_key(); DBUG_RETURN(res); } uint ha_mroonga::referenced_by_foreign_key() { MRN_DBUG_ENTER_METHOD(); uint res; if (share->wrapper_mode) { res = wrapper_referenced_by_foreign_key(); } else { res = storage_referenced_by_foreign_key(); } DBUG_RETURN(res); } void ha_mroonga::wrapper_init_table_handle_for_HANDLER() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->init_table_handle_for_HANDLER(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_init_table_handle_for_HANDLER() { MRN_DBUG_ENTER_METHOD(); handler::init_table_handle_for_HANDLER(); DBUG_VOID_RETURN; } void ha_mroonga::init_table_handle_for_HANDLER() { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_init_table_handle_for_HANDLER(); } else { storage_init_table_handle_for_HANDLER(); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_free_foreign_key_create_info(char* str) { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->free_foreign_key_create_info(str); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } #ifdef MRN_SUPPORT_FOREIGN_KEYS void ha_mroonga::storage_free_foreign_key_create_info(char* str) { MRN_DBUG_ENTER_METHOD(); my_free(str); DBUG_VOID_RETURN; } #else void ha_mroonga::storage_free_foreign_key_create_info(char* str) { MRN_DBUG_ENTER_METHOD(); handler::free_foreign_key_create_info(str); DBUG_VOID_RETURN; } #endif void ha_mroonga::free_foreign_key_create_info(char* str) { MRN_DBUG_ENTER_METHOD(); if (share->wrapper_mode) { wrapper_free_foreign_key_create_info(str); } else { storage_free_foreign_key_create_info(str); } DBUG_VOID_RETURN; } #ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS bool ha_mroonga::check_written_by_row_based_binlog() { MRN_DBUG_ENTER_METHOD(); THD *thd = ha_thd(); int current_stmt_binlog_row; #ifdef MRN_ROW_BASED_CHECK_IS_METHOD current_stmt_binlog_row = thd->is_current_stmt_binlog_format_row(); #else current_stmt_binlog_row = thd->current_stmt_binlog_row_based; #endif if (!current_stmt_binlog_row) { DBUG_RETURN(false); } if (table->s->tmp_table != NO_TMP_TABLE) { DBUG_RETURN(false); } if (!mrn_binlog_filter->db_ok(table->s->db.str)) { DBUG_RETURN(false); } if (!thd_test_options(thd, OPTION_BIN_LOG)) { DBUG_RETURN(false); } if (!mysql_bin_log.is_open()) { DBUG_RETURN(false); } DBUG_RETURN(true); } #endif #ifdef MRN_HAVE_HA_REBIND_PSI void ha_mroonga::wrapper_unbind_psi() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->unbind_psi(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_unbind_psi() { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } void ha_mroonga::unbind_psi() { MRN_DBUG_ENTER_METHOD(); handler::unbind_psi(); if (share->wrapper_mode) { wrapper_unbind_psi(); } else { storage_unbind_psi(); } DBUG_VOID_RETURN; } void ha_mroonga::wrapper_rebind() { MRN_DBUG_ENTER_METHOD(); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); wrap_handler->rebind_psi(); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_VOID_RETURN; } void ha_mroonga::storage_rebind() { MRN_DBUG_ENTER_METHOD(); DBUG_VOID_RETURN; } void ha_mroonga::rebind_psi() { MRN_DBUG_ENTER_METHOD(); handler::rebind_psi(); if (share->wrapper_mode) { wrapper_rebind(); } else { storage_rebind(); } DBUG_VOID_RETURN; } #endif my_bool ha_mroonga::wrapper_register_query_cache_table(THD *thd, const char *table_key, uint key_length, qc_engine_callback *engine_callback, ulonglong *engine_data) { MRN_DBUG_ENTER_METHOD(); my_bool res; MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); res = wrap_handler->register_query_cache_table(thd, table_key, key_length, engine_callback, engine_data); MRN_SET_BASE_SHARE_KEY(share, table->s); MRN_SET_BASE_TABLE_KEY(this, table); DBUG_RETURN(res); } my_bool ha_mroonga::storage_register_query_cache_table(THD *thd, const char *table_key, uint key_length, qc_engine_callback *engine_callback, ulonglong *engine_data) { MRN_DBUG_ENTER_METHOD(); my_bool res = handler::register_query_cache_table(thd, table_key, key_length, engine_callback, engine_data); DBUG_RETURN(res); } my_bool ha_mroonga::register_query_cache_table(THD *thd, const char *table_key, uint key_length, qc_engine_callback *engine_callback, ulonglong *engine_data) { MRN_DBUG_ENTER_METHOD(); my_bool res; if (share->wrapper_mode) { res = wrapper_register_query_cache_table(thd, table_key, key_length, engine_callback, engine_data); } else { res = storage_register_query_cache_table(thd, table_key, key_length, engine_callback, engine_data); } DBUG_RETURN(res); } #ifdef __cplusplus } #endif namespace mrn { namespace variables { ulonglong get_boolean_mode_syntax_flags(THD *thd) { ulonglong flags = BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT; #ifdef MRN_SUPPORT_THDVAR_SET flags = THDVAR(thd, boolean_mode_syntax_flags); #endif return flags; } ActionOnError get_action_on_fulltext_query_error(THD *thd) { ulong action = THDVAR(thd, action_on_fulltext_query_error); return static_cast(action); } } }