mariadb/storage/mroonga/lib/mrn_count_skip_checker.cpp

303 lines
9.7 KiB
C++

/* -*- 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-1301 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);
}
}