diff options
Diffstat (limited to 'storage/mroonga/lib/mrn_condition_converter.cpp')
-rw-r--r-- | storage/mroonga/lib/mrn_condition_converter.cpp | 637 |
1 files changed, 637 insertions, 0 deletions
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; + } +} |