summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/lib
diff options
context:
space:
mode:
Diffstat (limited to 'storage/mroonga/lib')
-rw-r--r--storage/mroonga/lib/Makefile.am23
-rw-r--r--storage/mroonga/lib/libmrn_need_mysql_sources.am50
-rw-r--r--storage/mroonga/lib/libmrn_no_mysql_sources.am9
-rw-r--r--storage/mroonga/lib/libmysqlservices_compat_sources.am2
-rw-r--r--storage/mroonga/lib/mrn_auto_increment_value_lock.cpp42
-rw-r--r--storage/mroonga/lib/mrn_auto_increment_value_lock.hpp36
-rw-r--r--storage/mroonga/lib/mrn_column_name.cpp69
-rw-r--r--storage/mroonga/lib/mrn_column_name.hpp39
-rw-r--r--storage/mroonga/lib/mrn_condition_converter.cpp637
-rw-r--r--storage/mroonga/lib/mrn_condition_converter.hpp85
-rw-r--r--storage/mroonga/lib/mrn_context_pool.cpp120
-rw-r--r--storage/mroonga/lib/mrn_context_pool.hpp41
-rw-r--r--storage/mroonga/lib/mrn_count_skip_checker.cpp303
-rw-r--r--storage/mroonga/lib/mrn_count_skip_checker.hpp57
-rw-r--r--storage/mroonga/lib/mrn_current_thread.hpp27
-rw-r--r--storage/mroonga/lib/mrn_database.cpp89
-rw-r--r--storage/mroonga/lib/mrn_database.hpp47
-rw-r--r--storage/mroonga/lib/mrn_database_manager.cpp364
-rw-r--r--storage/mroonga/lib/mrn_database_manager.hpp52
-rw-r--r--storage/mroonga/lib/mrn_database_repairer.cpp300
-rw-r--r--storage/mroonga/lib/mrn_database_repairer.hpp67
-rw-r--r--storage/mroonga/lib/mrn_debug_column_access.cpp36
-rw-r--r--storage/mroonga/lib/mrn_debug_column_access.hpp38
-rw-r--r--storage/mroonga/lib/mrn_encoding.cpp242
-rw-r--r--storage/mroonga/lib/mrn_encoding.hpp36
-rw-r--r--storage/mroonga/lib/mrn_external_lock.cpp43
-rw-r--r--storage/mroonga/lib/mrn_external_lock.hpp38
-rw-r--r--storage/mroonga/lib/mrn_field_normalizer.cpp145
-rw-r--r--storage/mroonga/lib/mrn_field_normalizer.hpp47
-rw-r--r--storage/mroonga/lib/mrn_grn.hpp39
-rw-r--r--storage/mroonga/lib/mrn_index_column_name.cpp96
-rw-r--r--storage/mroonga/lib/mrn_index_column_name.hpp43
-rw-r--r--storage/mroonga/lib/mrn_index_table_name.cpp136
-rw-r--r--storage/mroonga/lib/mrn_index_table_name.hpp55
-rw-r--r--storage/mroonga/lib/mrn_lock.cpp36
-rw-r--r--storage/mroonga/lib/mrn_lock.hpp37
-rw-r--r--storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp33
-rw-r--r--storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp35
-rw-r--r--storage/mroonga/lib/mrn_multiple_column_key_codec.cpp712
-rw-r--r--storage/mroonga/lib/mrn_multiple_column_key_codec.hpp100
-rw-r--r--storage/mroonga/lib/mrn_mysqlservices.cpp28
-rw-r--r--storage/mroonga/lib/mrn_operation.cpp51
-rw-r--r--storage/mroonga/lib/mrn_operation.hpp42
-rw-r--r--storage/mroonga/lib/mrn_operations.cpp401
-rw-r--r--storage/mroonga/lib/mrn_operations.hpp60
-rw-r--r--storage/mroonga/lib/mrn_parameters_parser.cpp177
-rw-r--r--storage/mroonga/lib/mrn_parameters_parser.hpp59
-rw-r--r--storage/mroonga/lib/mrn_path_mapper.cpp229
-rw-r--r--storage/mroonga/lib/mrn_path_mapper.hpp55
-rw-r--r--storage/mroonga/lib/mrn_query_parser.cpp361
-rw-r--r--storage/mroonga/lib/mrn_query_parser.hpp67
-rw-r--r--storage/mroonga/lib/mrn_smart_bitmap.cpp42
-rw-r--r--storage/mroonga/lib/mrn_smart_bitmap.hpp36
-rw-r--r--storage/mroonga/lib/mrn_smart_grn_obj.cpp59
-rw-r--r--storage/mroonga/lib/mrn_smart_grn_obj.hpp40
-rw-r--r--storage/mroonga/lib/mrn_table_fields_offset_mover.cpp47
-rw-r--r--storage/mroonga/lib/mrn_table_fields_offset_mover.hpp33
-rw-r--r--storage/mroonga/lib/mrn_time_converter.cpp295
-rw-r--r--storage/mroonga/lib/mrn_time_converter.hpp51
-rw-r--r--storage/mroonga/lib/mrn_value_decoder.cpp75
-rw-r--r--storage/mroonga/lib/mrn_value_decoder.hpp34
-rw-r--r--storage/mroonga/lib/mrn_windows.hpp31
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(&section_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,
+ &section_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,
+ &section_value_buffer,
+ default_weight,
+ n_weights);
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(specified_sections);
+
+ GRN_OBJ_FIN(ctx_, &section_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_ */