diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/mroonga/lib | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-upstream.tar.xz mariadb-10.5-upstream.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
62 files changed, 6679 insertions, 0 deletions
diff --git a/storage/mroonga/lib/Makefile.am b/storage/mroonga/lib/Makefile.am new file mode 100644 index 00000000..300131db --- /dev/null +++ b/storage/mroonga/lib/Makefile.am @@ -0,0 +1,23 @@ +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + $(MYSQL_INCLUDES) \ + $(GROONGA_CFLAGS) \ + $(MYSQL_VERSION_CFLAGS) + +libmrn_need_mysql_la_CXXFLAGS = $(AM_CXXFLAGS) $(MYSQL_CFLAGS) + +if WITH_LIBMYSQLSERVICES_COMPAT +LIBMYSQLSERVICES_COMPAT = libmysqlservices.la +endif + +noinst_LTLIBRARIES = \ + $(LIBMYSQLSERVICES_COMPAT) \ + libmrn_no_mysql.la \ + libmrn_need_mysql.la + +include libmrn_no_mysql_sources.am +include libmrn_need_mysql_sources.am +if WITH_LIBMYSQLSERVICES_COMPAT +include libmysqlservices_compat_sources.am +endif diff --git a/storage/mroonga/lib/libmrn_need_mysql_sources.am b/storage/mroonga/lib/libmrn_need_mysql_sources.am new file mode 100644 index 00000000..e8c03c63 --- /dev/null +++ b/storage/mroonga/lib/libmrn_need_mysql_sources.am @@ -0,0 +1,50 @@ +libmrn_need_mysql_la_SOURCES = \ + mrn_index_table_name.cpp \ + mrn_index_table_name.hpp \ + mrn_index_column_name.cpp \ + mrn_index_column_name.hpp \ + mrn_debug_column_access.cpp \ + mrn_debug_column_access.hpp \ + mrn_auto_increment_value_lock.cpp \ + mrn_auto_increment_value_lock.hpp \ + mrn_external_lock.cpp \ + mrn_external_lock.hpp \ + mrn_multiple_column_key_codec.cpp \ + mrn_multiple_column_key_codec.hpp \ + mrn_field_normalizer.cpp \ + mrn_field_normalizer.hpp \ + mrn_encoding.cpp \ + mrn_encoding.hpp \ + mrn_parameters_parser.cpp \ + mrn_parameters_parser.hpp \ + mrn_lock.cpp \ + mrn_lock.hpp \ + mrn_condition_converter.cpp \ + mrn_condition_converter.hpp \ + mrn_time_converter.cpp \ + mrn_time_converter.hpp \ + mrn_database_manager.cpp \ + mrn_database_manager.hpp \ + mrn_value_decoder.cpp \ + mrn_value_decoder.hpp \ + mrn_database_repairer.cpp \ + mrn_database_repairer.hpp \ + mrn_context_pool.cpp \ + mrn_context_pool.hpp \ + mrn_operations.cpp \ + mrn_operations.hpp \ + mrn_operation.cpp \ + mrn_operation.hpp \ + mrn_database.cpp \ + mrn_database.hpp \ + mrn_column_name.cpp \ + mrn_column_name.hpp \ + mrn_count_skip_checker.cpp \ + mrn_count_skip_checker.hpp \ + mrn_query_parser.cpp \ + mrn_query_parser.hpp \ + mrn_current_thread.hpp \ + mrn_smart_bitmap.cpp \ + mrn_smart_bitmap.hpp \ + mrn_table_fields_offset_mover.cpp \ + mrn_table_fields_offset_mover.hpp diff --git a/storage/mroonga/lib/libmrn_no_mysql_sources.am b/storage/mroonga/lib/libmrn_no_mysql_sources.am new file mode 100644 index 00000000..fd2d942d --- /dev/null +++ b/storage/mroonga/lib/libmrn_no_mysql_sources.am @@ -0,0 +1,9 @@ +libmrn_no_mysql_la_SOURCES = \ + mrn_match_escalation_threshold_scope.cpp \ + mrn_match_escalation_threshold_scope.hpp \ + mrn_path_mapper.cpp \ + mrn_path_mapper.hpp \ + mrn_windows.hpp \ + mrn_smart_grn_obj.cpp \ + mrn_smart_grn_obj.hpp \ + mrn_grn.hpp diff --git a/storage/mroonga/lib/libmysqlservices_compat_sources.am b/storage/mroonga/lib/libmysqlservices_compat_sources.am new file mode 100644 index 00000000..bb0712a4 --- /dev/null +++ b/storage/mroonga/lib/libmysqlservices_compat_sources.am @@ -0,0 +1,2 @@ +libmysqlservices_la_SOURCES = \ + mrn_mysqlservices.cpp diff --git a/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp b/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp new file mode 100644 index 00000000..4baef101 --- /dev/null +++ b/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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_auto_increment_value_lock.hpp" + +#if MYSQL_VERSION_ID >= 50500 +# define AUTO_INCREMENT_VALUE_MUTEX(table_share) (&(table_share->LOCK_ha_data)) +#else +# define AUTO_INCREMENT_VALUE_MUTEX(table_share) (&(table_share->mutex)) +#endif + +namespace mrn { + AutoIncrementValueLock::AutoIncrementValueLock(TABLE_SHARE *table_share) + : table_share_(table_share), + need_lock_(table_share_->tmp_table == NO_TMP_TABLE) { + if (need_lock_) { + mysql_mutex_lock(AUTO_INCREMENT_VALUE_MUTEX(table_share_)); + } + } + + AutoIncrementValueLock::~AutoIncrementValueLock() { + if (need_lock_) { + mysql_mutex_unlock(AUTO_INCREMENT_VALUE_MUTEX(table_share_)); + } + } +} diff --git a/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp b/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp new file mode 100644 index 00000000..ed2f9f3c --- /dev/null +++ b/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_ +#define MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_ + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +namespace mrn { + class AutoIncrementValueLock { + TABLE_SHARE *table_share_; + bool need_lock_; + public: + AutoIncrementValueLock(TABLE_SHARE *table_share); + ~AutoIncrementValueLock(); + }; +} + +#endif // MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_ diff --git a/storage/mroonga/lib/mrn_column_name.cpp b/storage/mroonga/lib/mrn_column_name.cpp new file mode 100644 index 00000000..173553dd --- /dev/null +++ b/storage/mroonga/lib/mrn_column_name.cpp @@ -0,0 +1,69 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 Kouhei Sutou <kou@clear-code.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> + +#include "mrn_column_name.hpp" + +#include <strfunc.h> + +#include <string.h> + +// for debug +#define MRN_CLASS_NAME "mrn::ColumnName" + +namespace mrn { + ColumnName::ColumnName(const char *mysql_name) + : mysql_name_(mysql_name) { + encode(mysql_name, strlen(mysql_name)); + } + + ColumnName::ColumnName(const LEX_CSTRING &mysql_name) + : mysql_name_(mysql_name.str) { + encode(mysql_name.str, mysql_name.length); + } + + const char *ColumnName::mysql_name() { + return mysql_name_; + } + + const char *ColumnName::c_str() { + return name_; + } + + size_t ColumnName::length() { + return length_; + } + + void ColumnName::encode(const char *mysql_name, + size_t mysql_name_length) { + MRN_DBUG_ENTER_METHOD(); + uint errors; + length_ = mrn_strconvert(system_charset_info, + mysql_name, + mysql_name_length, + &my_charset_filename, + name_, + MRN_MAX_PATH_SIZE, + &errors); + name_[length_] = '\0'; + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_column_name.hpp b/storage/mroonga/lib/mrn_column_name.hpp new file mode 100644 index 00000000..abd4bf54 --- /dev/null +++ b/storage/mroonga/lib/mrn_column_name.hpp @@ -0,0 +1,39 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 Kouhei Sutou <kou@clear-code.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 +*/ + +#pragma once + +#include <mrn_constants.hpp> + +namespace mrn { + class ColumnName { + public: + ColumnName(const char *mysql_name); + ColumnName(const LEX_CSTRING &mysql_name); + const char *mysql_name(); + const char *c_str(); + size_t length(); + private: + const char *mysql_name_; + char name_[MRN_MAX_PATH_SIZE]; + size_t length_; + + void encode(const char *mysql_name, size_t mysql_name_length); + }; +} diff --git a/storage/mroonga/lib/mrn_condition_converter.cpp b/storage/mroonga/lib/mrn_condition_converter.cpp new file mode 100644 index 00000000..761dfc72 --- /dev/null +++ b/storage/mroonga/lib/mrn_condition_converter.cpp @@ -0,0 +1,637 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013-2017 Kouhei Sutou <kou@clear-code.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_condition_converter.hpp" +#include "mrn_time_converter.hpp" +#include "mrn_smart_grn_obj.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::ConditionConverter" + +#ifdef MRN_ITEM_HAVE_ITEM_NAME +# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->item_name.ptr()) +# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->item_name.length()) +#else +# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->name.str) +# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->name.length) +#endif + +namespace mrn { + ConditionConverter::ConditionConverter(grn_ctx *ctx, grn_obj *table, + bool is_storage_mode) + : ctx_(ctx), + table_(table), + is_storage_mode_(is_storage_mode) { + GRN_TEXT_INIT(&column_name_, 0); + GRN_VOID_INIT(&value_); + } + + ConditionConverter::~ConditionConverter() { + grn_obj_unlink(ctx_, &column_name_); + grn_obj_unlink(ctx_, &value_); + } + + bool ConditionConverter::is_convertable(const Item *item) { + MRN_DBUG_ENTER_METHOD(); + + if (!item) { + DBUG_RETURN(false); + } + + switch (item->type()) { + case Item::COND_ITEM: + { + const Item_cond *cond_item = reinterpret_cast<const Item_cond *>(item); + bool convertable = is_convertable(cond_item); + DBUG_RETURN(convertable); + } + break; + case Item::FUNC_ITEM: + { + const Item_func *func_item = reinterpret_cast<const Item_func *>(item); + bool convertable = is_convertable(func_item); + DBUG_RETURN(convertable); + } + break; + default: + DBUG_RETURN(false); + break; + } + + DBUG_RETURN(false); + } + + bool ConditionConverter::is_convertable(const Item_cond *cond_item) { + MRN_DBUG_ENTER_METHOD(); + + if (!is_storage_mode_) { + DBUG_RETURN(false); + } + + if (cond_item->functype() != Item_func::COND_AND_FUNC) { + DBUG_RETURN(false); + } + + List<Item> *argument_list = + const_cast<Item_cond *>(cond_item)->argument_list(); + List_iterator<Item> iterator(*argument_list); + const Item *sub_item; + while ((sub_item = iterator++)) { + if (!is_convertable(sub_item)) { + DBUG_RETURN(false); + } + } + + DBUG_RETURN(true); + } + + bool ConditionConverter::is_convertable(const Item_func *func_item) { + MRN_DBUG_ENTER_METHOD(); + + switch (func_item->functype()) { + case Item_func::EQ_FUNC: + case Item_func::LT_FUNC: + case Item_func::LE_FUNC: + case Item_func::GE_FUNC: + case Item_func::GT_FUNC: + if (!is_storage_mode_) { + DBUG_RETURN(false); + } + { + Item **arguments = func_item->arguments(); + Item *left_item = arguments[0]; + Item *right_item = arguments[1]; + if (left_item->type() != Item::FIELD_ITEM) { + DBUG_RETURN(false); + } + if (!right_item->basic_const_item()) { + DBUG_RETURN(false); + } + + bool convertable = + is_convertable_binary_operation(static_cast<Item_field *>(left_item), + right_item, + func_item->functype()); + DBUG_RETURN(convertable); + } + break; + case Item_func::FT_FUNC: + DBUG_RETURN(true); + break; + case Item_func::BETWEEN: + if (!is_storage_mode_) { + DBUG_RETURN(false); + } + { + Item **arguments = func_item->arguments(); + Item *target_item = arguments[0]; + Item *min_item = arguments[1]; + Item *max_item = arguments[2]; + if (target_item->type() != Item::FIELD_ITEM) { + DBUG_RETURN(false); + } + if (!min_item->basic_const_item()) { + DBUG_RETURN(false); + } + if (!max_item->basic_const_item()) { + DBUG_RETURN(false); + } + + bool convertable = + is_convertable_between(static_cast<Item_field *>(target_item), + min_item, + max_item); + DBUG_RETURN(convertable); + } + default: + DBUG_RETURN(false); + break; + } + + DBUG_RETURN(true); + } + + bool ConditionConverter::is_convertable_binary_operation( + const Item_field *field_item, + Item *value_item, + Item_func::Functype func_type) { + MRN_DBUG_ENTER_METHOD(); + + bool convertable = false; + + enum_field_types field_type = field_item->field->real_type(); + NormalizedType normalized_type = normalize_field_type(field_type); + switch (normalized_type) { + case STRING_TYPE: + if (value_item->is_of_type(Item::CONST_ITEM, STRING_RESULT) && + func_type == Item_func::EQ_FUNC) { + convertable = have_index(field_item, GRN_OP_EQUAL); + } + break; + case INT_TYPE: + if (field_type == MYSQL_TYPE_ENUM) { + convertable = value_item->is_of_type(Item::CONST_ITEM, STRING_RESULT) || + value_item->is_of_type(Item::CONST_ITEM, INT_RESULT); + } else { + convertable = value_item->is_of_type(Item::CONST_ITEM, INT_RESULT); + } + break; + case TIME_TYPE: + if (is_valid_time_value(field_item, value_item)) { + convertable = have_index(field_item, func_type); + } + break; + case UNSUPPORTED_TYPE: + break; + } + + DBUG_RETURN(convertable); + } + + bool ConditionConverter::is_convertable_between(const Item_field *field_item, + Item *min_item, + Item *max_item) { + MRN_DBUG_ENTER_METHOD(); + + bool convertable = false; + + enum_field_types field_type = field_item->field->type(); + NormalizedType normalized_type = normalize_field_type(field_type); + switch (normalized_type) { + case STRING_TYPE: + if (min_item->is_of_type(Item::CONST_ITEM, STRING_RESULT) && + max_item->is_of_type(Item::CONST_ITEM, STRING_RESULT)) { + convertable = have_index(field_item, GRN_OP_LESS); + } + break; + case INT_TYPE: + if (min_item->is_of_type(Item::CONST_ITEM, INT_RESULT) && + max_item->is_of_type(Item::CONST_ITEM, INT_RESULT)) { + convertable = have_index(field_item, GRN_OP_LESS); + } + break; + case TIME_TYPE: + if (is_valid_time_value(field_item, min_item) && + is_valid_time_value(field_item, max_item)) { + convertable = have_index(field_item, GRN_OP_LESS); + } + break; + case UNSUPPORTED_TYPE: + break; + } + + DBUG_RETURN(convertable); + } + + bool ConditionConverter::is_valid_time_value(const Item_field *field_item, + Item *value_item) { + MRN_DBUG_ENTER_METHOD(); + + MYSQL_TIME mysql_time; + bool error = get_time_value(field_item, value_item, &mysql_time); + + DBUG_RETURN(!error); + } + + bool ConditionConverter::get_time_value(const Item_field *field_item, + Item *value_item, + MYSQL_TIME *mysql_time) { + MRN_DBUG_ENTER_METHOD(); + + bool error; + Item *real_value_item = value_item->real_item(); + switch (field_item->field->type()) { + case MYSQL_TYPE_TIME: + { + THD *thd= current_thd; + error= real_value_item->get_date(thd, mysql_time, Time::Options(thd)); + break; + } + case MYSQL_TYPE_YEAR: + mysql_time->year = static_cast<int>(value_item->val_int()); + mysql_time->month = 1; + mysql_time->day = 1; + mysql_time->hour = 0; + mysql_time->hour = 0; + mysql_time->minute = 0; + mysql_time->second_part = 0; + mysql_time->neg = false; + mysql_time->time_type = MYSQL_TIMESTAMP_DATE; + error = false; + break; + default: + { + THD *thd= current_thd; + Datetime::Options opt(TIME_FUZZY_DATES, thd); + error = real_value_item->get_date(thd, mysql_time, opt); + break; + } + } + + DBUG_RETURN(error); + } + + ConditionConverter::NormalizedType + ConditionConverter::normalize_field_type(enum_field_types field_type) { + MRN_DBUG_ENTER_METHOD(); + + NormalizedType type = UNSUPPORTED_TYPE; + + switch (field_type) { + case MYSQL_TYPE_DECIMAL: + type = STRING_TYPE; + break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + type = INT_TYPE; + break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + type = UNSUPPORTED_TYPE; + break; + case MYSQL_TYPE_NULL: + type = UNSUPPORTED_TYPE; + break; + case MYSQL_TYPE_TIMESTAMP: + type = TIME_TYPE; + break; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + type = INT_TYPE; + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + type = TIME_TYPE; + break; + case MYSQL_TYPE_VARCHAR: + type = STRING_TYPE; + break; + case MYSQL_TYPE_BIT: + type = INT_TYPE; + break; +#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: + type = TIME_TYPE; + break; +#endif +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: + type = TIME_TYPE; + break; +#endif +#ifdef MRN_HAVE_MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: + type = TIME_TYPE; + break; +#endif + case MYSQL_TYPE_NEWDECIMAL: + type = STRING_TYPE; + break; + case MYSQL_TYPE_ENUM: + type = INT_TYPE; + break; + case MYSQL_TYPE_SET: + type = INT_TYPE; + break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + type = STRING_TYPE; + break; + case MYSQL_TYPE_GEOMETRY: + type = UNSUPPORTED_TYPE; + 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 = STRING_TYPE; + break; +#endif + } + + DBUG_RETURN(type); + } + + bool ConditionConverter::have_index(const Item_field *field_item, + grn_operator _operator) { + MRN_DBUG_ENTER_METHOD(); + + grn_obj *column; + column = grn_obj_column(ctx_, table_, + MRN_ITEM_FIELD_GET_NAME(field_item), + MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item)); + if (!column) { + DBUG_RETURN(false); + } + mrn::SmartGrnObj smart_column(ctx_, column); + + int n_indexes = grn_column_index(ctx_, column, _operator, NULL, 0, NULL); + bool convertable = (n_indexes > 0); + + DBUG_RETURN(convertable); + } + + bool ConditionConverter::have_index(const Item_field *field_item, + Item_func::Functype func_type) { + MRN_DBUG_ENTER_METHOD(); + + bool have = false; + switch (func_type) { + case Item_func::EQ_FUNC: + have = have_index(field_item, GRN_OP_EQUAL); + break; + case Item_func::LT_FUNC: + have = have_index(field_item, GRN_OP_LESS); + break; + case Item_func::LE_FUNC: + have = have_index(field_item, GRN_OP_LESS_EQUAL); + break; + case Item_func::GE_FUNC: + have = have_index(field_item, GRN_OP_GREATER_EQUAL); + break; + case Item_func::GT_FUNC: + have = have_index(field_item, GRN_OP_GREATER); + break; + default: + break; + } + + DBUG_RETURN(have); + } + + unsigned int ConditionConverter::count_match_against(const Item *item) { + MRN_DBUG_ENTER_METHOD(); + + if (!item) { + DBUG_RETURN(0); + } + + switch (item->type()) { + case Item::COND_ITEM: + if (is_storage_mode_) { + Item_cond *cond_item = (Item_cond *)item; + if (cond_item->functype() == Item_func::COND_AND_FUNC) { + unsigned int n_match_againsts = 0; + List_iterator<Item> iterator(*((cond_item)->argument_list())); + const Item *sub_item; + while ((sub_item = iterator++)) { + n_match_againsts += count_match_against(sub_item); + } + DBUG_RETURN(n_match_againsts); + } + } + break; + case Item::FUNC_ITEM: + { + const Item_func *func_item = (const Item_func *)item; + switch (func_item->functype()) { + case Item_func::FT_FUNC: + DBUG_RETURN(1); + break; + default: + break; + } + } + break; + default: + break; + } + + DBUG_RETURN(0); + } + + void ConditionConverter::convert(const Item *where, grn_obj *expression) { + MRN_DBUG_ENTER_METHOD(); + + if (!where || where->type() != Item::COND_ITEM) { + DBUG_VOID_RETURN; + } + + Item_cond *cond_item = (Item_cond *)where; + List_iterator<Item> iterator(*((cond_item)->argument_list())); + const Item *sub_item; + while ((sub_item = iterator++)) { + switch (sub_item->type()) { + case Item::FUNC_ITEM: + { + const Item_func *func_item = (const Item_func *)sub_item; + switch (func_item->functype()) { + case Item_func::EQ_FUNC: + convert_binary_operation(func_item, expression, GRN_OP_EQUAL); + break; + case Item_func::LT_FUNC: + convert_binary_operation(func_item, expression, GRN_OP_LESS); + break; + case Item_func::LE_FUNC: + convert_binary_operation(func_item, expression, GRN_OP_LESS_EQUAL); + break; + case Item_func::GE_FUNC: + convert_binary_operation(func_item, expression, + GRN_OP_GREATER_EQUAL); + break; + case Item_func::GT_FUNC: + convert_binary_operation(func_item, expression, GRN_OP_GREATER); + break; + case Item_func::BETWEEN: + convert_between(func_item, expression); + break; + default: + break; + } + } + break; + default: + break; + } + } + + DBUG_VOID_RETURN; + } + + void ConditionConverter::convert_binary_operation(const Item_func *func_item, + grn_obj *expression, + grn_operator _operator) { + Item **arguments = func_item->arguments(); + Item *left_item = arguments[0]; + Item *right_item = arguments[1]; + if (left_item->type() == Item::FIELD_ITEM) { + const Item_field *field_item = static_cast<const Item_field *>(left_item); + append_field_value(field_item, expression); + append_const_item(field_item, right_item, expression); + grn_expr_append_op(ctx_, expression, _operator, 2); + grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2); + } + } + + void ConditionConverter::convert_between(const Item_func *func_item, + grn_obj *expression) { + MRN_DBUG_ENTER_METHOD(); + + Item **arguments = func_item->arguments(); + Item *target_item = arguments[0]; + Item *min_item = arguments[1]; + Item *max_item = arguments[2]; + + grn_obj *between_func = grn_ctx_get(ctx_, "between", strlen("between")); + grn_expr_append_obj(ctx_, expression, between_func, GRN_OP_PUSH, 1); + + const Item_field *field_item = static_cast<const Item_field *>(target_item); + append_field_value(field_item, expression); + + grn_obj include; + mrn::SmartGrnObj smart_include(ctx_, &include); + GRN_TEXT_INIT(&include, 0); + GRN_TEXT_PUTS(ctx_, &include, "include"); + append_const_item(field_item, min_item, expression); + grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1); + append_const_item(field_item, max_item, expression); + grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1); + + grn_expr_append_op(ctx_, expression, GRN_OP_CALL, 5); + + grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2); + + DBUG_VOID_RETURN; + } + + void ConditionConverter::append_field_value(const Item_field *field_item, + grn_obj *expression) { + MRN_DBUG_ENTER_METHOD(); + + GRN_BULK_REWIND(&column_name_); + GRN_TEXT_PUT(ctx_, &column_name_, + MRN_ITEM_FIELD_GET_NAME(field_item), + MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item)); + grn_expr_append_const(ctx_, expression, &column_name_, + GRN_OP_PUSH, 1); + grn_expr_append_op(ctx_, expression, GRN_OP_GET_VALUE, 1); + + DBUG_VOID_RETURN; + } + + void ConditionConverter::append_const_item(const Item_field *field_item, + Item *const_item, + grn_obj *expression) { + MRN_DBUG_ENTER_METHOD(); + + enum_field_types field_type = field_item->field->real_type(); + NormalizedType normalized_type = normalize_field_type(field_type); + + switch (normalized_type) { + case STRING_TYPE: + grn_obj_reinit(ctx_, &value_, GRN_DB_TEXT, 0); + { + String *string; + string = const_item->val_str(NULL); + GRN_TEXT_SET(ctx_, &value_, string->ptr(), string->length()); + } + break; + case INT_TYPE: + grn_obj_reinit(ctx_, &value_, GRN_DB_INT64, 0); + if (field_type == MYSQL_TYPE_ENUM) { + if (const_item->is_of_type(Item::CONST_ITEM, STRING_RESULT)) { + String *string; + string = const_item->val_str(NULL); + Field_enum *enum_field = static_cast<Field_enum *>(field_item->field); + int enum_value = find_type(string->c_ptr(), + enum_field->typelib, + FIND_TYPE_BASIC); + GRN_INT64_SET(ctx_, &value_, enum_value); + } else { + GRN_INT64_SET(ctx_, &value_, const_item->val_int()); + } + } else { + GRN_INT64_SET(ctx_, &value_, const_item->val_int()); + } + break; + case TIME_TYPE: + grn_obj_reinit(ctx_, &value_, GRN_DB_TIME, 0); + { + MYSQL_TIME mysql_time; + get_time_value(field_item, const_item, &mysql_time); + bool truncated = false; + TimeConverter time_converter; + long long int time = + time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); + GRN_TIME_SET(ctx_, &value_, time); + } + break; + case UNSUPPORTED_TYPE: + // Should not be occurred. + DBUG_PRINT("error", + ("mroonga: append_const_item: unsupported type: <%d> " + "This case should not be occurred.", + field_type)); + grn_obj_reinit(ctx_, &value_, GRN_DB_VOID, 0); + break; + } + grn_expr_append_const(ctx_, expression, &value_, GRN_OP_PUSH, 1); + + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_condition_converter.hpp b/storage/mroonga/lib/mrn_condition_converter.hpp new file mode 100644 index 00000000..bf10001d --- /dev/null +++ b/storage/mroonga/lib/mrn_condition_converter.hpp @@ -0,0 +1,85 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_CONDITION_CONVERTER_HPP_ +#define MRN_CONDITION_CONVERTER_HPP_ + +#include <mrn_mysql_compat.h> + +#include <item_cmpfunc.h> + +#include <groonga.h> + +namespace mrn { + class ConditionConverter { + public: + ConditionConverter(grn_ctx *ctx, grn_obj *table, bool is_storage_mode); + ~ConditionConverter(); + + bool is_convertable(const Item *item); + unsigned int count_match_against(const Item *item); + // caller must check "where" can be convertable by + // is_convertable(). This method doesn't validate "where". + void convert(const Item *where, grn_obj *expression); + + private: + enum NormalizedType { + STRING_TYPE, + INT_TYPE, + TIME_TYPE, + UNSUPPORTED_TYPE, + }; + + grn_ctx *ctx_; + grn_obj *table_; + bool is_storage_mode_; + grn_obj column_name_; + grn_obj value_; + + bool is_convertable(const Item_cond *cond_item); + bool is_convertable(const Item_func *func_item); + bool is_convertable_binary_operation(const Item_field *field_item, + Item *value_item, + Item_func::Functype func_type); + bool is_convertable_between(const Item_field *field_item, + Item *min_item, + Item *max_item); + bool is_valid_time_value(const Item_field *field_item, + Item *value_item); + bool get_time_value(const Item_field *field_item, + Item *value_item, + MYSQL_TIME *mysql_time); + bool have_index(const Item_field *field_item, grn_operator _operator); + bool have_index(const Item_field *field_item, Item_func::Functype func_type); + + NormalizedType normalize_field_type(enum_field_types field_type); + + void convert_binary_operation(const Item_func *func_item, + grn_obj *expression, + grn_operator _operator); + void convert_between(const Item_func *func_item, grn_obj *expression); + void append_field_value(const Item_field *field_item, + grn_obj *expression); + void append_const_item(const Item_field *field_item, + Item *const_item, + grn_obj *expression); + }; +} + +#endif /* MRN_CONDITION_CONVERTER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_context_pool.cpp b/storage/mroonga/lib/mrn_context_pool.cpp new file mode 100644 index 00000000..bbc239da --- /dev/null +++ b/storage/mroonga/lib/mrn_context_pool.cpp @@ -0,0 +1,120 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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_context_pool.hpp" +#include "mrn_lock.hpp" + +#include <time.h> + +namespace mrn { + // for debug +#define MRN_CLASS_NAME "mrn::ContextPool::Impl" + + class ContextPool::Impl { + public: + Impl(mysql_mutex_t *mutex) + : mutex_(mutex), + pool_(NULL), + last_pull_time_(0) { + } + + ~Impl(void) { + clear(); + } + + grn_ctx *pull(void) { + MRN_DBUG_ENTER_METHOD(); + grn_ctx *ctx = NULL; + + { + time_t now; + time(&now); + + mrn::Lock lock(mutex_); + if (pool_) { + ctx = static_cast<grn_ctx *>(pool_->data); + list_pop(pool_); + if ((uint) (now - last_pull_time_) >= CLEAR_THREATHOLD_IN_SECONDS) { + clear(); + } + } + last_pull_time_ = now; + } + + if (!ctx) { + ctx = grn_ctx_open(0); + } + + DBUG_RETURN(ctx); + } + + void release(grn_ctx *ctx) { + MRN_DBUG_ENTER_METHOD(); + + { + mrn::Lock lock(mutex_); + list_push(pool_, ctx); + grn_ctx_use(ctx, NULL); + } + + DBUG_VOID_RETURN; + } + + private: + static const int CLEAR_THREATHOLD_IN_SECONDS = 60 * 5; + + mysql_mutex_t *mutex_; + LIST *pool_; + time_t last_pull_time_; + + void clear(void) { + MRN_DBUG_ENTER_METHOD(); + while (pool_) { + grn_ctx *ctx = static_cast<grn_ctx *>(pool_->data); + grn_ctx_close(ctx); + list_pop(pool_); + } + DBUG_VOID_RETURN; + } + }; + + // For debug +#undef MRN_CLASS_NAME +#define MRN_CLASS_NAME "mrn::ContextPool" + + ContextPool::ContextPool(mysql_mutex_t *mutex) + : impl_(new Impl(mutex)) { + } + + ContextPool::~ContextPool(void) { + delete impl_; + } + + grn_ctx *ContextPool::pull(void) { + MRN_DBUG_ENTER_METHOD(); + grn_ctx *ctx = impl_->pull(); + DBUG_RETURN(ctx); + } + + void ContextPool::release(grn_ctx *ctx) { + MRN_DBUG_ENTER_METHOD(); + impl_->release(ctx); + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_context_pool.hpp b/storage/mroonga/lib/mrn_context_pool.hpp new file mode 100644 index 00000000..22b4fed8 --- /dev/null +++ b/storage/mroonga/lib/mrn_context_pool.hpp @@ -0,0 +1,41 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_CONTEXT_POOL_HPP_ +#define MRN_CONTEXT_POOL_HPP_ + +#include <mrn_mysql.h> + +#include <groonga.h> + +namespace mrn { + class ContextPool { + public: + ContextPool(mysql_mutex_t *mutex); + ~ContextPool(void); + grn_ctx *pull(void); + void release(grn_ctx *context); + + private: + class Impl; + Impl *impl_; + }; +} + +#endif /* MRN_CONTEXT_POOL_HPP_ */ diff --git a/storage/mroonga/lib/mrn_count_skip_checker.cpp b/storage/mroonga/lib/mrn_count_skip_checker.cpp new file mode 100644 index 00000000..dfa1a11d --- /dev/null +++ b/storage/mroonga/lib/mrn_count_skip_checker.cpp @@ -0,0 +1,303 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2017 Kouhei Sutou <kou@clear-code.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_count_skip_checker.hpp" + +#include <item_sum.h> + +// for debug +#define MRN_CLASS_NAME "mrn::CountSkipChecker" + +namespace mrn { + CountSkipChecker::CountSkipChecker(grn_ctx *ctx, + TABLE *table, + SELECT_LEX *select_lex, + KEY *key_info, + key_part_map target_key_part_map, + bool is_storage_mode) + : ctx_(ctx), + table_(table), + select_lex_(select_lex), + key_info_(key_info), + target_key_part_map_(target_key_part_map), + is_storage_mode_(is_storage_mode) { + } + + CountSkipChecker::~CountSkipChecker() { + } + + bool CountSkipChecker::check() { + MRN_DBUG_ENTER_METHOD(); + + if (select_lex_->item_list.elements != 1) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] not only one item: %u", + select_lex_->item_list.elements); + DBUG_RETURN(false); + } + if (select_lex_->group_list.elements > 0) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] have groups: %u", + select_lex_->group_list.elements); + DBUG_RETURN(false); + } + if (MRN_SELECT_LEX_GET_HAVING_COND(select_lex_)) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] have HAVING"); + DBUG_RETURN(false); + } + if (select_lex_->table_list.elements != 1) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] not only one table: %u", + select_lex_->table_list.elements); + DBUG_RETURN(false); + } + + Item *info = static_cast<Item *>(select_lex_->item_list.first_node()->info); + if (info->type() != Item::SUM_FUNC_ITEM) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] item isn't sum function: %u", + info->type()); + DBUG_RETURN(false); + } + Item_sum *sum_item = static_cast<Item_sum *>(info); + if (sum_item->sum_func() != Item_sum::COUNT_FUNC) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] not COUNT: %u", + sum_item->sum_func()); + DBUG_RETURN(false); + } + if (ITEM_SUM_GET_NEST_LEVEL(sum_item) != 0 || + ITEM_SUM_GET_AGGR_LEVEL(sum_item) != 0 || + ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item) != -1 || + sum_item->max_sum_func_level != -1) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] not simple COUNT(*): %d:%d:%d:%d", + ITEM_SUM_GET_NEST_LEVEL(sum_item), + ITEM_SUM_GET_AGGR_LEVEL(sum_item), + ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item), + sum_item->max_sum_func_level); + DBUG_RETURN(false); + } + + Item *where = MRN_SELECT_LEX_GET_WHERE_COND(select_lex_); + if (!where) { + if (is_storage_mode_) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][true] no condition"); + DBUG_RETURN(true); + } else { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] no condition with wrapper mode"); + DBUG_RETURN(false); + } + } + + bool skippable = is_skippable(where); + DBUG_RETURN(skippable); + } + + bool CountSkipChecker::is_skippable(Item *where) { + MRN_DBUG_ENTER_METHOD(); + + bool skippable = false; + switch (where->type()) { + case Item::COND_ITEM: + { + Item_cond *cond_item = static_cast<Item_cond *>(where); + skippable = is_skippable(cond_item); + if (skippable) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][true] skippable multiple conditions"); + } + } + break; + case Item::FUNC_ITEM: + { + Item_func *func_item = static_cast<Item_func *>(where); + if (func_item->functype() == Item_func::FT_FUNC) { + if (select_lex_->select_n_where_fields == 1) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][true] " + "only one full text search condition"); + DBUG_RETURN(true); + } else { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] " + "full text search condition and more conditions: %u", + select_lex_->select_n_where_fields); + DBUG_RETURN(false); + } + } else { + skippable = is_skippable(func_item); + if (skippable) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][true] skippable condition"); + } + } + } + break; + default: + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] unsupported top level item: %u", + where->type()); + break; + } + + DBUG_RETURN(skippable); + } + + bool CountSkipChecker::is_skippable(Item_cond *cond_item) { + MRN_DBUG_ENTER_METHOD(); + + List_iterator<Item> iterator(*(cond_item->argument_list())); + Item *sub_item; + while ((sub_item = iterator++)) { + if (sub_item->type() != Item::FUNC_ITEM) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] " + "sub condition isn't function item: %u", + sub_item->type()); + DBUG_RETURN(false); + } + if (!is_skippable(static_cast<Item_func *>(sub_item))) { + DBUG_RETURN(false); + } + } + DBUG_RETURN(true); + } + + bool CountSkipChecker::is_skippable(Item_func *func_item) { + MRN_DBUG_ENTER_METHOD(); + + switch (func_item->functype()) { + case Item_func::EQ_FUNC: + case Item_func::EQUAL_FUNC: + case Item_func::NE_FUNC: + case Item_func::LT_FUNC: + case Item_func::LE_FUNC: + case Item_func::GE_FUNC: + case Item_func::GT_FUNC: + { + Item **arguments = func_item->arguments(); + Item *left_item = arguments[0]; + if (left_item->type() != Item::FIELD_ITEM) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] not field: %u:%u", + func_item->functype(), + left_item->type()); + DBUG_RETURN(false); + } + + bool skippable = is_skippable(static_cast<Item_field *>(left_item)); + DBUG_RETURN(skippable); + } + break; + case Item_func::BETWEEN: + { + Item **arguments = func_item->arguments(); + Item *target_item = arguments[0]; + if (target_item->type() != Item::FIELD_ITEM) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] BETWEEN target isn't field: %u", + target_item->type()); + DBUG_RETURN(false); + } + + bool skippable = is_skippable(static_cast<Item_field *>(target_item)); + DBUG_RETURN(skippable); + } + break; + case Item_func::MULT_EQUAL_FUNC: +#ifdef MRN_HAVE_ITEM_EQUAL_FIELDS_ITERATOR + { + Item_equal *equal_item = static_cast<Item_equal *>(func_item); + Item_equal_fields_iterator iterator(*equal_item); + Item *field_item; + while ((field_item = iterator++)) { + bool skippable = is_skippable(static_cast<Item_field *>(field_item)); + if (!skippable) { + DBUG_RETURN(skippable); + } + } + DBUG_RETURN(true); + } +#endif + break; + default: + break; + } + + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] unsupported function item: %u", + func_item->functype()); + DBUG_RETURN(false); + } + + bool CountSkipChecker::is_skippable(Item_field *field_item) { + MRN_DBUG_ENTER_METHOD(); + + Field *field = field_item->field; + if (!field) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] field is missing"); + DBUG_RETURN(false); + } + + if (field->table != table_) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] external table's field"); + DBUG_RETURN(false); + } + + if (!key_info_) { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] no active index: <%s>:<%s>", + *(field->table_name), + field->field_name.str); + DBUG_RETURN(false); + } + + uint i; + KEY_PART_INFO *key_part = key_info_->key_part; + for (i = 0; i < KEY_N_KEY_PARTS(key_info_); i++) { + if (key_part[i].field == field) { + if ((target_key_part_map_ >> i) & 1) { + DBUG_RETURN(true); + } else { + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] " + "field's index are out of key part map: %u:%lu: <%s>:<%s>", + i, + target_key_part_map_, + *(field->table_name), + field->field_name.str); + DBUG_RETURN(false); + } + } + } + + GRN_LOG(ctx_, GRN_LOG_DEBUG, + "[mroonga][count-skip][false] field isn't indexed: <%s>:<%s>", + *(field->table_name), + field->field_name.str); + DBUG_RETURN(false); + } +} diff --git a/storage/mroonga/lib/mrn_count_skip_checker.hpp b/storage/mroonga/lib/mrn_count_skip_checker.hpp new file mode 100644 index 00000000..8ea93cd3 --- /dev/null +++ b/storage/mroonga/lib/mrn_count_skip_checker.hpp @@ -0,0 +1,57 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_COUNT_SKIP_CHECKER_HPP_ +#define MRN_COUNT_SKIP_CHECKER_HPP_ + +#include <mrn_mysql_compat.h> + +#include <item_cmpfunc.h> + +#include <groonga.h> + +namespace mrn { + class CountSkipChecker { + public: + CountSkipChecker(grn_ctx *ctx, + TABLE *table, + SELECT_LEX *select_lex, + KEY *key_info, + key_part_map target_key_part_map, + bool is_storage_mode); + ~CountSkipChecker(); + + bool check(); + + private: + grn_ctx *ctx_; + TABLE *table_; + SELECT_LEX *select_lex_; + KEY *key_info_; + key_part_map target_key_part_map_; + bool is_storage_mode_; + + bool is_skippable(Item *where); + bool is_skippable(Item_cond *cond_item); + bool is_skippable(Item_func *func_item); + bool is_skippable(Item_field *field_item); + }; +} + +#endif /* MRN_COUNT_SKIP_CHECKER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_current_thread.hpp b/storage/mroonga/lib/mrn_current_thread.hpp new file mode 100644 index 00000000..ee494f76 --- /dev/null +++ b/storage/mroonga/lib/mrn_current_thread.hpp @@ -0,0 +1,27 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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 +*/ + +#pragma once + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +#if (!defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 80002) +# include <current_thd.h> +#endif diff --git a/storage/mroonga/lib/mrn_database.cpp b/storage/mroonga/lib/mrn_database.cpp new file mode 100644 index 00000000..12f0b348 --- /dev/null +++ b/storage/mroonga/lib/mrn_database.cpp @@ -0,0 +1,89 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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_database.hpp" +#include "mrn_operations.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::Database" + +namespace mrn { + Database::Database(grn_ctx *ctx, grn_obj *db) + : ctx_(ctx), + db_(db), + broken_table_names_(NULL), + is_broken_(false) { + Operations operations(ctx_); + broken_table_names_ = operations.collect_processing_table_names(); + is_broken_ = operations.is_locked(); + } + + Database::~Database(void) { + close(); + } + + void Database::close() { + MRN_DBUG_ENTER_METHOD(); + if (db_) { + grn_hash_close(ctx_, broken_table_names_); + broken_table_names_ = NULL; + grn_obj_close(ctx_, db_); + db_ = NULL; + } + DBUG_VOID_RETURN; + } + + grn_rc Database::remove() { + MRN_DBUG_ENTER_METHOD(); + grn_rc rc = GRN_SUCCESS; + if (db_) { + grn_hash_close(ctx_, broken_table_names_); + broken_table_names_ = NULL; + rc = grn_obj_remove(ctx_, db_); + if (rc == GRN_SUCCESS) { + db_ = NULL; + } + } + DBUG_RETURN(rc); + } + + grn_obj *Database::get() { + MRN_DBUG_ENTER_METHOD(); + DBUG_RETURN(db_); + } + + bool Database::is_broken() { + MRN_DBUG_ENTER_METHOD(); + DBUG_RETURN(is_broken_); + } + + bool Database::is_broken_table(const char *name, size_t name_size) { + MRN_DBUG_ENTER_METHOD(); + grn_id id = grn_hash_get(ctx_, broken_table_names_, name, name_size, NULL); + DBUG_RETURN(id != GRN_ID_NIL); + } + + void Database::mark_table_repaired(const char *name, size_t name_size) { + MRN_DBUG_ENTER_METHOD(); + grn_hash_delete(ctx_, broken_table_names_, name, name_size, NULL); + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_database.hpp b/storage/mroonga/lib/mrn_database.hpp new file mode 100644 index 00000000..cf9f9d4a --- /dev/null +++ b/storage/mroonga/lib/mrn_database.hpp @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_DATABASE_HPP_ +#define MRN_DATABASE_HPP_ + +#include <groonga.h> + +namespace mrn { + class Database { + public: + Database(grn_ctx *ctx, grn_obj *db); + ~Database(void); + + void close(); + grn_rc remove(); + grn_obj *get(); + + bool is_broken(); + bool is_broken_table(const char *name, size_t name_size); + void mark_table_repaired(const char *name, size_t name_size); + + private: + grn_ctx *ctx_; + grn_obj *db_; + grn_hash *broken_table_names_; + bool is_broken_; + }; +} + +#endif /* MRN_DATABASE_HPP_ */ diff --git a/storage/mroonga/lib/mrn_database_manager.cpp b/storage/mroonga/lib/mrn_database_manager.cpp new file mode 100644 index 00000000..149c556f --- /dev/null +++ b/storage/mroonga/lib/mrn_database_manager.cpp @@ -0,0 +1,364 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010 Tetsuro IKEDA + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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_database_manager.hpp" +#include "mrn_encoding.hpp" +#include "mrn_lock.hpp" +#include "mrn_path_mapper.hpp" + +#include <groonga/plugin.h> + +// for debug +#define MRN_CLASS_NAME "mrn::DatabaseManager" + +#ifdef WIN32 +# include <direct.h> +# define MRN_MKDIR(pathname, mode) _mkdir((pathname)) +#else +# include <dirent.h> +# include <unistd.h> +# define MRN_MKDIR(pathname, mode) mkdir((pathname), (mode)) +#endif + +extern "C" { + grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(grn_ctx *ctx); + grn_rc GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(grn_ctx *ctx); +} + +namespace mrn { + DatabaseManager::DatabaseManager(grn_ctx *ctx, mysql_mutex_t *mutex) + : ctx_(ctx), + cache_(NULL), + mutex_(mutex) { + } + + DatabaseManager::~DatabaseManager(void) { + if (cache_) { + void *db_address; + GRN_HASH_EACH(ctx_, cache_, id, NULL, 0, &db_address, { + Database *db; + memcpy(&db, db_address, sizeof(grn_obj *)); + delete db; + }); + grn_hash_close(ctx_, cache_); + } + } + + bool DatabaseManager::init(void) { + MRN_DBUG_ENTER_METHOD(); + cache_ = grn_hash_create(ctx_, + NULL, + GRN_TABLE_MAX_KEY_SIZE, + sizeof(grn_obj *), + GRN_OBJ_KEY_VAR_SIZE); + if (!cache_) { + GRN_LOG(ctx_, GRN_LOG_ERROR, + "failed to initialize hash table for caching opened databases"); + DBUG_RETURN(false); + } + + DBUG_RETURN(true); + } + + int DatabaseManager::open(const char *path, Database **db) { + MRN_DBUG_ENTER_METHOD(); + + int error = 0; + *db = NULL; + + mrn::PathMapper mapper(path); + mrn::Lock lock(mutex_); + + error = mrn::encoding::set(ctx_, system_charset_info); + if (error) { + DBUG_RETURN(error); + } + + grn_id id; + void *db_address; + id = grn_hash_get(ctx_, cache_, + mapper.db_name(), strlen(mapper.db_name()), + &db_address); + if (id == GRN_ID_NIL) { + grn_obj *grn_db; + struct stat db_stat; + if (stat(mapper.db_path(), &db_stat)) { + GRN_LOG(ctx_, GRN_LOG_INFO, + "database not found. creating...: <%s>", mapper.db_path()); + if (path[0] == FN_CURLIB && + mrn_is_directory_separator(path[1])) { + ensure_database_directory(); + } + grn_db = grn_db_create(ctx_, mapper.db_path(), NULL); + if (ctx_->rc) { + error = ER_CANT_CREATE_TABLE; + my_message(error, ctx_->errbuf, MYF(0)); + DBUG_RETURN(error); + } + } else { + grn_db = grn_db_open(ctx_, mapper.db_path()); + if (ctx_->rc) { + error = ER_CANT_OPEN_FILE; + my_message(error, ctx_->errbuf, MYF(0)); + DBUG_RETURN(error); + } + } + *db = new Database(ctx_, grn_db); + grn_hash_add(ctx_, cache_, + mapper.db_name(), strlen(mapper.db_name()), + &db_address, NULL); + memcpy(db_address, db, sizeof(Database *)); + error = ensure_normalizers_registered((*db)->get()); + if (!error) { + if ((*db)->is_broken()) { + error = ER_CANT_OPEN_FILE; + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: database: open: " + "The database maybe broken. " + "We recommend you to recreate the database. " + "If the database isn't broken, " + "you can remove this error by running " + "'groonga %s table_remove mroonga_operations' " + "on server. But the latter isn't recommended.", + mapper.db_path()); + my_message(error, error_message, MYF(0)); + } + } + } else { + memcpy(db, db_address, sizeof(Database *)); + grn_ctx_use(ctx_, (*db)->get()); + } + + DBUG_RETURN(error); + } + + void DatabaseManager::close(const char *path) { + MRN_DBUG_ENTER_METHOD(); + + mrn::PathMapper mapper(path); + mrn::Lock lock(mutex_); + + grn_id id; + void *db_address; + id = grn_hash_get(ctx_, cache_, + mapper.db_name(), strlen(mapper.db_name()), + &db_address); + if (id == GRN_ID_NIL) { + DBUG_VOID_RETURN; + } + + Database *db = NULL; + memcpy(&db, db_address, sizeof(Database *)); + grn_ctx_use(ctx_, db->get()); + if (db) { + delete db; + } + + grn_hash_delete_by_id(ctx_, cache_, id, NULL); + + DBUG_VOID_RETURN; + } + + bool DatabaseManager::drop(const char *path) { + MRN_DBUG_ENTER_METHOD(); + + mrn::PathMapper mapper(path); + mrn::Lock lock(mutex_); + + grn_id id; + void *db_address; + id = grn_hash_get(ctx_, cache_, + mapper.db_name(), strlen(mapper.db_name()), + &db_address); + + Database *db = NULL; + if (id == GRN_ID_NIL) { + struct stat dummy; + if (stat(mapper.db_path(), &dummy) == 0) { + grn_obj *grn_db = grn_db_open(ctx_, mapper.db_path()); + db = new Database(ctx_, grn_db); + } + } else { + memcpy(&db, db_address, sizeof(Database *)); + grn_ctx_use(ctx_, db->get()); + } + + if (!db) { + DBUG_RETURN(false); + } + + if (db->remove() == GRN_SUCCESS) { + if (id != GRN_ID_NIL) { + grn_hash_delete_by_id(ctx_, cache_, id, NULL); + } + delete db; + DBUG_RETURN(true); + } else { + GRN_LOG(ctx_, GRN_LOG_ERROR, + "failed to drop database: <%s>: <%s>", + mapper.db_path(), ctx_->errbuf); + if (id == GRN_ID_NIL) { + delete db; + } + DBUG_RETURN(false); + } + } + + int DatabaseManager::clear(void) { + MRN_DBUG_ENTER_METHOD(); + + int error = 0; + + mrn::Lock lock(mutex_); + + grn_hash_cursor *cursor; + cursor = grn_hash_cursor_open(ctx_, cache_, + 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); + } + + while (grn_hash_cursor_next(ctx_, cursor) != GRN_ID_NIL) { + if (ctx_->rc) { + error = ER_ERROR_ON_READ; + my_message(error, ctx_->errbuf, MYF(0)); + break; + } + void *db_address; + Database *db; + grn_hash_cursor_get_value(ctx_, cursor, &db_address); + memcpy(&db, db_address, sizeof(Database *)); + grn_ctx_use(ctx_, db->get()); + grn_rc rc = grn_hash_cursor_delete(ctx_, cursor, NULL); + if (rc) { + error = ER_ERROR_ON_READ; + my_message(error, ctx_->errbuf, MYF(0)); + break; + } + delete db; + } + grn_hash_cursor_close(ctx_, cursor); + + DBUG_RETURN(error); + } + + const char *DatabaseManager::error_message() { + MRN_DBUG_ENTER_METHOD(); + DBUG_RETURN(ctx_->errbuf); + } + + void DatabaseManager::mkdir_p(const char *directory) { + MRN_DBUG_ENTER_METHOD(); + + int i = 0; + char sub_directory[MRN_MAX_PATH_SIZE]; + sub_directory[0] = '\0'; + while (true) { + if (mrn_is_directory_separator(directory[i]) || + directory[i] == '\0') { + sub_directory[i] = '\0'; + struct stat directory_status; + if (stat(sub_directory, &directory_status) != 0) { + DBUG_PRINT("info", ("mroonga: creating directory: <%s>", sub_directory)); + GRN_LOG(ctx_, GRN_LOG_INFO, "creating directory: <%s>", sub_directory); + if (MRN_MKDIR(sub_directory, S_IRWXU) == 0) { + DBUG_PRINT("info", + ("mroonga: created directory: <%s>", sub_directory)); + GRN_LOG(ctx_, GRN_LOG_INFO, "created directory: <%s>", sub_directory); + } else { + DBUG_PRINT("error", + ("mroonga: failed to create directory: <%s>: <%s>", + sub_directory, strerror(errno))); + GRN_LOG(ctx_, GRN_LOG_ERROR, + "failed to create directory: <%s>: <%s>", + sub_directory, strerror(errno)); + DBUG_VOID_RETURN; + } + } + } + + if (directory[i] == '\0') { + break; + } + + sub_directory[i] = directory[i]; + ++i; + } + + DBUG_VOID_RETURN; + } + + void DatabaseManager::ensure_database_directory(void) { + MRN_DBUG_ENTER_METHOD(); + + const char *path_prefix = mrn::PathMapper::default_path_prefix; + if (!path_prefix) + DBUG_VOID_RETURN; + + const char *last_path_separator; + last_path_separator = strrchr(path_prefix, FN_LIBCHAR); +#ifdef FN_LIBCHAR2 + if (!last_path_separator) + last_path_separator = strrchr(path_prefix, FN_LIBCHAR2); +#endif + if (!last_path_separator) + DBUG_VOID_RETURN; + if (path_prefix == last_path_separator) + DBUG_VOID_RETURN; + + char database_directory[MRN_MAX_PATH_SIZE]; + size_t database_directory_length = last_path_separator - path_prefix; + strncpy(database_directory, path_prefix, database_directory_length); + database_directory[database_directory_length] = '\0'; + mkdir_p(database_directory); + + DBUG_VOID_RETURN; + } + + int DatabaseManager::ensure_normalizers_registered(grn_obj *db) { + MRN_DBUG_ENTER_METHOD(); + + int error = 0; +#ifdef WITH_GROONGA_NORMALIZER_MYSQL + { +# ifdef MRN_GROONGA_NORMALIZER_MYSQL_EMBEDDED + GRN_PLUGIN_IMPL_NAME_TAGGED(init, normalizers_mysql)(ctx_); + GRN_PLUGIN_IMPL_NAME_TAGGED(register, normalizers_mysql)(ctx_); +# else + grn_obj *mysql_normalizer; + mysql_normalizer = grn_ctx_get(ctx_, "NormalizerMySQLGeneralCI", -1); + if (mysql_normalizer) { + grn_obj_unlink(ctx_, mysql_normalizer); + } else { + grn_plugin_register(ctx_, GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME); + } +# endif + } +#endif + + DBUG_RETURN(error); + } +} diff --git a/storage/mroonga/lib/mrn_database_manager.hpp b/storage/mroonga/lib/mrn_database_manager.hpp new file mode 100644 index 00000000..05383af6 --- /dev/null +++ b/storage/mroonga/lib/mrn_database_manager.hpp @@ -0,0 +1,52 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010 Tetsuro IKEDA + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_DATABASE_MANAGER_HPP_ +#define MRN_DATABASE_MANAGER_HPP_ + +#include "mrn_database.hpp" + +#include <groonga.h> + +namespace mrn { + class DatabaseManager { + public: + DatabaseManager(grn_ctx *ctx, mysql_mutex_t *mutex); + ~DatabaseManager(void); + bool init(void); + int open(const char *path, Database **db); + void close(const char *path); + bool drop(const char *path); + int clear(void); + const char *error_message(); + + private: + grn_ctx *ctx_; + grn_hash *cache_; + mysql_mutex_t *mutex_; + + void mkdir_p(const char *directory); + void ensure_database_directory(void); + int ensure_normalizers_registered(grn_obj *db); + }; +} + +#endif /* MRN_DATABASE_MANAGER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_database_repairer.cpp b/storage/mroonga/lib/mrn_database_repairer.cpp new file mode 100644 index 00000000..c0c4a90e --- /dev/null +++ b/storage/mroonga/lib/mrn_database_repairer.cpp @@ -0,0 +1,300 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2017 Kouhei Sutou <kou@clear-code.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> +#include <mrn_constants.hpp> + +#include "mrn_database_repairer.hpp" +#include "mrn_path_mapper.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::DatabaseRepairer" + +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +#ifndef WIN32 +# include <dirent.h> +#endif + +namespace mrn { + struct CheckResult { + CheckResult() : + is_crashed(false), + is_corrupt(false) { + } + + bool is_crashed; + bool is_corrupt; + }; + + DatabaseRepairer::DatabaseRepairer(grn_ctx *ctx, THD *thd) + : ctx_(ctx), + thd_(thd), + base_directory_(NULL), + base_directory_buffer_(), + path_prefix_(NULL), + path_prefix_buffer_(), + path_prefix_length_(0), + mrn_db_file_suffix_length_(strlen(MRN_DB_FILE_SUFFIX)) { + } + + DatabaseRepairer::~DatabaseRepairer() { + } + + bool DatabaseRepairer::is_crashed(void) { + MRN_DBUG_ENTER_METHOD(); + + CheckResult result; + each_database(&DatabaseRepairer::check_body, &result); + + DBUG_RETURN(result.is_crashed); + } + + bool DatabaseRepairer::is_corrupt(void) { + MRN_DBUG_ENTER_METHOD(); + + CheckResult result; + each_database(&DatabaseRepairer::check_body, &result); + + DBUG_RETURN(result.is_corrupt); + } + + bool DatabaseRepairer::repair(void) { + MRN_DBUG_ENTER_METHOD(); + + bool succeeded = true; + each_database(&DatabaseRepairer::repair_body, &succeeded); + + DBUG_RETURN(succeeded); + } + + void DatabaseRepairer::each_database(EachBodyFunc each_body_func, + void *user_data) { + MRN_DBUG_ENTER_METHOD(); + + detect_paths(); + +#ifdef WIN32 + WIN32_FIND_DATA data; + HANDLE finder = FindFirstFile(base_directory_, &data); + if (finder == INVALID_HANDLE_VALUE) { + DBUG_VOID_RETURN; + } + + grn_ctx ctx; + grn_rc rc = grn_ctx_init(&ctx, 0); + if (rc == GRN_SUCCESS) { + do { + each_database_body(data.cFileName, &ctx, each_body_func, user_data); + } while (FindNextFile(finder, &data) != 0); + grn_ctx_fin(&ctx); + } else { + GRN_LOG(ctx_, GRN_LOG_WARNING, + "[mroonga][database][repairer][each] " + "failed to initialize grn_ctx: %d: %s", + rc, grn_rc_to_string(rc)); + } + FindClose(finder); +#else + DIR *dir = opendir(base_directory_); + if (!dir) { + DBUG_VOID_RETURN; + } + + grn_ctx ctx; + grn_rc rc = grn_ctx_init(&ctx, 0); + if (rc == GRN_SUCCESS) { + while (struct dirent *entry = readdir(dir)) { + each_database_body(entry->d_name, &ctx, each_body_func, user_data); + } + grn_ctx_fin(&ctx); + } else { + GRN_LOG(ctx_, GRN_LOG_WARNING, + "[mroonga][database][repairer][each] " + "failed to initialize grn_ctx: %d: %s", + rc, grn_rc_to_string(rc)); + } + closedir(dir); +#endif + + DBUG_VOID_RETURN; + } + + void DatabaseRepairer::each_database_body(const char *base_path, + grn_ctx *ctx, + EachBodyFunc each_body_func, + void *user_data) { + MRN_DBUG_ENTER_METHOD(); + + if (path_prefix_length_ > 0 && + strncmp(base_path, path_prefix_, path_prefix_length_) != 0) { + DBUG_VOID_RETURN; + } + + size_t path_length = strlen(base_path); + if (path_length <= mrn_db_file_suffix_length_) { + DBUG_VOID_RETURN; + } + + if (strncmp(base_path + (path_length - mrn_db_file_suffix_length_), + MRN_DB_FILE_SUFFIX, mrn_db_file_suffix_length_) != 0) { + DBUG_VOID_RETURN; + } + + char db_path[MRN_MAX_PATH_SIZE]; + snprintf(db_path, MRN_MAX_PATH_SIZE, + "%s%c%s", base_directory_, FN_LIBCHAR, base_path); + grn_obj *db = grn_db_open(ctx, db_path); + if (!db) { + DBUG_VOID_RETURN; + } + + (this->*each_body_func)(ctx, db, db_path, user_data); + + grn_obj_close(ctx, db); + + DBUG_VOID_RETURN; + } + + void DatabaseRepairer::detect_paths(void) { + MRN_DBUG_ENTER_METHOD(); + + const char *raw_path_prefix = mrn::PathMapper::default_path_prefix; + + if (!raw_path_prefix) { + base_directory_ = "."; + path_prefix_ = NULL; + DBUG_VOID_RETURN; + } + + strcpy(base_directory_buffer_, raw_path_prefix); + size_t raw_path_prefix_length = strlen(raw_path_prefix); + size_t separator_position = raw_path_prefix_length; + for (; separator_position > 0; separator_position--) { + if (mrn_is_directory_separator(base_directory_buffer_[separator_position])) { + break; + } + } + if (separator_position == 0 || + separator_position == raw_path_prefix_length) { + base_directory_ = "."; + } else { + base_directory_buffer_[separator_position] = '\0'; + base_directory_ = base_directory_buffer_; + strcpy(path_prefix_buffer_, raw_path_prefix + separator_position + 1); + path_prefix_ = path_prefix_buffer_; + path_prefix_length_ = strlen(path_prefix_); + } + + DBUG_VOID_RETURN; + } + + void DatabaseRepairer::check_body(grn_ctx *ctx, + grn_obj *db, + const char *db_path, + void *user_data) { + MRN_DBUG_ENTER_METHOD(); + + CheckResult *result = static_cast<CheckResult *>(user_data); + + if (grn_obj_is_locked(ctx, db)) { + result->is_crashed = true; + result->is_corrupt = true; + DBUG_VOID_RETURN; + } + + grn_table_cursor *cursor; + cursor = grn_table_cursor_open(ctx, db, + NULL, 0, + NULL, 0, + 0, -1, GRN_CURSOR_BY_ID); + if (!cursor) { + result->is_crashed = true; + result->is_corrupt = true; + DBUG_VOID_RETURN; + } + + grn_id id; + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + grn_obj *object = grn_ctx_at(ctx, id); + + if (!object) { + if (ctx->rc == GRN_SUCCESS) { + continue; + } else { + result->is_corrupt = true; + break; + } + } + + switch (object->header.type) { + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: + case GRN_COLUMN_FIX_SIZE: + case GRN_COLUMN_VAR_SIZE: + case GRN_COLUMN_INDEX: + if (grn_obj_is_locked(ctx_, object)) { + result->is_crashed = true; + result->is_corrupt = true; + } + break; + default: + break; + } + + grn_obj_unlink(ctx, object); + + if (result->is_crashed || result->is_corrupt) { + break; + } + } + grn_table_cursor_close(ctx, cursor); + + DBUG_VOID_RETURN; + } + + void DatabaseRepairer::repair_body(grn_ctx *ctx, + grn_obj *db, + const char *db_path, + void *user_data) { + MRN_DBUG_ENTER_METHOD(); + + bool *succeeded = static_cast<bool *>(user_data); + if (grn_db_recover(ctx, db) != GRN_SUCCESS) { + push_warning_printf(thd_, + MRN_SEVERITY_WARNING, + ER_NOT_KEYFILE, + "mroonga: repair: " + "Failed to recover database: <%s>: <%s>", + db_path, ctx->errbuf); + *succeeded = false; + } + + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_database_repairer.hpp b/storage/mroonga/lib/mrn_database_repairer.hpp new file mode 100644 index 00000000..b56c2744 --- /dev/null +++ b/storage/mroonga/lib/mrn_database_repairer.hpp @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2017 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_DATABASE_REPAIRER_HPP_ +#define MRN_DATABASE_REPAIRER_HPP_ + +#include <groonga.h> + +namespace mrn { + class DatabaseRepairer { + public: + DatabaseRepairer(grn_ctx *ctx, THD *thd); + ~DatabaseRepairer(void); + bool is_crashed(void); + bool is_corrupt(void); + bool repair(void); + + private: + grn_ctx *ctx_; + THD *thd_; + const char *base_directory_; + char base_directory_buffer_[MRN_MAX_PATH_SIZE]; + const char *path_prefix_; + char path_prefix_buffer_[MRN_MAX_PATH_SIZE]; + size_t path_prefix_length_; + size_t mrn_db_file_suffix_length_; + + typedef void (DatabaseRepairer::*EachBodyFunc)(grn_ctx *ctx, + grn_obj *db, + const char *db_path, + void *user_data); + + void each_database(EachBodyFunc each_body_func, void *user_data); + void each_database_body(const char *base_path, + grn_ctx *ctx, + EachBodyFunc each_body_func, + void *user_data); + void detect_paths(void); + + void check_body(grn_ctx *ctx, + grn_obj *db, + const char *db_path, + void *user_data); + void repair_body(grn_ctx *ctx, + grn_obj *db, + const char *db_path, + void *user_data); + }; +} + +#endif /* MRN_DATABASE_REPAIRER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_debug_column_access.cpp b/storage/mroonga/lib/mrn_debug_column_access.cpp new file mode 100644 index 00000000..cb2ce7e3 --- /dev/null +++ b/storage/mroonga/lib/mrn_debug_column_access.cpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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_debug_column_access.hpp" + +namespace mrn { + DebugColumnAccess::DebugColumnAccess(TABLE *table, MY_BITMAP **bitmap) + : table_(table), + bitmap_(bitmap) { +#ifdef DBUG_ASSERT_EXISTS + map_ = dbug_tmp_use_all_columns(table_, bitmap_); +#endif + } + + DebugColumnAccess::~DebugColumnAccess() { +#ifdef DBUG_ASSERT_EXISTS + dbug_tmp_restore_column_map(bitmap_, map_); +#endif + } +} diff --git a/storage/mroonga/lib/mrn_debug_column_access.hpp b/storage/mroonga/lib/mrn_debug_column_access.hpp new file mode 100644 index 00000000..954e0413 --- /dev/null +++ b/storage/mroonga/lib/mrn_debug_column_access.hpp @@ -0,0 +1,38 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_DEBUG_COLUMN_ACCESS_HPP_ +#define MRN_DEBUG_COLUMN_ACCESS_HPP_ + +#include <mrn_mysql.h> + +namespace mrn { + class DebugColumnAccess { + TABLE *table_; + MY_BITMAP **bitmap_; +#ifdef DBUG_ASSERT_EXISTS + MY_BITMAP *map_; +#endif + public: + DebugColumnAccess(TABLE *table, MY_BITMAP **bitmap); + ~DebugColumnAccess(); + }; +} + +#endif // MRN_DEBUG_COLUMN_ACCESS_HPP_ diff --git a/storage/mroonga/lib/mrn_encoding.cpp b/storage/mroonga/lib/mrn_encoding.cpp new file mode 100644 index 00000000..1cee6226 --- /dev/null +++ b/storage/mroonga/lib/mrn_encoding.cpp @@ -0,0 +1,242 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com> + Copyright(C) 2011-2013 Kentoku SHIBA + + 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_err.h> +#include "mrn_encoding.hpp" + +namespace mrn { + namespace encoding { + CHARSET_INFO *mrn_charset_utf8 = NULL; + CHARSET_INFO *mrn_charset_utf8mb4 = NULL; + CHARSET_INFO *mrn_charset_binary = NULL; + CHARSET_INFO *mrn_charset_ascii = NULL; + CHARSET_INFO *mrn_charset_latin1_1 = NULL; + CHARSET_INFO *mrn_charset_latin1_2 = NULL; + CHARSET_INFO *mrn_charset_cp932 = NULL; + CHARSET_INFO *mrn_charset_sjis = NULL; + CHARSET_INFO *mrn_charset_eucjpms = NULL; + CHARSET_INFO *mrn_charset_ujis = NULL; + CHARSET_INFO *mrn_charset_koi8r = NULL; + + void init(void) { + CHARSET_INFO **cs; + MRN_DBUG_ENTER_FUNCTION(); + for (cs = all_charsets; cs < all_charsets + MY_ALL_CHARSETS_SIZE; cs++) + { + if (!cs[0]) + continue; + if (!strcmp(cs[0]->csname, "utf8")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_utf8) + mrn_charset_utf8 = cs[0]; + else if (mrn_charset_utf8->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "utf8mb4")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_utf8mb4) + mrn_charset_utf8mb4 = cs[0]; + else if (mrn_charset_utf8mb4->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "binary")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_binary) + mrn_charset_binary = cs[0]; + else if (mrn_charset_binary->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "ascii")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_ascii) + mrn_charset_ascii = cs[0]; + else if (mrn_charset_ascii->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "latin1")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_latin1_1) + mrn_charset_latin1_1 = cs[0]; + else if (mrn_charset_latin1_1->cset != cs[0]->cset) + { + if (!mrn_charset_latin1_2) + mrn_charset_latin1_2 = cs[0]; + else if (mrn_charset_latin1_2->cset != cs[0]->cset) + DBUG_ASSERT(0); + } + continue; + } + if (!strcmp(cs[0]->csname, "cp932")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_cp932) + mrn_charset_cp932 = cs[0]; + else if (mrn_charset_cp932->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "sjis")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_sjis) + mrn_charset_sjis = cs[0]; + else if (mrn_charset_sjis->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "eucjpms")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_eucjpms) + mrn_charset_eucjpms = cs[0]; + else if (mrn_charset_eucjpms->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "ujis")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_ujis) + mrn_charset_ujis = cs[0]; + else if (mrn_charset_ujis->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + if (!strcmp(cs[0]->csname, "koi8r")) + { + DBUG_PRINT("info", ("mroonga: %s is %s [%p]", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + if (!mrn_charset_koi8r) + mrn_charset_koi8r = cs[0]; + else if (mrn_charset_koi8r->cset != cs[0]->cset) + DBUG_ASSERT(0); + continue; + } + DBUG_PRINT("info", ("mroonga: %s[%s][%p] is not supported", + cs[0]->name, cs[0]->csname, cs[0]->cset)); + } + DBUG_VOID_RETURN; + } + + int set(grn_ctx *ctx, const CHARSET_INFO *charset) { + MRN_DBUG_ENTER_FUNCTION(); + int error = 0; + + if (!set_raw(ctx, charset)) { + const char *name = "<null>"; + const char *csname = "<null>"; + if (charset) { + name = charset->name; + csname = charset->csname; + } + error = ER_MRN_CHARSET_NOT_SUPPORT_NUM; + my_printf_error(error, + ER_MRN_CHARSET_NOT_SUPPORT_STR, + MYF(0), name, csname); + } + + DBUG_RETURN(error); + } + + bool set_raw(grn_ctx *ctx, const CHARSET_INFO *charset) { + MRN_DBUG_ENTER_FUNCTION(); + if (!charset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_utf8->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8); + DBUG_RETURN(true); + } + if (mrn_charset_utf8mb4 && charset->cset == mrn_charset_utf8mb4->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_cp932->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_SJIS); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_eucjpms->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_EUC_JP); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_latin1_1->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_LATIN1); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_latin1_2->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_LATIN1); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_koi8r->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_KOI8R); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_binary->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_ascii->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_sjis->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_SJIS); + DBUG_RETURN(true); + } + if (charset->cset == mrn_charset_ujis->cset) + { + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_EUC_JP); + DBUG_RETURN(true); + } + GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE); + DBUG_RETURN(false); + } + } +} diff --git a/storage/mroonga/lib/mrn_encoding.hpp b/storage/mroonga/lib/mrn_encoding.hpp new file mode 100644 index 00000000..f321ca68 --- /dev/null +++ b/storage/mroonga/lib/mrn_encoding.hpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_ENCODING_HPP_ +#define MRN_ENCODING_HPP_ + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +#include <groonga.h> + +namespace mrn { + namespace encoding { + void init(void); + int set(grn_ctx *ctx, const CHARSET_INFO *charset); + bool set_raw(grn_ctx *ctx, const CHARSET_INFO *charset); + } +} + +#endif // MRN_ENCODING_HPP_ diff --git a/storage/mroonga/lib/mrn_external_lock.cpp b/storage/mroonga/lib/mrn_external_lock.cpp new file mode 100644 index 00000000..762a96d0 --- /dev/null +++ b/storage/mroonga/lib/mrn_external_lock.cpp @@ -0,0 +1,43 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kentoku SHIBA + + 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_external_lock.hpp" + +namespace mrn { + ExternalLock::ExternalLock(THD *thd, handler *handler, int lock_type) + : thd_(thd), + handler_(handler), + lock_type_(lock_type) { + if (lock_type_ != F_UNLCK) { + error_ = handler_->ha_external_lock(thd_, lock_type); + } else { + error_ = 0; + } + } + + ExternalLock::~ExternalLock() { + if (lock_type_ != F_UNLCK) { + handler_->ha_external_unlock(thd_); + } + } + + int ExternalLock::error() { + return error_; + } +} diff --git a/storage/mroonga/lib/mrn_external_lock.hpp b/storage/mroonga/lib/mrn_external_lock.hpp new file mode 100644 index 00000000..9bf7e811 --- /dev/null +++ b/storage/mroonga/lib/mrn_external_lock.hpp @@ -0,0 +1,38 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kentoku SHIBA + + 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 +*/ + +#ifndef MRN_EXTERNAL_LOCK_HPP_ +#define MRN_EXTERNAL_LOCK_HPP_ + +#include <mrn_mysql.h> + +namespace mrn { + class ExternalLock { + THD *thd_; + handler *handler_; + int lock_type_; + int error_; + public: + ExternalLock(THD *thd, handler *handler, int lock_type); + ~ExternalLock(); + int error(); + }; +} + +#endif // MRN_EXTERNAL_LOCK_HPP_ diff --git a/storage/mroonga/lib/mrn_field_normalizer.cpp b/storage/mroonga/lib/mrn_field_normalizer.cpp new file mode 100644 index 00000000..bb9982f0 --- /dev/null +++ b/storage/mroonga/lib/mrn_field_normalizer.cpp @@ -0,0 +1,145 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013 Kouhei Sutou <kou@clear-code.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_field_normalizer.hpp" +#include "mrn_encoding.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::FieldNormalizer" + +namespace mrn { + FieldNormalizer::FieldNormalizer(grn_ctx *ctx, THD *thread, Field *field) + : ctx_(ctx), + thread_(thread), + field_(field) { + } + + FieldNormalizer::~FieldNormalizer() { + } + + bool FieldNormalizer::should_normalize() { + MRN_DBUG_ENTER_METHOD(); + + DBUG_PRINT("info", + ("mroonga: result_type = %u", field_->result_type())); + DBUG_PRINT("info", + ("mroonga: charset->name = %s", field_->charset()->name)); + DBUG_PRINT("info", + ("mroonga: charset->csname = %s", field_->charset()->csname)); + DBUG_PRINT("info", + ("mroonga: charset->state = %u", field_->charset()->state)); + bool need_normalize_p; + if (field_->charset()->state & (MY_CS_BINSORT | MY_CS_CSSORT)) { + need_normalize_p = false; + DBUG_PRINT("info", + ("mroonga: should_normalize: false: sort is required")); + } else { + if (is_text_type()) { + need_normalize_p = true; + DBUG_PRINT("info", ("mroonga: should_normalize: true: text type")); + } else { + need_normalize_p = false; + DBUG_PRINT("info", ("mroonga: should_normalize: false: no text type")); + } + } + + DBUG_RETURN(need_normalize_p); + } + + bool FieldNormalizer::is_text_type() { + MRN_DBUG_ENTER_METHOD(); + bool text_type_p; + switch (field_->type()) { + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + text_type_p = true; + break; + case MYSQL_TYPE_STRING: + switch (field_->real_type()) { + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + text_type_p = false; + break; + default: + text_type_p = true; + break; + } + break; + default: + text_type_p = false; + break; + } + DBUG_RETURN(text_type_p); + } + + grn_obj *FieldNormalizer::normalize(const char *string, + unsigned int string_length) { + MRN_DBUG_ENTER_METHOD(); + grn_obj *normalizer = find_grn_normalizer(); + int flags = 0; + grn_encoding original_encoding = GRN_CTX_GET_ENCODING(ctx_); + encoding::set_raw(ctx_, field_->charset()); + grn_obj *grn_string = grn_string_open(ctx_, string, string_length, + normalizer, flags); + GRN_CTX_SET_ENCODING(ctx_, original_encoding); + DBUG_RETURN(grn_string); + } + + grn_obj *FieldNormalizer::find_grn_normalizer() { + MRN_DBUG_ENTER_METHOD(); + + const CHARSET_INFO *charset_info = field_->charset(); + const char *normalizer_name = NULL; + const char *default_normalizer_name = "NormalizerAuto"; + if ((strcmp(charset_info->name, "utf8_general_ci") == 0) || + (strcmp(charset_info->name, "utf8mb4_general_ci") == 0)) { + normalizer_name = "NormalizerMySQLGeneralCI"; + } else if ((strcmp(charset_info->name, "utf8_unicode_ci") == 0) || + (strcmp(charset_info->name, "utf8mb4_unicode_ci") == 0)) { + normalizer_name = "NormalizerMySQLUnicodeCI"; + } else if ((strcmp(charset_info->name, "utf8_unicode_520_ci") == 0) || + (strcmp(charset_info->name, "utf8mb4_unicode_520_ci") == 0)) { + normalizer_name = "NormalizerMySQLUnicode520CI"; + } + + grn_obj *normalizer = NULL; + if (normalizer_name) { + normalizer = grn_ctx_get(ctx_, normalizer_name, -1); + if (!normalizer) { + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "%s normalizer isn't found for %s. " + "Install groonga-normalizer-mysql normalizer. " + "%s is used as fallback.", + normalizer_name, + charset_info->name, + default_normalizer_name); + push_warning(thread_, MRN_SEVERITY_WARNING, + HA_ERR_UNSUPPORTED, error_message); + } + } + + if (!normalizer) { + normalizer = grn_ctx_get(ctx_, default_normalizer_name, -1); + } + + DBUG_RETURN(normalizer); + } +} diff --git a/storage/mroonga/lib/mrn_field_normalizer.hpp b/storage/mroonga/lib/mrn_field_normalizer.hpp new file mode 100644 index 00000000..76083377 --- /dev/null +++ b/storage/mroonga/lib/mrn_field_normalizer.hpp @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_FIELD_NORMALIZER_HPP_ +#define MRN_FIELD_NORMALIZER_HPP_ + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +#include <groonga.h> + +namespace mrn { + class FieldNormalizer { + public: + FieldNormalizer(grn_ctx *ctx, THD *thread, Field *field); + ~FieldNormalizer(); + + bool should_normalize(); + grn_obj *normalize(const char *string, unsigned int string_length); + grn_obj *find_grn_normalizer(); + + private: + grn_ctx *ctx_; + THD *thread_; + Field *field_; + + bool is_text_type(); + }; +} + +#endif // MRN_FIELD_NORMALIZER_HPP_ diff --git a/storage/mroonga/lib/mrn_grn.hpp b/storage/mroonga/lib/mrn_grn.hpp new file mode 100644 index 00000000..f288f3e4 --- /dev/null +++ b/storage/mroonga/lib/mrn_grn.hpp @@ -0,0 +1,39 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2014 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_GRN_HPP_ +#define MRN_GRN_HPP_ + +#include <groonga.h> + +namespace mrn { + namespace grn { + bool is_table(grn_obj *obj) { + grn_id type = obj->header.type; + return GRN_TABLE_HASH_KEY <= type && obj->header.type <= GRN_DB; + } + + bool is_vector_column(grn_obj *column) { + int column_type = (column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK); + return column_type == GRN_OBJ_COLUMN_VECTOR; + } + } +} + +#endif // MRN_GRN_HPP_ diff --git a/storage/mroonga/lib/mrn_index_column_name.cpp b/storage/mroonga/lib/mrn_index_column_name.cpp new file mode 100644 index 00000000..1a19b9d1 --- /dev/null +++ b/storage/mroonga/lib/mrn_index_column_name.cpp @@ -0,0 +1,96 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011-2013 Kentoku SHIBA + Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.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_index_column_name.hpp" + +#define MRN_MIN_INDEX_COLUMN_NAME_LENGTH 65 + +// for debug +#define MRN_CLASS_NAME "mrn::IndexColumnName" + +namespace mrn { + IndexColumnName::IndexColumnName(const char *table_name, + const char *mysql_column_name) + : table_name_(table_name), + mysql_column_name_(mysql_column_name) { + uchar encoded_mysql_column_name_multibyte[MRN_MAX_KEY_SIZE]; + const uchar *mysql_column_name_multibyte = + reinterpret_cast<const uchar *>(mysql_column_name_); + encode(encoded_mysql_column_name_multibyte, + encoded_mysql_column_name_multibyte + MRN_MAX_KEY_SIZE, + mysql_column_name_multibyte, + mysql_column_name_multibyte + strlen(mysql_column_name_)); + snprintf(name_, MRN_MAX_KEY_SIZE, + "%s-%s", table_name_, encoded_mysql_column_name_multibyte); + length_ = strlen(name_); + if (length_ < MRN_MIN_INDEX_COLUMN_NAME_LENGTH) { + memset(name_ + length_, '-', MRN_MIN_INDEX_COLUMN_NAME_LENGTH - length_); + length_ = MRN_MIN_INDEX_COLUMN_NAME_LENGTH; + name_[length_] = '\0'; + } + } + + const char *IndexColumnName::c_str() { + return name_; + } + + size_t IndexColumnName::length() { + return length_; + } + + uint IndexColumnName::encode(uchar *encoded_start, + uchar *encoded_end, + const uchar *mysql_string_start, + const uchar *mysql_string_end) { + MRN_DBUG_ENTER_METHOD(); + my_charset_conv_mb_wc mb_wc = system_charset_info->cset->mb_wc; + my_charset_conv_wc_mb wc_mb = my_charset_filename.cset->wc_mb; + DBUG_PRINT("info", ("mroonga: in=%s", mysql_string_start)); + encoded_end--; + uchar *encoded = encoded_start; + const uchar *mysql_string = mysql_string_start; + while (mysql_string < mysql_string_end && encoded < encoded_end) { + my_wc_t wc; + int mb_wc_converted_length; + int wc_mb_converted_length; + mb_wc_converted_length = + (*mb_wc)(NULL, &wc, mysql_string, mysql_string_end); + if (mb_wc_converted_length > 0) { + wc_mb_converted_length = (*wc_mb)(NULL, wc, encoded, encoded_end); + if (wc_mb_converted_length <= 0) { + break; + } + } else if (mb_wc_converted_length == MY_CS_ILSEQ) { + *encoded = *mysql_string; + mb_wc_converted_length = 1; + wc_mb_converted_length = 1; + } else { + break; + } + mysql_string += mb_wc_converted_length; + encoded += wc_mb_converted_length; + } + *encoded = '\0'; + DBUG_PRINT("info", ("mroonga: out=%s", encoded_start)); + DBUG_RETURN(encoded - encoded_start); + } +} diff --git a/storage/mroonga/lib/mrn_index_column_name.hpp b/storage/mroonga/lib/mrn_index_column_name.hpp new file mode 100644 index 00000000..da3b96db --- /dev/null +++ b/storage/mroonga/lib/mrn_index_column_name.hpp @@ -0,0 +1,43 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011-2013 Kentoku SHIBA + Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_INDEX_COLUMN_NAME_HPP_ +#define MRN_INDEX_COLUMN_NAME_HPP_ + +#include <mrn_constants.hpp> + +namespace mrn { + class IndexColumnName { + public: + IndexColumnName(const char *table_name, const char *mysql_column_name); + const char *c_str(); + size_t length(); + private: + const char *table_name_; + const char *mysql_column_name_; + char name_[MRN_MAX_KEY_SIZE]; + size_t length_; + + uint encode(uchar *encoded_start, uchar *encoded_end, + const uchar *mysql_string_start, const uchar *mysql_string_end); + }; +} + +#endif /* MRN_INDEX_COLUMN_NAME_HPP_ */ diff --git a/storage/mroonga/lib/mrn_index_table_name.cpp b/storage/mroonga/lib/mrn_index_table_name.cpp new file mode 100644 index 00000000..62e67b3c --- /dev/null +++ b/storage/mroonga/lib/mrn_index_table_name.cpp @@ -0,0 +1,136 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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_index_table_name.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::IndexTableName" + +namespace mrn { + const char *IndexTableName::SEPARATOR = "#"; + const char *IndexTableName::OLD_SEPARATOR = "-"; + + bool IndexTableName::is_custom_name(const char *table_name, + size_t table_name_length, + const char *index_table_name, + size_t index_table_name_length) + { + MRN_DBUG_ENTER_METHOD(); + + if (index_table_name_length <= (table_name_length + strlen(SEPARATOR))) { + DBUG_RETURN(true); + } + + if (strncmp(table_name, index_table_name, table_name_length) != 0) { + DBUG_RETURN(true); + } + + if ((strncmp(OLD_SEPARATOR, + index_table_name + table_name_length, + strlen(OLD_SEPARATOR)) != 0) && + (strncmp(SEPARATOR, + index_table_name + table_name_length, + strlen(SEPARATOR)) != 0)) { + DBUG_RETURN(true); + } + + DBUG_RETURN(false); + } + + IndexTableName::IndexTableName(const char *table_name, + const char *mysql_index_name) + : table_name_(table_name), + mysql_index_name_(mysql_index_name) { + uchar encoded_mysql_index_name_multibyte[MRN_MAX_KEY_SIZE]; + const uchar *mysql_index_name_multibyte = + reinterpret_cast<const uchar *>(mysql_index_name_); + encode(encoded_mysql_index_name_multibyte, + encoded_mysql_index_name_multibyte + MRN_MAX_KEY_SIZE, + mysql_index_name_multibyte, + mysql_index_name_multibyte + strlen(mysql_index_name_)); + snprintf(old_name_, MRN_MAX_KEY_SIZE, + "%s%s%s", + table_name_, + OLD_SEPARATOR, + encoded_mysql_index_name_multibyte); + old_length_ = strlen(old_name_); + snprintf(name_, MRN_MAX_KEY_SIZE, + "%s%s%s", + table_name_, + SEPARATOR, + encoded_mysql_index_name_multibyte); + length_ = strlen(name_); + } + + const char *IndexTableName::c_str() { + return name_; + } + + size_t IndexTableName::length() { + return length_; + } + + const char *IndexTableName::old_c_str() { + return old_name_; + } + + size_t IndexTableName::old_length() { + return old_length_; + } + + uint IndexTableName::encode(uchar *encoded_start, + uchar *encoded_end, + const uchar *mysql_string_start, + const uchar *mysql_string_end) { + MRN_DBUG_ENTER_METHOD(); + my_charset_conv_mb_wc mb_wc = system_charset_info->cset->mb_wc; + my_charset_conv_wc_mb wc_mb = my_charset_filename.cset->wc_mb; + DBUG_PRINT("info", ("mroonga: in=%s", mysql_string_start)); + encoded_end--; + uchar *encoded = encoded_start; + const uchar *mysql_string = mysql_string_start; + while (mysql_string < mysql_string_end && encoded < encoded_end) { + my_wc_t wc; + int mb_wc_converted_length; + int wc_mb_converted_length; + mb_wc_converted_length = + (*mb_wc)(NULL, &wc, mysql_string, mysql_string_end); + if (mb_wc_converted_length > 0) { + wc_mb_converted_length = (*wc_mb)(NULL, wc, encoded, encoded_end); + if (wc_mb_converted_length <= 0) { + break; + } + } else if (mb_wc_converted_length == MY_CS_ILSEQ) { + *encoded = *mysql_string; + mb_wc_converted_length = 1; + wc_mb_converted_length = 1; + } else { + break; + } + mysql_string += mb_wc_converted_length; + encoded += wc_mb_converted_length; + } + *encoded = '\0'; + DBUG_PRINT("info", ("mroonga: out=%s", encoded_start)); + DBUG_RETURN(encoded - encoded_start); + } +} diff --git a/storage/mroonga/lib/mrn_index_table_name.hpp b/storage/mroonga/lib/mrn_index_table_name.hpp new file mode 100644 index 00000000..80d2444b --- /dev/null +++ b/storage/mroonga/lib/mrn_index_table_name.hpp @@ -0,0 +1,55 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_INDEX_TABLE_NAME_HPP_ +#define MRN_INDEX_TABLE_NAME_HPP_ + +#include <mrn_constants.hpp> + +namespace mrn { + class IndexTableName { + public: + static const char *SEPARATOR; + static const char *OLD_SEPARATOR; + + static bool is_custom_name(const char *table_name, + size_t table_name_length, + const char *index_table_name, + size_t index_table_name_length); + + IndexTableName(const char *table_name, const char *mysql_index_name); + const char *c_str(); + size_t length(); + const char *old_c_str(); + size_t old_length(); + private: + const char *table_name_; + const char *mysql_index_name_; + char old_name_[MRN_MAX_KEY_SIZE]; + size_t old_length_; + char name_[MRN_MAX_KEY_SIZE]; + size_t length_; + + uint encode(uchar *encoded_start, uchar *encoded_end, + const uchar *mysql_string_start, const uchar *mysql_string_end); + }; +} + +#endif /* MRN_INDEX_TABLE_NAME_HPP_ */ diff --git a/storage/mroonga/lib/mrn_lock.cpp b/storage/mroonga/lib/mrn_lock.cpp new file mode 100644 index 00000000..cfeb519b --- /dev/null +++ b/storage/mroonga/lib/mrn_lock.cpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013-2015 Kouhei Sutou <kou@clear-code.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_lock.hpp" + +namespace mrn { + Lock::Lock(mysql_mutex_t *mutex, bool execute) + : mutex_(mutex), + execute_(execute) { + if (execute_) { + mysql_mutex_lock(mutex_); + } + } + + Lock::~Lock() { + if (execute_) { + mysql_mutex_unlock(mutex_); + } + } +} diff --git a/storage/mroonga/lib/mrn_lock.hpp b/storage/mroonga/lib/mrn_lock.hpp new file mode 100644 index 00000000..29337d8e --- /dev/null +++ b/storage/mroonga/lib/mrn_lock.hpp @@ -0,0 +1,37 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2013-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_LOCK_HPP_ +#define MRN_LOCK_HPP_ + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +namespace mrn { + class Lock { + public: + Lock(mysql_mutex_t *mutex, bool execute=true); + ~Lock(); + private: + mysql_mutex_t *mutex_; + bool execute_; + }; +} + +#endif /* MRN_LOCK_HPP_ */ diff --git a/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp new file mode 100644 index 00000000..62e63023 --- /dev/null +++ b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp @@ -0,0 +1,33 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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_match_escalation_threshold_scope.hpp" + +namespace mrn { + MatchEscalationThresholdScope::MatchEscalationThresholdScope( + grn_ctx *ctx, long long int threshold) + : ctx_(ctx), + original_threshold_(grn_ctx_get_match_escalation_threshold(ctx_)) { + grn_ctx_set_match_escalation_threshold(ctx_, threshold); + } + + MatchEscalationThresholdScope::~MatchEscalationThresholdScope() { + grn_ctx_set_match_escalation_threshold(ctx_, original_threshold_); + } +} diff --git a/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp new file mode 100644 index 00000000..1c5488f8 --- /dev/null +++ b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp @@ -0,0 +1,35 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_ +#define MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_ + +#include <groonga.h> + +namespace mrn { + class MatchEscalationThresholdScope { + grn_ctx *ctx_; + long long int original_threshold_; + public: + MatchEscalationThresholdScope(grn_ctx *ctx, long long int threshold); + ~MatchEscalationThresholdScope(); + }; +} + +#endif // MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_ diff --git a/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp b/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp new file mode 100644 index 00000000..73639685 --- /dev/null +++ b/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp @@ -0,0 +1,712 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012-2015 Kouhei Sutou <kou@clear-code.com> + Copyright(C) 2013 Kentoku SHIBA + + 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_multiple_column_key_codec.hpp" +#include "mrn_field_normalizer.hpp" +#include "mrn_smart_grn_obj.hpp" +#include "mrn_time_converter.hpp" +#include "mrn_value_decoder.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::MultipleColumnKeyCodec" + +#ifdef WORDS_BIGENDIAN +#define mrn_byte_order_host_to_network(buf, key, size) \ +{ \ + uint32 size_ = (uint32)(size); \ + uint8 *buf_ = (uint8 *)(buf); \ + uint8 *key_ = (uint8 *)(key); \ + while (size_--) { *buf_++ = *key_++; } \ +} +#define mrn_byte_order_network_to_host(buf, key, size) \ +{ \ + uint32 size_ = (uint32)(size); \ + uint8 *buf_ = (uint8 *)(buf); \ + uint8 *key_ = (uint8 *)(key); \ + while (size_) { *buf_++ = *key_++; size_--; } \ +} +#else /* WORDS_BIGENDIAN */ +#define mrn_byte_order_host_to_network(buf, key, size) \ +{ \ + uint32 size_ = (uint32)(size); \ + uint8 *buf_ = (uint8 *)(buf); \ + uint8 *key_ = (uint8 *)(key) + size_; \ + while (size_--) { *buf_++ = *(--key_); } \ +} +#define mrn_byte_order_network_to_host(buf, key, size) \ +{ \ + uint32 size_ = (uint32)(size); \ + uint8 *buf_ = (uint8 *)(buf); \ + uint8 *key_ = (uint8 *)(key) + size_; \ + while (size_) { *buf_++ = *(--key_); size_--; } \ +} +#endif /* WORDS_BIGENDIAN */ + +namespace mrn { + MultipleColumnKeyCodec::MultipleColumnKeyCodec(grn_ctx *ctx, + THD *thread, + KEY *key_info) + : ctx_(ctx), + thread_(thread), + key_info_(key_info) { + } + + MultipleColumnKeyCodec::~MultipleColumnKeyCodec() { + } + + int MultipleColumnKeyCodec::encode(const uchar *mysql_key, + uint mysql_key_length, + uchar *grn_key, + uint *grn_key_length) { + MRN_DBUG_ENTER_METHOD(); + int error = 0; + const uchar *current_mysql_key = mysql_key; + const uchar *mysql_key_end = mysql_key + mysql_key_length; + uchar *current_grn_key = grn_key; + + int n_key_parts = KEY_N_KEY_PARTS(key_info_); + DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts)); + *grn_key_length = 0; + for (int i = 0; i < n_key_parts && current_mysql_key < mysql_key_end; i++) { + KEY_PART_INFO *key_part = &(key_info_->key_part[i]); + Field *field = key_part->field; + bool is_null = false; + DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length)); + + if (field->null_bit) { + DBUG_PRINT("info", ("mroonga: field has null bit")); + *current_grn_key = 0; + is_null = *current_mysql_key; + current_mysql_key += 1; + current_grn_key += 1; + (*grn_key_length)++; + } + + DataType data_type = TYPE_UNKNOWN; + uint data_size = 0; + get_key_info(key_part, &data_type, &data_size); + uint grn_key_data_size = data_size; + + switch (data_type) { + case TYPE_UNKNOWN: + // TODO: This will not be happen. This is just for + // suppressing warnings by gcc -O2. :< + error = HA_ERR_UNSUPPORTED; + break; + case TYPE_LONG_LONG_NUMBER: + { + long long int long_long_value = 0; + long_long_value = sint8korr(current_mysql_key); + encode_long_long_int(long_long_value, current_grn_key); + } + break; + case TYPE_NUMBER: + { + Field_num *number_field = static_cast<Field_num *>(field); + encode_number(current_mysql_key, + data_size, + !number_field->unsigned_flag, + current_grn_key); + } + break; + case TYPE_FLOAT: + { + float value; + value_decoder::decode(&value, current_mysql_key); + encode_float(value, data_size, current_grn_key); + } + break; + case TYPE_DOUBLE: + { + double value; + value_decoder::decode(&value, current_mysql_key); + encode_double(value, data_size, current_grn_key); + } + break; + case TYPE_DATETIME: + { + long long int mysql_datetime; +#ifdef WORDS_BIGENDIAN + if (field->table && field->table->s->db_low_byte_first) { + mysql_datetime = sint8korr(current_mysql_key); + } else +#endif + { + value_decoder::decode(&mysql_datetime, current_mysql_key); + } + TimeConverter time_converter; + bool truncated; + long long int grn_time = + time_converter.mysql_datetime_to_grn_time(mysql_datetime, + &truncated); + encode_long_long_int(grn_time, current_grn_key); + } + break; +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + case TYPE_DATETIME2: + { + Field_datetimef *datetimef_field = + static_cast<Field_datetimef *>(field); + long long int mysql_datetime_packed = is_null ? 0 : + my_datetime_packed_from_binary(current_mysql_key, + datetimef_field->decimals()); + MYSQL_TIME mysql_time; + TIME_from_longlong_datetime_packed(&mysql_time, mysql_datetime_packed); + TimeConverter time_converter; + bool truncated; + long long int grn_time = + time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); + grn_key_data_size = 8; + encode_long_long_int(grn_time, current_grn_key); + } + break; +#endif + case TYPE_BYTE_SEQUENCE: + memcpy(current_grn_key, current_mysql_key, data_size); + break; + case TYPE_BYTE_REVERSE: + encode_reverse(current_mysql_key, data_size, current_grn_key); + break; + case TYPE_BYTE_BLOB: + encode_blob(current_mysql_key, &data_size, field, current_grn_key); + grn_key_data_size = data_size; + break; + } + + if (error) { + break; + } + + current_mysql_key += data_size; + current_grn_key += grn_key_data_size; + *grn_key_length += grn_key_data_size; + } + + DBUG_RETURN(error); + } + + int MultipleColumnKeyCodec::decode(const uchar *grn_key, + uint grn_key_length, + uchar *mysql_key, + uint *mysql_key_length) { + MRN_DBUG_ENTER_METHOD(); + int error = 0; + const uchar *current_grn_key = grn_key; + const uchar *grn_key_end = grn_key + grn_key_length; + uchar *current_mysql_key = mysql_key; + + int n_key_parts = KEY_N_KEY_PARTS(key_info_); + DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts)); + *mysql_key_length = 0; + for (int i = 0; i < n_key_parts && current_grn_key < grn_key_end; i++) { + KEY_PART_INFO *key_part = &(key_info_->key_part[i]); + Field *field = key_part->field; + DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length)); + + if (field->null_bit) { + DBUG_PRINT("info", ("mroonga: field has null bit")); + *current_mysql_key = 0; + current_grn_key += 1; + current_mysql_key += 1; + (*mysql_key_length)++; + } + + DataType data_type = TYPE_UNKNOWN; + uint data_size = 0; + get_key_info(key_part, &data_type, &data_size); + uint grn_key_data_size = data_size; + + switch (data_type) { + case TYPE_UNKNOWN: + // TODO: This will not be happen. This is just for + // suppressing warnings by gcc -O2. :< + error = HA_ERR_UNSUPPORTED; + break; + case TYPE_LONG_LONG_NUMBER: + { + long long int value; + decode_long_long_int(current_grn_key, &value); + int8store(current_mysql_key, value); + } + break; + case TYPE_NUMBER: + { + Field_num *number_field = static_cast<Field_num *>(field); + decode_number(current_grn_key, + grn_key_data_size, + !number_field->unsigned_flag, + current_mysql_key); + } + break; + case TYPE_FLOAT: + decode_float(current_grn_key, grn_key_data_size, current_mysql_key); + break; + case TYPE_DOUBLE: + decode_double(current_grn_key, grn_key_data_size, current_mysql_key); + break; + case TYPE_DATETIME: + { + long long int grn_time; + decode_long_long_int(current_grn_key, &grn_time); + TimeConverter time_converter; + long long int mysql_datetime = + time_converter.grn_time_to_mysql_datetime(grn_time); +#ifdef WORDS_BIGENDIAN + if (field->table && field->table->s->db_low_byte_first) { + int8store(current_mysql_key, mysql_datetime); + } else +#endif + { + longlongstore(current_mysql_key, mysql_datetime); + } + } + break; +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + case TYPE_DATETIME2: + { + Field_datetimef *datetimef_field = + static_cast<Field_datetimef *>(field); + long long int grn_time; + grn_key_data_size = 8; + decode_long_long_int(current_grn_key, &grn_time); + TimeConverter time_converter; + MYSQL_TIME mysql_time; + mysql_time.neg = FALSE; + mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME; + time_converter.grn_time_to_mysql_time(grn_time, &mysql_time); + long long int mysql_datetime_packed = + TIME_to_longlong_datetime_packed(&mysql_time); + my_datetime_packed_to_binary(mysql_datetime_packed, + current_mysql_key, + datetimef_field->decimals()); + } + break; +#endif + case TYPE_BYTE_SEQUENCE: + memcpy(current_mysql_key, current_grn_key, grn_key_data_size); + break; + case TYPE_BYTE_REVERSE: + decode_reverse(current_grn_key, grn_key_data_size, current_mysql_key); + break; + case TYPE_BYTE_BLOB: + memcpy(current_mysql_key, + current_grn_key + data_size, + HA_KEY_BLOB_LENGTH); + memcpy(current_mysql_key + HA_KEY_BLOB_LENGTH, + current_grn_key, + data_size); + data_size += HA_KEY_BLOB_LENGTH; + grn_key_data_size = data_size; + break; + } + + if (error) { + break; + } + + current_grn_key += grn_key_data_size; + current_mysql_key += data_size; + *mysql_key_length += data_size; + } + + DBUG_RETURN(error); + } + + uint MultipleColumnKeyCodec::size() { + MRN_DBUG_ENTER_METHOD(); + + int n_key_parts = KEY_N_KEY_PARTS(key_info_); + DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts)); + + uint total_size = 0; + for (int i = 0; i < n_key_parts; ++i) { + KEY_PART_INFO *key_part = &(key_info_->key_part[i]); + Field *field = key_part->field; + DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length)); + + if (field->null_bit) { + DBUG_PRINT("info", ("mroonga: field has null bit")); + ++total_size; + } + + DataType data_type = TYPE_UNKNOWN; + uint data_size = 0; + get_key_info(key_part, &data_type, &data_size); + switch (data_type) { +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + case TYPE_DATETIME2: + data_size = 8; + break; +#endif + case TYPE_BYTE_BLOB: + data_size += HA_KEY_BLOB_LENGTH; + break; + default: + break; + } + total_size += data_size; + } + + DBUG_RETURN(total_size); + } + + void MultipleColumnKeyCodec::get_key_info(KEY_PART_INFO *key_part, + DataType *data_type, + uint *data_size) { + MRN_DBUG_ENTER_METHOD(); + + *data_type = TYPE_UNKNOWN; + *data_size = 0; + + Field *field = key_part->field; + switch (field->real_type()) { + case MYSQL_TYPE_DECIMAL: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DECIMAL")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_YEAR: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TINY")); + *data_type = TYPE_NUMBER; + *data_size = 1; + break; + case MYSQL_TYPE_SHORT: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_SHORT")); + *data_type = TYPE_NUMBER; + *data_size = 2; + break; + case MYSQL_TYPE_LONG: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_LONG")); + *data_type = TYPE_NUMBER; + *data_size = 4; + break; + case MYSQL_TYPE_FLOAT: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_FLOAT")); + *data_type = TYPE_FLOAT; + *data_size = 4; + break; + case MYSQL_TYPE_DOUBLE: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DOUBLE")); + *data_type = TYPE_DOUBLE; + *data_size = 8; + break; + case MYSQL_TYPE_NULL: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_NULL")); + *data_type = TYPE_NUMBER; + *data_size = 1; + break; + case MYSQL_TYPE_TIMESTAMP: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIMESTAMP")); + *data_type = TYPE_BYTE_REVERSE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_DATE: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DATE")); + *data_type = TYPE_BYTE_REVERSE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_DATETIME: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DATETIME")); + *data_type = TYPE_DATETIME; + *data_size = key_part->length; + break; + case MYSQL_TYPE_NEWDATE: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_NEWDATE")); + *data_type = TYPE_BYTE_REVERSE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_LONGLONG: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_LONGLONG")); + *data_type = TYPE_NUMBER; + *data_size = 8; + break; + case MYSQL_TYPE_INT24: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_INT24")); + *data_type = TYPE_NUMBER; + *data_size = 3; + break; + case MYSQL_TYPE_TIME: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIME")); + *data_type = TYPE_NUMBER; + *data_size = 3; + break; + case MYSQL_TYPE_VARCHAR: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_VARCHAR")); + *data_type = TYPE_BYTE_BLOB; + *data_size = key_part->length; + break; + case MYSQL_TYPE_BIT: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_BIT")); + *data_type = TYPE_NUMBER; + *data_size = 1; + break; +#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIMESTAMP2")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; +#endif +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DATETIME2")); + *data_type = TYPE_DATETIME2; + *data_size = key_part->length; + break; +#endif +#ifdef MRN_HAVE_MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIME2")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; +#endif + case MYSQL_TYPE_NEWDECIMAL: + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_NEWDECIMAL")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_ENUM: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_ENUM")); + *data_type = TYPE_NUMBER; + *data_size = 1; + break; + case MYSQL_TYPE_SET: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_SET")); + *data_type = TYPE_NUMBER; + *data_size = 1; + break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_BLOB")); + *data_type = TYPE_BYTE_BLOB; + *data_size = key_part->length; + break; + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_STRING")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; + case MYSQL_TYPE_GEOMETRY: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_GEOMETRY")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->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: + // TODO + DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_JSON")); + *data_type = TYPE_BYTE_SEQUENCE; + *data_size = key_part->length; + break; +#endif + } + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_number(const uchar *mysql_key, + uint mysql_key_size, + bool is_signed, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + mrn_byte_order_host_to_network(grn_key, mysql_key, mysql_key_size); + if (is_signed) { + grn_key[0] ^= 0x80; + } + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::decode_number(const uchar *grn_key, + uint grn_key_size, + bool is_signed, + uchar *mysql_key) { + MRN_DBUG_ENTER_METHOD(); + uchar buffer[8]; + memcpy(buffer, grn_key, grn_key_size); + if (is_signed) { + buffer[0] ^= 0x80; + } + mrn_byte_order_network_to_host(mysql_key, buffer, grn_key_size); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_long_long_int(volatile long long int value, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + uint value_size = 8; + mrn_byte_order_host_to_network(grn_key, &value, value_size); + grn_key[0] ^= 0x80; + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::decode_long_long_int(const uchar *grn_key, + long long int *value) { + MRN_DBUG_ENTER_METHOD(); + uint grn_key_size = 8; + uchar buffer[8]; + memcpy(buffer, grn_key, grn_key_size); + buffer[0] ^= 0x80; + mrn_byte_order_network_to_host(value, buffer, grn_key_size); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_float(volatile float value, + uint value_size, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + int n_bits = (value_size * 8 - 1); + volatile int *int_value_pointer = (int *)(&value); + int int_value = *int_value_pointer; + int_value ^= ((int_value >> n_bits) | (1 << n_bits)); + mrn_byte_order_host_to_network(grn_key, &int_value, value_size); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::decode_float(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key) { + MRN_DBUG_ENTER_METHOD(); + int int_value; + mrn_byte_order_network_to_host(&int_value, grn_key, grn_key_size); + int max_bit = (grn_key_size * 8 - 1); + *((int *)mysql_key) = + int_value ^ (((int_value ^ (1 << max_bit)) >> max_bit) | + (1 << max_bit)); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_double(volatile double value, + uint value_size, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + int n_bits = (value_size * 8 - 1); + volatile long long int *long_long_value_pointer = (long long int *)(&value); + volatile long long int long_long_value = *long_long_value_pointer; + long_long_value ^= ((long_long_value >> n_bits) | (1LL << n_bits)); + mrn_byte_order_host_to_network(grn_key, &long_long_value, value_size); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::decode_double(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key) { + MRN_DBUG_ENTER_METHOD(); + long long int long_long_value; + mrn_byte_order_network_to_host(&long_long_value, grn_key, grn_key_size); + int max_bit = (grn_key_size * 8 - 1); + long_long_value = + long_long_value ^ (((long_long_value ^ (1LL << max_bit)) >> max_bit) | + (1LL << max_bit)); + memcpy(mysql_key, &long_long_value, sizeof(long_long_value)); + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_reverse(const uchar *mysql_key, + uint mysql_key_size, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + for (uint i = 0; i < mysql_key_size; i++) { + grn_key[i] = mysql_key[mysql_key_size - i - 1]; + } + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::decode_reverse(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key) { + MRN_DBUG_ENTER_METHOD(); + for (uint i = 0; i < grn_key_size; i++) { + mysql_key[i] = grn_key[grn_key_size - i - 1]; + } + DBUG_VOID_RETURN; + } + + void MultipleColumnKeyCodec::encode_blob(const uchar *mysql_key, + uint *mysql_key_size, + Field *field, + uchar *grn_key) { + MRN_DBUG_ENTER_METHOD(); + FieldNormalizer normalizer(ctx_, thread_, field); + if (normalizer.should_normalize()) { +#if HA_KEY_BLOB_LENGTH != 2 +# error "TODO: support HA_KEY_BLOB_LENGTH != 2 case if it is needed" +#endif + const char *blob_data = + reinterpret_cast<const char *>(mysql_key + HA_KEY_BLOB_LENGTH); + uint16 blob_data_length = *((uint16 *)(mysql_key)); + grn_obj *grn_string = normalizer.normalize(blob_data, + blob_data_length); + mrn::SmartGrnObj smart_grn_string(ctx_, grn_string); + const char *normalized; + unsigned int normalized_length = 0; + grn_string_get_normalized(ctx_, grn_string, + &normalized, &normalized_length, NULL); + uint16 new_blob_data_length; + if (normalized_length <= UINT_MAX16) { + if (normalized_length) + memcpy(grn_key, normalized, normalized_length); + if (normalized_length < *mysql_key_size) { + memset(grn_key + normalized_length, + '\0', *mysql_key_size - normalized_length); + } + new_blob_data_length = normalized_length; + } else { + push_warning_printf(thread_, + MRN_SEVERITY_WARNING, + MRN_ERROR_CODE_DATA_TRUNCATE(thread_), + "normalized data truncated " + "for multiple column index: " + "normalized-data-size: <%u> " + "max-data-size: <%u> " + "column-name: <%s> " + "data: <%.*s>", + normalized_length, + UINT_MAX16, + field->field_name, + blob_data_length, blob_data); + memcpy(grn_key, normalized, blob_data_length); + new_blob_data_length = blob_data_length; + } + memcpy(grn_key + *mysql_key_size, + &new_blob_data_length, + HA_KEY_BLOB_LENGTH); + } else { + memcpy(grn_key + *mysql_key_size, mysql_key, HA_KEY_BLOB_LENGTH); + memcpy(grn_key, mysql_key + HA_KEY_BLOB_LENGTH, *mysql_key_size); + } + *mysql_key_size += HA_KEY_BLOB_LENGTH; + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp b/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp new file mode 100644 index 00000000..26de08ca --- /dev/null +++ b/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp @@ -0,0 +1,100 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_ +#define MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_ + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +#include <groonga.h> + +namespace mrn { + class MultipleColumnKeyCodec { + public: + MultipleColumnKeyCodec(grn_ctx *ctx, THD *thread, KEY *key_info); + ~MultipleColumnKeyCodec(); + + int encode(const uchar *mysql_key, uint mysql_key_length, + uchar *grn_key, uint *grn_key_length); + int decode(const uchar *grn_key, uint grn_key_length, + uchar *mysql_key, uint *mysql_key_length); + uint size(); + + private: + enum DataType { + TYPE_UNKNOWN, + TYPE_LONG_LONG_NUMBER, + TYPE_NUMBER, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_DATETIME, +#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2 + TYPE_DATETIME2, +#endif + TYPE_BYTE_SEQUENCE, + TYPE_BYTE_REVERSE, + TYPE_BYTE_BLOB + }; + + grn_ctx *ctx_; + THD *thread_; + KEY *key_info_; + + void get_key_info(KEY_PART_INFO *key_part, + DataType *data_type, uint *data_size); + + void encode_number(const uchar *mysql_key, + uint mysql_key_size, + bool is_signed, + uchar *grn_key); + void decode_number(const uchar *grn_key, + uint grn_key_size, + bool is_signed, + uchar *mysql_key); + void encode_long_long_int(volatile long long int value, + uchar *grn_key); + void decode_long_long_int(const uchar *grn_key, + long long int *value); + void encode_float(volatile float value, + uint value_size, + uchar *grn_key); + void decode_float(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key); + void encode_double(volatile double value, + uint value_size, + uchar *grn_key); + void decode_double(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key); + void encode_reverse(const uchar *mysql_key, + uint mysql_key_size, + uchar *grn_key); + void decode_reverse(const uchar *grn_key, + uint grn_key_size, + uchar *mysql_key); + void encode_blob(const uchar *mysql_key, + uint *mysql_key_size, + Field *field, + uchar *grn_key); + }; +} + +#endif // MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_ diff --git a/storage/mroonga/lib/mrn_mysqlservices.cpp b/storage/mroonga/lib/mrn_mysqlservices.cpp new file mode 100644 index 00000000..d1fdd471 --- /dev/null +++ b/storage/mroonga/lib/mrn_mysqlservices.cpp @@ -0,0 +1,28 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.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> + +/* +void *thd_alloc(MYSQL_THD thd, unsigned int size) +{ + return thd->alloc(size); +} +*/ diff --git a/storage/mroonga/lib/mrn_operation.cpp b/storage/mroonga/lib/mrn_operation.cpp new file mode 100644 index 00000000..e351945f --- /dev/null +++ b/storage/mroonga/lib/mrn_operation.cpp @@ -0,0 +1,51 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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_operation.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::Operation" + +namespace mrn { + Operation::Operation(mrn::Operations *operations, + const char *type, + const char *table_name, + size_t table_name_size) + : operations_(operations), + id_(operations_->start(type, table_name, table_name_size)) { + } + + Operation::~Operation() { + MRN_DBUG_ENTER_METHOD(); + + operations_->finish(id_); + + DBUG_VOID_RETURN; + } + + void Operation::record_target(grn_id record_id) { + MRN_DBUG_ENTER_METHOD(); + + operations_->record_target(id_, record_id); + + DBUG_VOID_RETURN; + } +} diff --git a/storage/mroonga/lib/mrn_operation.hpp b/storage/mroonga/lib/mrn_operation.hpp new file mode 100644 index 00000000..9375cefb --- /dev/null +++ b/storage/mroonga/lib/mrn_operation.hpp @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_OPERATION_HPP_ +#define MRN_OPERATION_HPP_ + +#include <mrn_operations.hpp> + +namespace mrn { + class Operation { + public: + Operation(mrn::Operations *operations, + const char *type, + const char *table_name, + size_t table_name_size); + ~Operation(); + + void record_target(grn_id record_id); + + private: + mrn::Operations *operations_; + grn_id id_; + }; +} + +#endif /* MRN_OPERATION_HPP_ */ diff --git a/storage/mroonga/lib/mrn_operations.cpp b/storage/mroonga/lib/mrn_operations.cpp new file mode 100644 index 00000000..22a8901e --- /dev/null +++ b/storage/mroonga/lib/mrn_operations.cpp @@ -0,0 +1,401 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 <string.h> + +#include "mrn_operations.hpp" + +// for debug +#define MRN_CLASS_NAME "mrn::Operations" + +#define TABLE_NAME "mroonga_operations" +#define COLUMN_TYPE_NAME "type" +#define COLUMN_TABLE_NAME "table" +#define COLUMN_RECORD_NAME "record" + +namespace mrn { + Operations::Operations(grn_ctx *ctx) + : ctx_(ctx) { + MRN_DBUG_ENTER_METHOD(); + + GRN_TEXT_INIT(&text_buffer_, GRN_OBJ_DO_SHALLOW_COPY); + GRN_UINT32_INIT(&id_buffer_, 0); + + table_ = grn_ctx_get(ctx_, TABLE_NAME, -1); + if (!table_) { + table_ = grn_table_create(ctx_, + TABLE_NAME, strlen(TABLE_NAME), + NULL, + GRN_OBJ_TABLE_NO_KEY | GRN_OBJ_PERSISTENT, + NULL, NULL); + columns_.type_ = + grn_column_create(ctx_, table_, + COLUMN_TYPE_NAME, strlen(COLUMN_TYPE_NAME), + NULL, + GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT, + grn_ctx_at(ctx_, GRN_DB_SHORT_TEXT)); + columns_.table_ = + grn_column_create(ctx_, table_, + COLUMN_TABLE_NAME, strlen(COLUMN_TABLE_NAME), + NULL, + GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT, + grn_ctx_at(ctx_, GRN_DB_SHORT_TEXT)); + columns_.record_ = + grn_column_create(ctx_, table_, + COLUMN_RECORD_NAME, strlen(COLUMN_RECORD_NAME), + NULL, + GRN_OBJ_COLUMN_SCALAR | GRN_OBJ_PERSISTENT, + grn_ctx_at(ctx_, GRN_DB_UINT32)); + } else { + columns_.type_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_TYPE_NAME, -1); + columns_.table_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_TABLE_NAME, -1); + columns_.record_ = grn_ctx_get(ctx_, TABLE_NAME "." COLUMN_RECORD_NAME, -1); + } + + is_enabled_recording_ = true; + + DBUG_VOID_RETURN; + } + + Operations::~Operations() { + MRN_DBUG_ENTER_METHOD(); + + GRN_OBJ_FIN(ctx_, &id_buffer_); + GRN_OBJ_FIN(ctx_, &text_buffer_); + + DBUG_VOID_RETURN; + } + + bool Operations::is_locked() { + MRN_DBUG_ENTER_METHOD(); + + if (grn_obj_is_locked(ctx_, table_) > 0) + DBUG_RETURN(true); + + if (grn_obj_is_locked(ctx_, columns_.type_) > 0) + DBUG_RETURN(true); + + if (grn_obj_is_locked(ctx_, columns_.table_) > 0) + DBUG_RETURN(true); + + if (grn_obj_is_locked(ctx_, columns_.record_) > 0) + DBUG_RETURN(true); + + DBUG_RETURN(false); + } + + grn_id Operations::start(const char *type, + const char *table_name, size_t table_name_size) { + MRN_DBUG_ENTER_METHOD(); + + if (!is_enabled_recording_) { + DBUG_RETURN(GRN_ID_NIL); + } + + grn_id id = grn_table_add(ctx_, table_, NULL, 0, NULL); + + GRN_TEXT_SETS(ctx_, &text_buffer_, type); + grn_obj_set_value(ctx_, columns_.type_, id, &text_buffer_, GRN_OBJ_SET); + + GRN_TEXT_SET(ctx_, &text_buffer_, table_name, table_name_size); + grn_obj_set_value(ctx_, columns_.table_, id, &text_buffer_, GRN_OBJ_SET); + + DBUG_RETURN(id); + } + + void Operations::record_target(grn_id id, grn_id record_id) { + MRN_DBUG_ENTER_METHOD(); + + if (!is_enabled_recording_) { + DBUG_VOID_RETURN; + } + + GRN_UINT32_SET(ctx_, &id_buffer_, record_id); + grn_obj_set_value(ctx_, columns_.record_, id, &id_buffer_, GRN_OBJ_SET); + + DBUG_VOID_RETURN; + } + + void Operations::finish(grn_id id) { + MRN_DBUG_ENTER_METHOD(); + + if (!is_enabled_recording_) { + DBUG_VOID_RETURN; + } + + grn_table_delete_by_id(ctx_, table_, id); + + DBUG_VOID_RETURN; + } + + void Operations::enable_recording() { + MRN_DBUG_ENTER_METHOD(); + + is_enabled_recording_ = true; + + DBUG_VOID_RETURN; + } + + void Operations::disable_recording() { + MRN_DBUG_ENTER_METHOD(); + + is_enabled_recording_ = false; + + DBUG_VOID_RETURN; + } + + grn_hash *Operations::collect_processing_table_names() { + MRN_DBUG_ENTER_METHOD(); + + grn_hash *table_names = + grn_hash_create(ctx_, NULL, GRN_TABLE_MAX_KEY_SIZE, 0, + GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_KEY_VAR_SIZE); + + grn_table_cursor *cursor; + cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + GRN_LOG(ctx_, GRN_LOG_NOTICE, + "[operations] failed to open cursor: %s", + ctx_->errbuf); + DBUG_RETURN(table_names); + } + + grn_id id; + while ((id = grn_table_cursor_next(ctx_, cursor))) { + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_); + if (GRN_TEXT_LEN(&text_buffer_) > 0) { + grn_hash_add(ctx_, table_names, + GRN_TEXT_VALUE(&text_buffer_), + GRN_TEXT_LEN(&text_buffer_), + NULL, + NULL); + } + } + grn_table_cursor_close(ctx_, cursor); + + DBUG_RETURN(table_names); + } + + int Operations::repair(const char *table_name, size_t table_name_size) { + MRN_DBUG_ENTER_METHOD(); + + int error = 0; + + grn_table_cursor *cursor; + cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + error = HA_ERR_CRASHED_ON_USAGE; + if (ctx_->rc) { + my_message(error, ctx_->errbuf, MYF(0)); + } else { + my_message(error, + "mroonga: repair: " + "failed to open cursor for operations table", + MYF(0)); + } + DBUG_RETURN(error); + } + + grn_obj *target_table = grn_ctx_get(ctx_, table_name, table_name_size); + if (!target_table) { + GRN_LOG(ctx_, GRN_LOG_WARNING, + "table doesn't exist for auto repair: <%.*s>", + static_cast<int>(table_name_size), table_name); + } + + grn_id id; + while ((id = grn_table_cursor_next(ctx_, cursor))) { + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_); + if (!((static_cast<size_t>(GRN_TEXT_LEN(&text_buffer_)) == + table_name_size) && + memcmp(GRN_TEXT_VALUE(&text_buffer_), + table_name, + table_name_size) == 0)) { + continue; + } + + if (!target_table) { + grn_rc rc = grn_table_cursor_delete(ctx_, cursor); + if (rc != GRN_SUCCESS) { + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_); + GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0'); + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: repair: failed to delete an orphan operation: " + "[%u]: <%.*s>[%s]: <%s>(%d)", + id, + static_cast<int>(table_name_size), table_name, + GRN_TEXT_VALUE(&text_buffer_), + ctx_->errbuf, + rc); + my_message(error, error_message, MYF(0)); + break; + } + continue; + } + + GRN_BULK_REWIND(&id_buffer_); + grn_obj_get_value(ctx_, columns_.record_, id, &id_buffer_); + grn_id record_id = GRN_UINT32_VALUE(&id_buffer_); + if (record_id == GRN_ID_NIL) { + grn_rc rc = grn_table_cursor_delete(ctx_, cursor); + if (rc != GRN_SUCCESS) { + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_); + GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0'); + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: repair: " + "failed to delete an operation that has no related record: " + "[%u]: <%.*s>[%s]: <%s>(%d)", + id, + static_cast<int>(table_name_size), table_name, + GRN_TEXT_VALUE(&text_buffer_), + ctx_->errbuf, + rc); + my_message(error, error_message, MYF(0)); + break; + } + continue; + } + + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_); + GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0'); + if (strcmp(GRN_TEXT_VALUE(&text_buffer_), "write") == 0 || + strcmp(GRN_TEXT_VALUE(&text_buffer_), "delete") == 0) { + grn_rc rc = grn_table_delete_by_id(ctx_, target_table, record_id); + if (rc != GRN_SUCCESS) { + error = HA_ERR_CRASHED_ON_USAGE; + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: repair: failed to delete an incomplete record: " + "[%u]: <%.*s>[%u]: <%s>(%d)", + id, + static_cast<int>(table_name_size), table_name, + record_id, + ctx_->errbuf, + rc); + my_message(error, error_message, MYF(0)); + break; + } + + rc = grn_table_cursor_delete(ctx_, cursor); + if (rc != GRN_SUCCESS) { + error = HA_ERR_CRASHED_ON_USAGE; + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: repair: failed to delete an incomplete operation: " + "[%u]: <%.*s>[%u][%s]: <%s>(%d)", + id, + static_cast<int>(table_name_size), table_name, + record_id, + GRN_TEXT_VALUE(&text_buffer_), + ctx_->errbuf, + rc); + my_message(error, error_message, MYF(0)); + break; + } + } else if (strcmp(GRN_TEXT_VALUE(&text_buffer_), "update") == 0) { + error = HA_ERR_CRASHED_ON_USAGE; + my_message(error, + "mroonga: repair: can't recover from crash while updating", + MYF(0)); + break; + } else { + error = HA_ERR_CRASHED_ON_USAGE; + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: repair: unknown operation type: " + "[%u]: <%.*s>[%u]: <%s>", + id, + static_cast<int>(table_name_size), table_name, + record_id, + GRN_TEXT_VALUE(&text_buffer_)); + my_message(error, error_message, MYF(0)); + break; + } + } + grn_table_cursor_close(ctx_, cursor); + + DBUG_RETURN(error); + } + + int Operations::clear(const char *table_name, size_t table_name_size) { + MRN_DBUG_ENTER_METHOD(); + + int error = 0; + + grn_table_cursor *cursor; + cursor = grn_table_cursor_open(ctx_, table_, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + error = HA_ERR_CRASHED_ON_USAGE; + if (ctx_->rc) { + my_message(error, ctx_->errbuf, MYF(0)); + } else { + my_message(error, + "mroonga: clear: " + "failed to open cursor for operations table", + MYF(0)); + } + DBUG_RETURN(error); + } + + grn_id id; + while ((id = grn_table_cursor_next(ctx_, cursor))) { + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.table_, id, &text_buffer_); + if ((static_cast<size_t>(GRN_TEXT_LEN(&text_buffer_)) == + table_name_size) && + memcmp(GRN_TEXT_VALUE(&text_buffer_), + table_name, + table_name_size) == 0) { + grn_rc rc = grn_table_cursor_delete(ctx_, cursor); + if (rc != GRN_SUCCESS) { + error = HA_ERR_CRASHED_ON_USAGE; + GRN_BULK_REWIND(&id_buffer_); + grn_obj_get_value(ctx_, columns_.record_, id, &id_buffer_); + GRN_BULK_REWIND(&text_buffer_); + grn_obj_get_value(ctx_, columns_.type_, id, &text_buffer_); + GRN_TEXT_PUTC(ctx_, &text_buffer_, '\0'); + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "mroonga: clear: failed to delete an operation: " + "[%u]: <%.*s>[%u][%s]: <%s>(%d)", + id, + static_cast<int>(table_name_size), table_name, + GRN_UINT32_VALUE(&id_buffer_), + GRN_TEXT_VALUE(&text_buffer_), + ctx_->errbuf, + rc); + my_message(error, error_message, MYF(0)); + break; + } + } + } + grn_table_cursor_close(ctx_, cursor); + + DBUG_RETURN(error); + } +} diff --git a/storage/mroonga/lib/mrn_operations.hpp b/storage/mroonga/lib/mrn_operations.hpp new file mode 100644 index 00000000..803d9ab6 --- /dev/null +++ b/storage/mroonga/lib/mrn_operations.hpp @@ -0,0 +1,60 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_OPERATIONS_HPP_ +#define MRN_OPERATIONS_HPP_ + +#include <groonga.h> + +namespace mrn { + class Operations { + public: + Operations(grn_ctx *ctx); + ~Operations(); + + bool is_locked(); + + grn_id start(const char *type, + const char *table_name, size_t table_name_size); + void record_target(grn_id id, grn_id target_id); + void finish(grn_id id); + + void enable_recording(); + void disable_recording(); + + grn_hash *collect_processing_table_names(); + + int repair(const char *table_name, size_t table_name_size); + int clear(const char *table_name, size_t table_name_size); + + private: + grn_ctx *ctx_; + grn_obj text_buffer_; + grn_obj id_buffer_; + grn_obj *table_; + struct { + grn_obj *type_; + grn_obj *table_; + grn_obj *record_; + } columns_; + bool is_enabled_recording_; + }; +} + +#endif /* MRN_OPERATIONS_HPP_ */ diff --git a/storage/mroonga/lib/mrn_parameters_parser.cpp b/storage/mroonga/lib/mrn_parameters_parser.cpp new file mode 100644 index 00000000..a1db22bf --- /dev/null +++ b/storage/mroonga/lib/mrn_parameters_parser.cpp @@ -0,0 +1,177 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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_parameters_parser.hpp" + +#include <mrn_mysql_compat.h> +#include <mrn_variables.hpp> + +namespace mrn { + class Parameter { + public: + char *key_; + char *value_; + + Parameter(const char *key, unsigned int key_length, + const char *value, unsigned int value_length) + : key_(mrn_my_strndup(key, key_length, MYF(0))), + value_(mrn_my_strndup(value, value_length, MYF(0))) { + }; + ~Parameter() { + if (key_) { + my_free(key_); + } + if (value_) { + my_free(value_); + } + }; + }; + + ParametersParser::ParametersParser(const char *input, + unsigned int input_length) + : input_(input), + input_length_(input_length), + parameters_(NULL) { + } + + ParametersParser::~ParametersParser() { + for (LIST *next = parameters_; next; next = next->next) { + Parameter *parameter = static_cast<Parameter *>(next->data); + delete parameter; + } + list_free(parameters_, false); + } + + void ParametersParser::parse() { + const char *current = input_; + const char *end = input_ + input_length_; + for (; current < end; ++current) { + if (is_white_space(current[0])) { + continue; + } + + const char *key = current; + unsigned int key_length = 0; + while (current < end && + !is_white_space(current[0]) && + current[0] != '\'' && current[0] != '"' && current[0] != ',') { + ++current; + ++key_length; + } + if (current == end) { + break; + } + + while (current < end && is_white_space(current[0])) { + ++current; + } + if (current == end) { + break; + } + current = parse_value(current, end, key, key_length); + if (!current) { + break; + } + + while (current < end && is_white_space(current[0])) { + ++current; + } + if (current == end) { + break; + } + if (current[0] != ',') { + // TODO: report error + break; + } + } + } + + const char *ParametersParser::parse_value(const char *current, + const char *end, + const char *key, + unsigned int key_length) { + char quote = current[0]; + if (quote != '\'' && quote != '"') { + // TODO: report error + return NULL; + } + ++current; + + bool found = false; + static const unsigned int max_value_length = 4096; + char value[max_value_length]; + unsigned int value_length = 0; + for (; current < end && value_length < max_value_length; ++current) { + if (current[0] == quote) { + Parameter *parameter = new Parameter(key, key_length, + value, value_length); + list_push(parameters_, parameter); + found = true; + ++current; + break; + } + + switch (current[0]) { + case '\\': + if (current + 1 == end) { + break; + } + switch (current[1]) { + case 'b': + value[value_length] = '\b'; + break; + case 'n': + value[value_length] = '\n'; + break; + case 'r': + value[value_length] = '\r'; + break; + case 't': + value[value_length] = '\t'; + break; + default: + value[value_length] = current[1]; + break; + } + break; + default: + value[value_length] = current[0]; + break; + } + ++value_length; + } + + if (!found) { + // TODO: report error + } + + return current; + } + + const char *ParametersParser::operator[](const char *key) { + for (LIST *next = parameters_; next; next = next->next) { + Parameter *parameter = static_cast<Parameter *>(next->data); + if (strcasecmp(parameter->key_, key) == 0) { + return parameter->value_; + } + } + return NULL; + } +} diff --git a/storage/mroonga/lib/mrn_parameters_parser.hpp b/storage/mroonga/lib/mrn_parameters_parser.hpp new file mode 100644 index 00000000..18ad769e --- /dev/null +++ b/storage/mroonga/lib/mrn_parameters_parser.hpp @@ -0,0 +1,59 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2011-2013 Kentoku SHIBA + Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_PARAMETERS_PARSER_HPP_ +#define MRN_PARAMETERS_PARSER_HPP_ + +#include <mrn_mysql.h> +#include <my_list.h> + +namespace mrn { + class ParametersParser { + public: + ParametersParser(const char *input, unsigned int input_length); + ~ParametersParser(); + void parse(); + const char *operator[](const char *key); + + private: + const char *input_; + unsigned int input_length_; + + LIST *parameters_; + + bool is_white_space(char character) { + switch (character) { + case ' ': + case '\r': + case '\n': + case '\t': + return true; + break; + default: + return false; + break; + } + }; + const char *parse_value(const char *current, const char *end, + const char *key, unsigned int key_length); + }; +} + +#endif /* MRN_PARAMETERS_PARSER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_path_mapper.cpp b/storage/mroonga/lib/mrn_path_mapper.cpp new file mode 100644 index 00000000..0e867b88 --- /dev/null +++ b/storage/mroonga/lib/mrn_path_mapper.cpp @@ -0,0 +1,229 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010 Tetsuro IKEDA + Copyright(C) 2011-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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_path_mapper.hpp" + +#include <string.h> + +namespace mrn { + char *PathMapper::default_path_prefix = NULL; + char *PathMapper::default_mysql_data_home_path = NULL; + + PathMapper::PathMapper(const char *original_mysql_path, + const char *path_prefix, + const char *mysql_data_home_path) + : original_mysql_path_(original_mysql_path), + path_prefix_(path_prefix), + mysql_data_home_path_(mysql_data_home_path) { + db_path_[0] = '\0'; + db_name_[0] = '\0'; + table_name_[0] = '\0'; + mysql_table_name_[0] = '\0'; + mysql_path_[0] = '\0'; + } + + /** + * "./${db}/${table}" ==> "${db}.mrn" + * "./${db}/" ==> "${db}.mrn" + * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0" ==> + * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0.mrn" + */ + const char *PathMapper::db_path() { + if (db_path_[0] != '\0') { + return db_path_; + } + + if (original_mysql_path_[0] == FN_CURLIB && + original_mysql_path_[1] == FN_LIBCHAR) { + if (path_prefix_) { + strcpy(db_path_, path_prefix_); + } + + int i = 2, j = strlen(db_path_), len; + len = strlen(original_mysql_path_); + while (original_mysql_path_[i] != FN_LIBCHAR && i < len) { + db_path_[j++] = original_mysql_path_[i++]; + } + db_path_[j] = '\0'; + } else if (mysql_data_home_path_) { + int len = strlen(original_mysql_path_); + int mysql_data_home_len = strlen(mysql_data_home_path_); + if (len > mysql_data_home_len && + !strncmp(original_mysql_path_, + mysql_data_home_path_, + mysql_data_home_len)) { + int i = mysql_data_home_len, j; + if (path_prefix_ && path_prefix_[0] == FN_LIBCHAR) { + strcpy(db_path_, path_prefix_); + j = strlen(db_path_); + } else { + memcpy(db_path_, mysql_data_home_path_, mysql_data_home_len); + if (path_prefix_) { + if (path_prefix_[0] == FN_CURLIB && + path_prefix_[1] == FN_LIBCHAR) { + strcpy(&db_path_[mysql_data_home_len], &path_prefix_[2]); + } else { + strcpy(&db_path_[mysql_data_home_len], path_prefix_); + } + j = strlen(db_path_); + } else { + j = mysql_data_home_len; + } + } + + while (original_mysql_path_[i] != FN_LIBCHAR && i < len) { + db_path_[j++] = original_mysql_path_[i++]; + } + if (i == len) { + memcpy(db_path_, original_mysql_path_, len); + } else { + db_path_[j] = '\0'; + } + } else { + strcpy(db_path_, original_mysql_path_); + } + } else { + strcpy(db_path_, original_mysql_path_); + } + strcat(db_path_, MRN_DB_FILE_SUFFIX); + return db_path_; + } + + /** + * "./${db}/${table}" ==> "${db}" + * "./${db}/" ==> "${db}" + * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0" ==> + * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0" + */ + const char *PathMapper::db_name() { + if (db_name_[0] != '\0') { + return db_name_; + } + + if (original_mysql_path_[0] == FN_CURLIB && + original_mysql_path_[1] == FN_LIBCHAR) { + int i = 2, j = 0, len; + len = strlen(original_mysql_path_); + while (original_mysql_path_[i] != FN_LIBCHAR && i < len) { + db_name_[j++] = original_mysql_path_[i++]; + } + db_name_[j] = '\0'; + } else if (mysql_data_home_path_) { + int len = strlen(original_mysql_path_); + int mysql_data_home_len = strlen(mysql_data_home_path_); + if (len > mysql_data_home_len && + !strncmp(original_mysql_path_, + mysql_data_home_path_, + mysql_data_home_len)) { + int i = mysql_data_home_len, j = 0; + while (original_mysql_path_[i] != FN_LIBCHAR && i < len) { + db_name_[j++] = original_mysql_path_[i++]; + } + if (i == len) { + memcpy(db_name_, original_mysql_path_, len); + } else { + db_name_[j] = '\0'; + } + } else { + strcpy(db_name_, original_mysql_path_); + } + } else { + strcpy(db_name_, original_mysql_path_); + } + return db_name_; + } + + /** + * "./${db}/${table}" ==> "${table}" (with encoding first '_') + */ + const char *PathMapper::table_name() { + if (table_name_[0] != '\0') { + return table_name_; + } + + int len = strlen(original_mysql_path_); + int i = len, j = 0; + for (; original_mysql_path_[--i] != FN_LIBCHAR ;) {} + if (original_mysql_path_[i + 1] == '_') { + table_name_[j++] = '@'; + table_name_[j++] = '0'; + table_name_[j++] = '0'; + table_name_[j++] = '5'; + table_name_[j++] = 'f'; + i++; + } + for (; i < len ;) { + table_name_[j++] = original_mysql_path_[++i]; + } + table_name_[j] = '\0'; + return table_name_; + } + + /** + * "./${db}/${table}" ==> "${table}" (without encoding first '_') + */ + const char *PathMapper::mysql_table_name() { + if (mysql_table_name_[0] != '\0') { + return mysql_table_name_; + } + + int len = strlen(original_mysql_path_); + int i = len, j = 0; + for (; original_mysql_path_[--i] != FN_LIBCHAR ;) {} + for (; i < len ;) { + if (len - i - 1 >= 3 && + strncmp(original_mysql_path_ + i + 1, "#P#", 3) == 0) { + break; + } + mysql_table_name_[j++] = original_mysql_path_[++i]; + } + mysql_table_name_[j] = '\0'; + return mysql_table_name_; + } + + /** + * "./${db}/${table}" ==> "./${db}/${table}" + * "./${db}/${table}#P#xxx" ==> "./${db}/${table}" + */ + const char *PathMapper::mysql_path() { + if (mysql_path_[0] != '\0') { + return mysql_path_; + } + + int i; + int len = strlen(original_mysql_path_); + for (i = 0; i < len; i++) { + if (len - i >= 3 && + strncmp(original_mysql_path_ + i, "#P#", 3) == 0) { + break; + } + mysql_path_[i] = original_mysql_path_[i]; + } + mysql_path_[i] = '\0'; + return mysql_path_; + } + + bool PathMapper::is_internal_table_name() { + return mysql_table_name()[0] == '#'; + } +} diff --git a/storage/mroonga/lib/mrn_path_mapper.hpp b/storage/mroonga/lib/mrn_path_mapper.hpp new file mode 100644 index 00000000..60e99717 --- /dev/null +++ b/storage/mroonga/lib/mrn_path_mapper.hpp @@ -0,0 +1,55 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010 Tetsuro IKEDA + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_PATH_MAPPER_HPP_ +#define MRN_PATH_MAPPER_HPP_ + +#include <mrn_constants.hpp> + +namespace mrn { + class PathMapper { + public: + static char *default_path_prefix; + static char *default_mysql_data_home_path; + + PathMapper(const char *original_mysql_path, + const char *path_prefix=default_path_prefix, + const char *mysql_data_home_path=default_mysql_data_home_path); + const char *db_path(); + const char *db_name(); + const char *table_name(); + const char *mysql_table_name(); + const char *mysql_path(); + bool is_internal_table_name(); + bool is_temporary_table_name(); + private: + const char *original_mysql_path_; + const char *path_prefix_; + const char *mysql_data_home_path_; + char db_path_[MRN_MAX_PATH_SIZE]; + char db_name_[MRN_MAX_PATH_SIZE]; + char table_name_[MRN_MAX_PATH_SIZE]; + char mysql_table_name_[MRN_MAX_PATH_SIZE]; + char mysql_path_[MRN_MAX_PATH_SIZE]; + }; +} + +#endif /* MRN_PATH_MAPPER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_query_parser.cpp b/storage/mroonga/lib/mrn_query_parser.cpp new file mode 100644 index 00000000..92387e25 --- /dev/null +++ b/storage/mroonga/lib/mrn_query_parser.cpp @@ -0,0 +1,361 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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_query_parser.hpp" + +#include <mrn_variables.hpp> + +extern "C" { + /* Groonga's internal functions */ + int grn_atoi(const char *nptr, const char *end, const char **rest); + uint grn_atoui(const char *nptr, const char *end, const char **rest); +} + +#define MRN_CLASS_NAME "mrn::QueryParser" + +namespace mrn { + QueryParser::QueryParser(grn_ctx *ctx, + THD *thd, + grn_obj *expression, + grn_obj *default_column, + uint n_sections, + grn_obj *match_columns) + : ctx_(ctx), + thd_(thd), + expression_(expression), + default_column_(default_column), + n_sections_(n_sections), + match_columns_(match_columns) { + } + + QueryParser::~QueryParser() { + } + + grn_rc QueryParser::parse(const char *query, size_t query_length) { + MRN_DBUG_ENTER_METHOD(); + + const char *raw_query = NULL; + size_t raw_query_length = 0; + grn_operator default_operator = GRN_OP_OR; + grn_expr_flags expression_flags = 0; + parse_pragma(query, + query_length, + &raw_query, + &raw_query_length, + &default_operator, + &expression_flags); + + grn_obj *default_column = default_column_; + if (match_columns_) { + default_column = match_columns_; + } + grn_rc rc = grn_expr_parse(ctx_, + expression_, + raw_query, + raw_query_length, + default_column, + GRN_OP_MATCH, + default_operator, + expression_flags); + if (rc != GRN_SUCCESS) { + char error_message[MRN_MESSAGE_BUFFER_SIZE]; + snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE, + "failed to parse fulltext search keyword: <%.*s>: <%s>", + static_cast<int>(query_length), + query, + ctx_->errbuf); + variables::ActionOnError action = + variables::get_action_on_fulltext_query_error(thd_); + switch (action) { + case variables::ACTION_ON_ERROR_ERROR: + my_message(ER_PARSE_ERROR, error_message, MYF(0)); + break; + case variables::ACTION_ON_ERROR_ERROR_AND_LOG: + my_message(ER_PARSE_ERROR, error_message, MYF(0)); + GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message); + break; + case variables::ACTION_ON_ERROR_IGNORE: + break; + case variables::ACTION_ON_ERROR_IGNORE_AND_LOG: + GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message); + break; + } + } + + DBUG_RETURN(rc); + } + + void QueryParser::parse_pragma(const char *query, + size_t query_length, + const char **raw_query, + size_t *raw_query_length, + grn_operator *default_operator, + grn_expr_flags *flags) { + MRN_DBUG_ENTER_METHOD(); + + const char *current_query = query; + size_t current_query_length = query_length; + + *default_operator = GRN_OP_OR; + + if (current_query_length >= 4 && memcmp(current_query, "*SS ", 4) == 0) { + *raw_query = current_query + 4; + *raw_query_length = current_query_length - 4; + *flags = GRN_EXPR_SYNTAX_SCRIPT; + DBUG_VOID_RETURN; + } + + bool weight_specified = false; + *raw_query = query; + *raw_query_length = query_length; + *flags = default_expression_flags(); + if (current_query_length >= 2 && current_query[0] == '*') { + bool parsed = false; + bool done = false; + current_query++; + current_query_length--; + while (!done) { + size_t consumed_query_length = 0; + switch (current_query[0]) { + case 'D': + if (parse_pragma_d(current_query + 1, + current_query_length - 1, + default_operator, + &consumed_query_length)) { + parsed = true; + consumed_query_length += 1; + current_query += consumed_query_length; + current_query_length -= consumed_query_length; + } else { + done = true; + } + break; + case 'W': + if (parse_pragma_w(current_query + 1, + current_query_length - 1, + &consumed_query_length)) { + parsed = true; + weight_specified = true; + consumed_query_length += 1; + current_query += consumed_query_length; + current_query_length -= consumed_query_length; + } else { + done = true; + } + break; + default: + done = true; + break; + } + } + if (parsed) { + *raw_query = current_query; + *raw_query_length = current_query_length; + } + } + + // WORKAROUND: ignore the first '+' to support "+apple macintosh" pattern. + while (*raw_query_length > 0 && (*raw_query)[0] == ' ') { + (*raw_query)++; + (*raw_query_length)--; + } + if (*raw_query_length > 0 && (*raw_query)[0] == '+') { + (*raw_query)++; + (*raw_query_length)--; + } + if (!weight_specified && match_columns_) { + grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1); + } + + DBUG_VOID_RETURN; + } + + bool QueryParser::parse_pragma_w(const char *query, + size_t query_length, + size_t *consumed_query_length) { + MRN_DBUG_ENTER_METHOD(); + + *consumed_query_length = 0; + + grn_obj section_value_buffer; + GRN_UINT32_INIT(§ion_value_buffer, 0); + + MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(bool, specified_sections, n_sections_); + for (uint i = 0; i < n_sections_; ++i) { + specified_sections[i] = false; + } + + uint n_weights = 0; + while (query_length >= 1) { + if (n_weights >= 1) { + if (query[0] != ',') { + break; + } + size_t n_used_query_length = 1; + *consumed_query_length += n_used_query_length; + query_length -= n_used_query_length; + query += n_used_query_length; + if (query_length == 0) { + break; + } + } + + uint section = 0; + if ('1' <= query[0] && query[0] <= '9') { + const char *section_start = query; + const char *query_end = query + query_length; + const char *query_rest; + section = grn_atoui(section_start, query_end, &query_rest); + if (section_start == query_rest) { + break; + } + if (!(0 < section && section <= n_sections_)) { + break; + } + section -= 1; + specified_sections[section] = true; + size_t n_used_query_length = query_rest - query; + *consumed_query_length += n_used_query_length; + query_length -= n_used_query_length; + query += n_used_query_length; + } else { + break; + } + + int weight = 1; + if (query_length >= 2 && query[0] == ':') { + const char *weight_start = query + 1; + const char *query_end = query + query_length; + const char *query_rest; + weight = grn_atoi(weight_start, query_end, &query_rest); + if (weight_start == query_rest) { + break; + } + size_t n_used_query_length = query_rest - query; + *consumed_query_length += n_used_query_length; + query_length -= n_used_query_length; + query += n_used_query_length; + } + + n_weights++; + + append_section(section, + §ion_value_buffer, + weight, + n_weights); + } + + for (uint section = 0; section < n_sections_; ++section) { + if (specified_sections[section]) { + continue; + } + + ++n_weights; + + int default_weight = 1; + append_section(section, + §ion_value_buffer, + default_weight, + n_weights); + } + MRN_FREE_VARIABLE_LENGTH_ARRAYS(specified_sections); + + GRN_OBJ_FIN(ctx_, §ion_value_buffer); + + DBUG_RETURN(n_weights > 0); + } + + void QueryParser::append_section(uint section, + grn_obj *section_value_buffer, + int weight, + uint n_weights) { + MRN_DBUG_ENTER_METHOD(); + + if (!match_columns_) { + DBUG_VOID_RETURN; + } + + grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1); + GRN_UINT32_SET(ctx_, section_value_buffer, section); + grn_expr_append_const(ctx_, match_columns_, section_value_buffer, + GRN_OP_PUSH, 1); + grn_expr_append_op(ctx_, match_columns_, GRN_OP_GET_MEMBER, 2); + + if (weight != 1) { + grn_expr_append_const_int(ctx_, match_columns_, weight, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx_, match_columns_, GRN_OP_STAR, 2); + } + + if (n_weights >= 2) { + grn_expr_append_op(ctx_, match_columns_, GRN_OP_OR, 2); + } + + DBUG_VOID_RETURN; + } + + bool QueryParser::parse_pragma_d(const char *query, + size_t query_length, + grn_operator *default_operator, + size_t *consumed_query_length) { + MRN_DBUG_ENTER_METHOD(); + + bool succeeded = true; + if (query_length >= 1 && query[0] == '+') { + *default_operator = GRN_OP_AND; + *consumed_query_length = 1; + } else if (query_length >= 1 && query[0] == '-') { + *default_operator = GRN_OP_AND_NOT; + *consumed_query_length = 1; + } else if (query_length >= 2 && memcmp(query, "OR", 2) == 0) { + *default_operator = GRN_OP_OR; + *consumed_query_length = 2; + } else { + succeeded = false; + } + + DBUG_RETURN(succeeded); + } + + grn_expr_flags QueryParser::default_expression_flags() { + MRN_DBUG_ENTER_METHOD(); + + ulonglong syntax_flags = variables::get_boolean_mode_syntax_flags(thd_); + grn_expr_flags expression_flags = 0; + if (syntax_flags == variables::BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT) { + expression_flags = GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_LEADING_NOT; + } else { + if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_SYNTAX_SCRIPT) { + expression_flags |= GRN_EXPR_SYNTAX_SCRIPT; + } else { + expression_flags |= GRN_EXPR_SYNTAX_QUERY; + } + if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_COLUMN) { + expression_flags |= GRN_EXPR_ALLOW_COLUMN; + } + if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_UPDATE) { + expression_flags |= GRN_EXPR_ALLOW_UPDATE; + } + if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_LEADING_NOT) { + expression_flags |= GRN_EXPR_ALLOW_LEADING_NOT; + } + } + + DBUG_RETURN(expression_flags); + } +} diff --git a/storage/mroonga/lib/mrn_query_parser.hpp b/storage/mroonga/lib/mrn_query_parser.hpp new file mode 100644 index 00000000..ba67394d --- /dev/null +++ b/storage/mroonga/lib/mrn_query_parser.hpp @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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 +*/ + +#pragma once + +#include <mrn_mysql.h> +#include <mrn_mysql_compat.h> + +#include <groonga.h> + +namespace mrn { + class QueryParser { + public: + QueryParser(grn_ctx *ctx, + THD *thd, + grn_obj *expression, + grn_obj *default_column, + uint n_sections, + grn_obj *match_columns=NULL); + ~QueryParser(); + + grn_rc parse(const char *query, size_t query_length); + void parse_pragma(const char *query, + size_t query_length, + const char **raw_query, + size_t *raw_query_length, + grn_operator *default_operator, + grn_expr_flags *flags); + + private: + grn_ctx *ctx_; + THD *thd_; + grn_obj *expression_; + grn_obj *default_column_; + uint n_sections_; + grn_obj *match_columns_; + + bool parse_pragma_w(const char *query, + size_t query_length, + size_t *consumed_query_length); + void append_section(uint section, + grn_obj *section_value_buffer, + int weight, + uint n_weights); + bool parse_pragma_d(const char *query, + size_t query_length, + grn_operator *default_operator, + size_t *consumed_query_length); + grn_expr_flags default_expression_flags(); + }; +} diff --git a/storage/mroonga/lib/mrn_smart_bitmap.cpp b/storage/mroonga/lib/mrn_smart_bitmap.cpp new file mode 100644 index 00000000..f8fd4f72 --- /dev/null +++ b/storage/mroonga/lib/mrn_smart_bitmap.cpp @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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_smart_bitmap.hpp" + +namespace mrn { + SmartBitmap::SmartBitmap(MY_BITMAP *bitmap) + : bitmap_(bitmap) { + } + + SmartBitmap::~SmartBitmap() { + if (bitmap_) { + bitmap_free(bitmap_); + } + } + + MY_BITMAP *SmartBitmap::get() { + return bitmap_; + } + + MY_BITMAP *SmartBitmap::release() { + MY_BITMAP *bitmap = bitmap_; + bitmap_ = NULL; + return bitmap; + } +} diff --git a/storage/mroonga/lib/mrn_smart_bitmap.hpp b/storage/mroonga/lib/mrn_smart_bitmap.hpp new file mode 100644 index 00000000..5cc80df0 --- /dev/null +++ b/storage/mroonga/lib/mrn_smart_bitmap.hpp @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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 +*/ + +#pragma once + +#include <mrn_mysql.h> +#include <my_bitmap.h> + +namespace mrn { + class SmartBitmap { + public: + SmartBitmap(MY_BITMAP *bitmap); + ~SmartBitmap(); + + MY_BITMAP *get(); + MY_BITMAP *release(); + private: + MY_BITMAP *bitmap_; + }; +} diff --git a/storage/mroonga/lib/mrn_smart_grn_obj.cpp b/storage/mroonga/lib/mrn_smart_grn_obj.cpp new file mode 100644 index 00000000..845a5fb1 --- /dev/null +++ b/storage/mroonga/lib/mrn_smart_grn_obj.cpp @@ -0,0 +1,59 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2014 Kouhei Sutou <kou@clear-code.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 <string.h> + +#include "mrn_smart_grn_obj.hpp" + +namespace mrn { + SmartGrnObj::SmartGrnObj(grn_ctx *ctx, grn_obj *obj) + : ctx_(ctx), + obj_(obj) { + } + + SmartGrnObj::SmartGrnObj(grn_ctx *ctx, const char *name, int name_size) + : ctx_(ctx), + obj_(NULL) { + if (name_size < 0) { + name_size = strlen(name); + } + obj_ = grn_ctx_get(ctx_, name, name_size); + } + + SmartGrnObj::SmartGrnObj(grn_ctx *ctx, grn_id id) + : ctx_(ctx), + obj_(grn_ctx_at(ctx_, id)) { + } + + SmartGrnObj::~SmartGrnObj() { + if (obj_) { + grn_obj_unlink(ctx_, obj_); + } + } + + grn_obj *SmartGrnObj::get() { + return obj_; + } + + grn_obj *SmartGrnObj::release() { + grn_obj *obj = obj_; + obj_ = NULL; + return obj; + } +} diff --git a/storage/mroonga/lib/mrn_smart_grn_obj.hpp b/storage/mroonga/lib/mrn_smart_grn_obj.hpp new file mode 100644 index 00000000..9158145e --- /dev/null +++ b/storage/mroonga/lib/mrn_smart_grn_obj.hpp @@ -0,0 +1,40 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2014 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_SMART_GRN_OBJ_HPP_ +#define MRN_SMART_GRN_OBJ_HPP_ + +#include <groonga.h> + +namespace mrn { + class SmartGrnObj { + grn_ctx *ctx_; + grn_obj *obj_; + public: + SmartGrnObj(grn_ctx *ctx, grn_obj *obj); + SmartGrnObj(grn_ctx *ctx, const char *name, int name_size=-1); + SmartGrnObj(grn_ctx *ctx, grn_id id); + ~SmartGrnObj(); + + grn_obj *get(); + grn_obj *release(); + }; +} + +#endif // MRN_SMART_GRN_OBJ_HPP_ diff --git a/storage/mroonga/lib/mrn_table_fields_offset_mover.cpp b/storage/mroonga/lib/mrn_table_fields_offset_mover.cpp new file mode 100644 index 00000000..bc2d2f25 --- /dev/null +++ b/storage/mroonga/lib/mrn_table_fields_offset_mover.cpp @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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_table_fields_offset_mover.hpp" + +namespace mrn { + FieldTableChanger::FieldTableChanger(TABLE *table, + TABLE *new_table) + : old_table_(table), + new_table_(new_table) { + my_ptrdiff_t diff = + PTR_BYTE_DIFF(new_table_->record[0], old_table_->record[0]); + uint n_columns = old_table_->s->fields; + for (uint i = 0; i < n_columns; ++i) { + Field *field = old_table_->field[i]; + field->move_field_offset(diff); + field->table = new_table; + } + } + + FieldTableChanger::~FieldTableChanger() { + my_ptrdiff_t diff = + PTR_BYTE_DIFF(new_table_->record[0], old_table_->record[0]); + uint n_columns = old_table_->s->fields; + for (uint i = 0; i < n_columns; ++i) { + Field *field = old_table_->field[i]; + field->move_field_offset(-diff); + field->table = old_table_; + } + } +} diff --git a/storage/mroonga/lib/mrn_table_fields_offset_mover.hpp b/storage/mroonga/lib/mrn_table_fields_offset_mover.hpp new file mode 100644 index 00000000..94f96772 --- /dev/null +++ b/storage/mroonga/lib/mrn_table_fields_offset_mover.hpp @@ -0,0 +1,33 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2017 Kouhei Sutou <kou@clear-code.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 +*/ + +#pragma once + +#include <mrn_mysql.h> + +namespace mrn { + class FieldTableChanger { + public: + FieldTableChanger(TABLE *table, TABLE *new_table); + ~FieldTableChanger(); + private: + TABLE *old_table_; + TABLE *new_table_; + }; +} diff --git a/storage/mroonga/lib/mrn_time_converter.cpp b/storage/mroonga/lib/mrn_time_converter.cpp new file mode 100644 index 00000000..9bb8d89a --- /dev/null +++ b/storage/mroonga/lib/mrn_time_converter.cpp @@ -0,0 +1,295 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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_time_converter.hpp" + +#ifdef min +# undef min +#endif +#ifdef max +# undef max +#endif + +#include <limits> + +// for debug +#define MRN_CLASS_NAME "mrn::TimeConverter" + +namespace mrn { + TimeConverter::TimeConverter() { + } + + TimeConverter::~TimeConverter() { + } + + time_t TimeConverter::tm_to_time_gm(struct tm *time, bool *truncated) { + MRN_DBUG_ENTER_METHOD(); + *truncated = true; + struct tm gmdate; + time->tm_yday = -1; + time->tm_isdst = -1; + time_t sec_t = mktime(time); + if (time->tm_yday == -1) { + DBUG_RETURN(-1); + } + if (!gmtime_r(&sec_t, &gmdate)) { + DBUG_RETURN(-1); + } + int32 mrn_utc_diff_in_seconds = + ( + time->tm_mday > 25 && gmdate.tm_mday == 1 ? -1 : + time->tm_mday == 1 && gmdate.tm_mday > 25 ? 1 : + time->tm_mday - gmdate.tm_mday + ) * 24 * 60 * 60 + + (time->tm_hour - gmdate.tm_hour) * 60 * 60 + + (time->tm_min - gmdate.tm_min) * 60 + + (time->tm_sec - gmdate.tm_sec); + DBUG_PRINT("info", ("mroonga: time->tm_year=%d", time->tm_year)); + DBUG_PRINT("info", ("mroonga: time->tm_mon=%d", time->tm_mon)); + DBUG_PRINT("info", ("mroonga: time->tm_mday=%d", time->tm_mday)); + DBUG_PRINT("info", ("mroonga: time->tm_hour=%d", time->tm_hour)); + DBUG_PRINT("info", ("mroonga: time->tm_min=%d", time->tm_min)); + DBUG_PRINT("info", ("mroonga: time->tm_sec=%d", time->tm_sec)); + DBUG_PRINT("info", ("mroonga: mrn_utc_diff_in_seconds=%d", + mrn_utc_diff_in_seconds)); + if (mrn_utc_diff_in_seconds > 0) { + if (sec_t > std::numeric_limits<time_t>::max() - mrn_utc_diff_in_seconds) { + DBUG_RETURN(-1); + } + } else { + if (sec_t < std::numeric_limits<time_t>::min() - mrn_utc_diff_in_seconds) { + DBUG_RETURN(-1); + } + } + *truncated = false; + DBUG_RETURN(sec_t + mrn_utc_diff_in_seconds); + } + + long long int TimeConverter::tm_to_grn_time(struct tm *time, int usec, + bool *truncated) { + MRN_DBUG_ENTER_METHOD(); + + long long int sec = tm_to_time_gm(time, truncated); + + DBUG_PRINT("info", ("mroonga: sec=%lld", sec)); + DBUG_PRINT("info", ("mroonga: usec=%d", usec)); + + long long int grn_time = *truncated ? 0 : GRN_TIME_PACK(sec, usec); + + DBUG_RETURN(grn_time); + } + + long long int TimeConverter::mysql_time_to_grn_time(MYSQL_TIME *mysql_time, + bool *truncated) { + MRN_DBUG_ENTER_METHOD(); + + int usec = mysql_time->second_part; + long long int grn_time = 0; + + *truncated = false; + switch (mysql_time->time_type) { + case MYSQL_TIMESTAMP_DATE: + { + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE")); + struct tm date; + memset(&date, 0, sizeof(struct tm)); + date.tm_year = mysql_time->year - TM_YEAR_BASE; + if (mysql_time->month > 0) { + date.tm_mon = mysql_time->month - 1; + } else { + date.tm_mon = 0; + *truncated = true; + } + if (mysql_time->day > 0) { + date.tm_mday = mysql_time->day; + } else { + date.tm_mday = 1; + *truncated = true; + } + DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year)); + DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon)); + DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday)); + bool tm_truncated = false; + grn_time = tm_to_grn_time(&date, usec, &tm_truncated); + if (tm_truncated) { + *truncated = true; + } + } + break; + case MYSQL_TIMESTAMP_DATETIME: + { + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME")); + struct tm datetime; + memset(&datetime, 0, sizeof(struct tm)); + datetime.tm_year = mysql_time->year - TM_YEAR_BASE; + if (mysql_time->month > 0) { + datetime.tm_mon = mysql_time->month - 1; + } else { + datetime.tm_mon = 0; + *truncated = true; + } + if (mysql_time->day > 0) { + datetime.tm_mday = mysql_time->day; + } else { + datetime.tm_mday = 1; + *truncated = true; + } + datetime.tm_hour = mysql_time->hour; + datetime.tm_min = mysql_time->minute; + datetime.tm_sec = mysql_time->second; + DBUG_PRINT("info", ("mroonga: tm_year=%d", datetime.tm_year)); + DBUG_PRINT("info", ("mroonga: tm_mon=%d", datetime.tm_mon)); + DBUG_PRINT("info", ("mroonga: tm_mday=%d", datetime.tm_mday)); + DBUG_PRINT("info", ("mroonga: tm_hour=%d", datetime.tm_hour)); + DBUG_PRINT("info", ("mroonga: tm_min=%d", datetime.tm_min)); + DBUG_PRINT("info", ("mroonga: tm_sec=%d", datetime.tm_sec)); + bool tm_truncated = false; + grn_time = tm_to_grn_time(&datetime, usec, &tm_truncated); + if (tm_truncated) { + *truncated = true; + } + } + break; + case MYSQL_TIMESTAMP_TIME: + { + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME")); + int sec = + mysql_time->hour * 60 * 60 + + mysql_time->minute * 60 + + mysql_time->second; + DBUG_PRINT("info", ("mroonga: sec=%d", sec)); + grn_time = GRN_TIME_PACK(sec, usec); + if (mysql_time->neg) { + grn_time = -grn_time; + } + } + break; + default: + DBUG_PRINT("info", ("mroonga: default")); + grn_time = 0; + break; + } + + DBUG_RETURN(grn_time); + } + + void TimeConverter::grn_time_to_mysql_time(long long int grn_time, + MYSQL_TIME *mysql_time) { + MRN_DBUG_ENTER_METHOD(); + long long int sec; + int usec; + GRN_TIME_UNPACK(grn_time, sec, usec); + DBUG_PRINT("info", ("mroonga: sec=%lld", sec)); + DBUG_PRINT("info", ("mroonga: usec=%d", usec)); + switch (mysql_time->time_type) { + case MYSQL_TIMESTAMP_DATE: + { + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE")); + struct tm date; + time_t sec_t = sec; + // TODO: Add error check + gmtime_r(&sec_t, &date); + DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year)); + mysql_time->year = date.tm_year + TM_YEAR_BASE; + DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon)); + mysql_time->month = date.tm_mon + 1; + DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday)); + mysql_time->day = date.tm_mday; + } + break; + case MYSQL_TIMESTAMP_DATETIME: + { + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME")); + struct tm date; + time_t sec_t = sec; + // TODO: Add error check + gmtime_r(&sec_t, &date); + DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year)); + mysql_time->year = date.tm_year + TM_YEAR_BASE; + DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon)); + mysql_time->month = date.tm_mon + 1; + DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday)); + mysql_time->day = date.tm_mday; + DBUG_PRINT("info", ("mroonga: tm_hour=%d", date.tm_hour)); + mysql_time->hour = date.tm_hour; + DBUG_PRINT("info", ("mroonga: tm_min=%d", date.tm_min)); + mysql_time->minute = date.tm_min; + DBUG_PRINT("info", ("mroonga: tm_sec=%d", date.tm_sec)); + mysql_time->second = date.tm_sec; + mysql_time->second_part = usec; + } + break; + case MYSQL_TIMESTAMP_TIME: + DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME")); + if (sec < 0) { + mysql_time->neg = true; + sec = -sec; + } + mysql_time->hour = static_cast<unsigned int>(sec / 60 / 60); + mysql_time->minute = sec / 60 % 60; + mysql_time->second = sec % 60; + mysql_time->second_part = usec; + break; + default: + DBUG_PRINT("info", ("mroonga: default")); + break; + } + DBUG_VOID_RETURN; + } + + long long int TimeConverter::mysql_datetime_to_grn_time(long long int mysql_datetime, + bool *truncated) { + MRN_DBUG_ENTER_METHOD(); + + MYSQL_TIME mysql_time; + mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME; + mysql_time.neg = 0; + mysql_time.second_part = 0; + mysql_time.second = (mysql_datetime % 100); + mysql_time.minute = (mysql_datetime / 100 % 100); + mysql_time.hour = (mysql_datetime / 10000 % 100); + mysql_time.day = (mysql_datetime / 1000000 % 100); + mysql_time.month = (mysql_datetime / 100000000 % 100); + mysql_time.year = (mysql_datetime / 10000000000LL % 10000); + + long long int grn_time = mysql_time_to_grn_time(&mysql_time, truncated); + + DBUG_RETURN(grn_time); + } + + long long int TimeConverter::grn_time_to_mysql_datetime(long long int grn_time) { + MRN_DBUG_ENTER_METHOD(); + + MYSQL_TIME mysql_time; + mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME; + + grn_time_to_mysql_time(grn_time, &mysql_time); + + long long int mysql_datetime = + (mysql_time.second * 1) + + (mysql_time.minute * 100) + + (mysql_time.hour * 10000) + + (mysql_time.day * 1000000) + + (mysql_time.month * 100000000) + + (mysql_time.year * 10000000000LL); + + DBUG_RETURN(mysql_datetime); + } +} diff --git a/storage/mroonga/lib/mrn_time_converter.hpp b/storage/mroonga/lib/mrn_time_converter.hpp new file mode 100644 index 00000000..2f7f00f5 --- /dev/null +++ b/storage/mroonga/lib/mrn_time_converter.hpp @@ -0,0 +1,51 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_TIME_CONVERTER_HPP_ +#define MRN_TIME_CONVERTER_HPP_ + +#include <mrn_mysql_compat.h> + +#include <groonga.h> + +namespace mrn { + class TimeConverter { + public: + static const long long int TM_YEAR_BASE = 1900; + + TimeConverter(); + ~TimeConverter(); + + long long int mysql_time_to_grn_time(MYSQL_TIME *mysql_time, + bool *truncated); + long long int mysql_datetime_to_grn_time(long long int mysql_datetime, + bool *truncated); + + long long int tm_to_grn_time(struct tm *time, int usec, bool *truncated); + + void grn_time_to_mysql_time(long long int grn_time, MYSQL_TIME *mysql_time); + long long int grn_time_to_mysql_datetime(long long int grn_time); + + private: + time_t tm_to_time_gm(struct tm *time, bool *truncated); + }; +} + +#endif /* MRN_TIME_CONVERTER_HPP_ */ diff --git a/storage/mroonga/lib/mrn_value_decoder.cpp b/storage/mroonga/lib/mrn_value_decoder.cpp new file mode 100644 index 00000000..3b2eed67 --- /dev/null +++ b/storage/mroonga/lib/mrn_value_decoder.cpp @@ -0,0 +1,75 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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_value_decoder.hpp" + +#if MYSQL_VERSION_ID >= 50706 && !defined(MRN_MARIADB_P) +# define MRN_DEST_IS_POINTER +#endif + +namespace mrn { + namespace value_decoder { + void decode(uint16 *dest, const uchar *source) { + MRN_DBUG_ENTER_FUNCTION(); +#ifdef MRN_DEST_IS_POINTER + ushortget(dest, source); +#else + uint16 value; + ushortget(value, source); + *dest = value; +#endif + DBUG_VOID_RETURN; + }; + + void decode(float *dest, const uchar *source) { + MRN_DBUG_ENTER_FUNCTION(); +#ifdef MRN_DEST_IS_POINTER + float4get(dest, source); +#else + float value; + float4get(value, source); + *dest = value; +#endif + DBUG_VOID_RETURN; + }; + + void decode(double *dest, const uchar *source) { + MRN_DBUG_ENTER_FUNCTION(); +#ifdef MRN_DEST_IS_POINTER + float8get(dest, source); +#else + double value; + float8get(value, source); + *dest = value; +#endif + DBUG_VOID_RETURN; + } + void decode(long long int *dest, const uchar *source) { + MRN_DBUG_ENTER_FUNCTION(); +#ifdef MRN_DEST_IS_POINTER + longlongget(dest, source); +#else + long long int value; + longlongget(value, source); + *dest = value; +#endif + DBUG_VOID_RETURN; + } + } +} diff --git a/storage/mroonga/lib/mrn_value_decoder.hpp b/storage/mroonga/lib/mrn_value_decoder.hpp new file mode 100644 index 00000000..7919a88e --- /dev/null +++ b/storage/mroonga/lib/mrn_value_decoder.hpp @@ -0,0 +1,34 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_VALUE_DECODER_HPP_ +#define MRN_VALUE_DECODER_HPP_ + +#include <mrn_mysql.h> + +namespace mrn { + namespace value_decoder { + void decode(uint16 *dest, const uchar *source); + void decode(float *dest, const uchar *source); + void decode(double *dest, const uchar *source); + void decode(long long int *dest, const uchar *source); + } +} + +#endif // MRN_VALUE_DECODER_HPP_ diff --git a/storage/mroonga/lib/mrn_windows.hpp b/storage/mroonga/lib/mrn_windows.hpp new file mode 100644 index 00000000..cfe35b41 --- /dev/null +++ b/storage/mroonga/lib/mrn_windows.hpp @@ -0,0 +1,31 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + Copyright(C) 2010 Tetsuro IKEDA + Copyright(C) 2010-2013 Kentoku SHIBA + Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.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 +*/ + +#ifndef MRN_WINDOWS_HPP_ +#define MRN_WINDOWS_HPP_ + +#if defined(_WIN32) || defined(_WIN64) +# define MRN_API __declspec(dllexport) +#else +# define MRN_API +#endif + +#endif /* MRN_WINDOWS_HPP_ */ |