summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/lib/mrn_condition_converter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/mroonga/lib/mrn_condition_converter.cpp')
-rw-r--r--storage/mroonga/lib/mrn_condition_converter.cpp637
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;
+ }
+}