diff options
Diffstat (limited to 'storage/mroonga/ha_mroonga.cpp')
-rw-r--r-- | storage/mroonga/ha_mroonga.cpp | 17105 |
1 files changed, 17105 insertions, 0 deletions
diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp new file mode 100644 index 00000000..a176ccf3 --- /dev/null +++ b/storage/mroonga/ha_mroonga.cpp @@ -0,0 +1,17105 @@ +/* -*- 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 <kou@clear-code.com> + Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com> + + 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 <sql_plugin.h> +#include <sql_show.h> +#include <key.h> +#include <tztime.h> +#include <sql_base.h> +#include <sql_select.h> +#include <item_sum.h> + +#ifdef MRN_HAVE_BINLOG_H +# include <binlog.h> +#endif + +#ifdef MRN_HAVE_SQL_OPTIMIZER_H +# include <sql_optimizer.h> +#endif + +#include <ft_global.h> +#include <spatial.h> +#include <mysql.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef WIN32 +# include <math.h> +# include <direct.h> +# 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 <dirent.h> +# include <unistd.h> +#endif + +#include "mrn_err.h" +#include "mrn_table.hpp" +#include <groonga/plugin.h> +#include "ha_mroonga.hpp" +#include <mrn_path_mapper.hpp> +#include <mrn_index_table_name.hpp> +#include <mrn_index_column_name.hpp> +#include <mrn_debug_column_access.hpp> +#include <mrn_auto_increment_value_lock.hpp> +#include <mrn_external_lock.hpp> +#include <mrn_match_escalation_threshold_scope.hpp> +#include <mrn_multiple_column_key_codec.hpp> +#include <mrn_field_normalizer.hpp> +#include <mrn_encoding.hpp> +#include <mrn_parameters_parser.hpp> +#include <mrn_lock.hpp> +#include <mrn_condition_converter.hpp> +#include <mrn_time_converter.hpp> +#include <mrn_smart_grn_obj.hpp> +#include <mrn_database_manager.hpp> +#include <mrn_context_pool.hpp> +#include <mrn_grn.hpp> +#include <mrn_value_decoder.hpp> +#include <mrn_database_repairer.hpp> +#include <mrn_operation.hpp> +#include <mrn_column_name.hpp> +#include <mrn_count_skip_checker.hpp> +#include <mrn_variables.hpp> +#include <mrn_query_parser.hpp> +#include <mrn_smart_bitmap.hpp> +#include <mrn_table_fields_offset_mover.hpp> + +#ifdef MRN_SUPPORT_FOREIGN_KEYS +# include <sql_table.h> +#endif + +#ifdef MRN_SUPPORT_CUSTOM_OPTIONS +# include <create_options.h> +#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 = "<unknown>"; + 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 = "<unknown>"; + 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<MRN_SHARE *>(const_cast<uchar *>(record)); + *length = share->table_name_length; + DBUG_RETURN(reinterpret_cast<uchar *>(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<MRN_LONG_TERM_SHARE *>(const_cast<uchar *>(record)); + *length = long_term_share->table_name_length; + DBUG_RETURN(reinterpret_cast<uchar *>(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<char *>(grn_get_version()); +static char *mrn_version = const_cast<char *>(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<uint>((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<uchar *>(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<const ulong *>(save); + ulong old_value = mrn_log_level; + mrn_log_level = new_value; + mrn_logger.max_level = static_cast<grn_log_level>(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<ulong>(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<const int *>(save); + int *old_value_ptr = static_cast<int *>(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<const bool *>(save); + bool *old_value_ptr = static_cast<bool *>(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_num *>(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_num *>(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_num *>(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_num *>(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_num *>(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<int>(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<int>(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<handlerton *>(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<grn_log_level>(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<st_mrn_ft_info *>(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> 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_spec> 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_spec> 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(HA_KEY_SWITCH_ALL); + if (!error) { + error = wrapper_enable_indexes_mroonga(HA_KEY_SWITCH_ALL); + } + } + } + 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<grn_obj *>(columns)); + unsigned int n_columns = + grn_table_size(ctx, reinterpret_cast<grn_obj *>(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_blob *>(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<int>(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<int>(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<grn_obj *>(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<grn_id *>(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<grn_ii *>(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<grn_ii *>(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<unsigned int>(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<struct st_mrn_ft_info *>(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<int>(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<struct st_mrn_ft_info *>(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<struct st_mrn_ft_info *>(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<grn_obj *>(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<grn_id *>(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 *>(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<MAX_FIELD_WIDTH> 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_num *>(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<int>(field->val_int() + 2000); + } else { + year = static_cast<int>(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<MAX_FIELD_WIDTH> 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_json *>(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_num *>(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<int32>(sec), + static_cast<int32>(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<int32>(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 *>(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<uchar *>(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<uint16>(field->sort_charset()->min_sort_char); + if (raw_min_sort_char <= UINT_MAX8) { + uchar min_sort_char = static_cast<uchar>(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_set*>(field)->typelib, + static_cast<Field_set*>(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<Item_func_match> 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(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) { + 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; + } + } + } else { + error = HA_ERR_WRONG_COMMAND; + } + DBUG_RETURN(error); +} + +int ha_mroonga::wrapper_disable_indexes(uint mode) +{ + 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(mode); + 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(mode); + } + DBUG_RETURN(error); +} + +int ha_mroonga::storage_disable_indexes(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) { + 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 (mode == HA_KEY_SWITCH_NONUNIQ_SAVE && + (key_info[i].flags & HA_NOSAME)) { + continue; + } + + int sub_error = generic_disable_index(i, key_info); + if (error != 0 && sub_error != 0) { + error = sub_error; + } + } + } else { + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + DBUG_RETURN(error); +} + +int ha_mroonga::disable_indexes(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + if (share->wrapper_mode) + { + error = wrapper_disable_indexes(mode); + } else { + error = storage_disable_indexes(mode); + } + DBUG_RETURN(error); +} + +int ha_mroonga::wrapper_enable_indexes_mroonga(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) { + 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); + } else { + error = HA_ERR_WRONG_COMMAND; + } + DBUG_RETURN(error); +} + +int ha_mroonga::wrapper_enable_indexes(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + + int mroonga_error = wrapper_enable_indexes_mroonga(mode); + + MRN_SET_WRAP_SHARE_KEY(share, table->s); + MRN_SET_WRAP_TABLE_KEY(this, table); + error = wrap_handler->ha_enable_indexes(mode); + 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(uint mode) +{ + 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; + bool skip_unique_key = (mode == HA_KEY_SWITCH_NONUNIQ_SAVE); + MRN_DBUG_ENTER_METHOD(); + if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) { + uint i; + for (i = 0; i < table_share->keys; i++) { + if (i == table->s->primary_key) { + continue; + } + if (!grn_index_columns[i]) { + break; + } + } + if (i == table_share->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 = 0; i < n_keys; i++) { + if (i == table->s->primary_key) { + continue; + } + if (skip_unique_key && (key_info[i].flags & HA_NOSAME)) { + 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) + { + error = storage_add_index_multiple_columns(key_info, n_keys, + index_tables, + index_columns, + skip_unique_key); + } + bitmap_set_all(table->read_set); + } else { + MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); + MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables); + MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns); + DBUG_RETURN(error); +} + +int ha_mroonga::enable_indexes(uint mode) +{ + int error = 0; + MRN_DBUG_ENTER_METHOD(); + share->disable_keys = false; + if (share->wrapper_mode) + { + error = wrapper_enable_indexes(mode); + } else { + error = storage_enable_indexes(mode); + } + 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<ulong>(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_field> 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_field> 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_field> 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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<FOREIGN_KEY_INFO> *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<ActionOnError>(action); + } + } +} |