/* -*- c-basic-offset: 2 -*- */ /* Copyright(C) 2010-2013 Kentoku SHIBA Copyright(C) 2011-2017 Kouhei Sutou 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 // 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() = default; 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(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(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(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(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 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(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(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(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(func_item); Item_equal_fields_iterator iterator(*equal_item); Item *field_item; while ((field_item = iterator++)) { bool skippable = is_skippable(static_cast(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); } }