summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/lib/mrn_count_skip_checker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/mroonga/lib/mrn_count_skip_checker.cpp')
-rw-r--r--storage/mroonga/lib/mrn_count_skip_checker.cpp303
1 files changed, 303 insertions, 0 deletions
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);
+ }
+}