1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2010-2013 Kentoku SHIBA
4   Copyright(C) 2011-2017 Kouhei Sutou <kou@clear-code.com>
5 
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License as published by the Free Software Foundation; either
9   version 2.1 of the License, or (at your option) any later version.
10 
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
19 */
20 
21 #include "mrn_count_skip_checker.hpp"
22 
23 #include <item_sum.h>
24 
25 // for debug
26 #define MRN_CLASS_NAME "mrn::CountSkipChecker"
27 
28 namespace mrn {
CountSkipChecker(grn_ctx * ctx,TABLE * table,SELECT_LEX * select_lex,KEY * key_info,key_part_map target_key_part_map,bool is_storage_mode)29   CountSkipChecker::CountSkipChecker(grn_ctx *ctx,
30                                      TABLE *table,
31                                      SELECT_LEX *select_lex,
32                                      KEY *key_info,
33                                      key_part_map target_key_part_map,
34                                      bool is_storage_mode)
35     : ctx_(ctx),
36       table_(table),
37       select_lex_(select_lex),
38       key_info_(key_info),
39       target_key_part_map_(target_key_part_map),
40       is_storage_mode_(is_storage_mode) {
41   }
42 
~CountSkipChecker()43   CountSkipChecker::~CountSkipChecker() {
44   }
45 
check()46   bool CountSkipChecker::check() {
47     MRN_DBUG_ENTER_METHOD();
48 
49     if (select_lex_->item_list.elements != 1) {
50       GRN_LOG(ctx_, GRN_LOG_DEBUG,
51               "[mroonga][count-skip][false] not only one item: %u",
52               select_lex_->item_list.elements);
53       DBUG_RETURN(false);
54     }
55     if (select_lex_->group_list.elements > 0) {
56       GRN_LOG(ctx_, GRN_LOG_DEBUG,
57               "[mroonga][count-skip][false] have groups: %u",
58               select_lex_->group_list.elements);
59       DBUG_RETURN(false);
60     }
61     if (MRN_SELECT_LEX_GET_HAVING_COND(select_lex_)) {
62       GRN_LOG(ctx_, GRN_LOG_DEBUG,
63               "[mroonga][count-skip][false] have HAVING");
64       DBUG_RETURN(false);
65     }
66     if (select_lex_->table_list.elements != 1) {
67       GRN_LOG(ctx_, GRN_LOG_DEBUG,
68               "[mroonga][count-skip][false] not only one table: %u",
69               select_lex_->table_list.elements);
70       DBUG_RETURN(false);
71     }
72 
73     Item *info = static_cast<Item *>(select_lex_->item_list.first_node()->info);
74     if (info->type() != Item::SUM_FUNC_ITEM) {
75       GRN_LOG(ctx_, GRN_LOG_DEBUG,
76               "[mroonga][count-skip][false] item isn't sum function: %u",
77               info->type());
78       DBUG_RETURN(false);
79     }
80     Item_sum *sum_item = static_cast<Item_sum *>(info);
81     if (sum_item->sum_func() != Item_sum::COUNT_FUNC) {
82       GRN_LOG(ctx_, GRN_LOG_DEBUG,
83               "[mroonga][count-skip][false] not COUNT: %u",
84               sum_item->sum_func());
85       DBUG_RETURN(false);
86     }
87     if (ITEM_SUM_GET_NEST_LEVEL(sum_item) != 0 ||
88         ITEM_SUM_GET_AGGR_LEVEL(sum_item) != 0 ||
89         ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item) != -1 ||
90         sum_item->max_sum_func_level != -1) {
91       GRN_LOG(ctx_, GRN_LOG_DEBUG,
92               "[mroonga][count-skip][false] not simple COUNT(*): %d:%d:%d:%d",
93               ITEM_SUM_GET_NEST_LEVEL(sum_item),
94               ITEM_SUM_GET_AGGR_LEVEL(sum_item),
95               ITEM_SUM_GET_MAX_AGGR_LEVEL(sum_item),
96               sum_item->max_sum_func_level);
97       DBUG_RETURN(false);
98     }
99 
100     Item *where = MRN_SELECT_LEX_GET_WHERE_COND(select_lex_);
101     if (!where) {
102       if (is_storage_mode_) {
103         GRN_LOG(ctx_, GRN_LOG_DEBUG,
104                 "[mroonga][count-skip][true] no condition");
105         DBUG_RETURN(true);
106       } else {
107         GRN_LOG(ctx_, GRN_LOG_DEBUG,
108                 "[mroonga][count-skip][false] no condition with wrapper mode");
109         DBUG_RETURN(false);
110       }
111     }
112 
113     bool skippable = is_skippable(where);
114     DBUG_RETURN(skippable);
115   }
116 
is_skippable(Item * where)117   bool CountSkipChecker::is_skippable(Item *where) {
118     MRN_DBUG_ENTER_METHOD();
119 
120     bool skippable = false;
121     switch (where->type()) {
122     case Item::COND_ITEM:
123       {
124         Item_cond *cond_item = static_cast<Item_cond *>(where);
125         skippable = is_skippable(cond_item);
126         if (skippable) {
127           GRN_LOG(ctx_, GRN_LOG_DEBUG,
128                   "[mroonga][count-skip][true] skippable multiple conditions");
129         }
130       }
131       break;
132     case Item::FUNC_ITEM:
133       {
134         Item_func *func_item = static_cast<Item_func *>(where);
135         if (func_item->functype() == Item_func::FT_FUNC) {
136           if (select_lex_->select_n_where_fields == 1) {
137             GRN_LOG(ctx_, GRN_LOG_DEBUG,
138                     "[mroonga][count-skip][true] "
139                     "only one full text search condition");
140             DBUG_RETURN(true);
141           } else {
142             GRN_LOG(ctx_, GRN_LOG_DEBUG,
143                     "[mroonga][count-skip][false] "
144                     "full text search condition and more conditions: %u",
145                     select_lex_->select_n_where_fields);
146             DBUG_RETURN(false);
147           }
148         } else {
149           skippable = is_skippable(func_item);
150           if (skippable) {
151             GRN_LOG(ctx_, GRN_LOG_DEBUG,
152                     "[mroonga][count-skip][true] skippable condition");
153           }
154         }
155       }
156       break;
157     default:
158       GRN_LOG(ctx_, GRN_LOG_DEBUG,
159               "[mroonga][count-skip][false] unsupported top level item: %u",
160               where->type());
161       break;
162     }
163 
164     DBUG_RETURN(skippable);
165   }
166 
is_skippable(Item_cond * cond_item)167   bool CountSkipChecker::is_skippable(Item_cond *cond_item) {
168     MRN_DBUG_ENTER_METHOD();
169 
170     List_iterator<Item> iterator(*(cond_item->argument_list()));
171     Item *sub_item;
172     while ((sub_item = iterator++)) {
173       if (sub_item->type() != Item::FUNC_ITEM) {
174         GRN_LOG(ctx_, GRN_LOG_DEBUG,
175                 "[mroonga][count-skip][false] "
176                 "sub condition isn't function item: %u",
177                 sub_item->type());
178         DBUG_RETURN(false);
179       }
180       if (!is_skippable(static_cast<Item_func *>(sub_item))) {
181         DBUG_RETURN(false);
182       }
183     }
184     DBUG_RETURN(true);
185   }
186 
is_skippable(Item_func * func_item)187   bool CountSkipChecker::is_skippable(Item_func *func_item) {
188     MRN_DBUG_ENTER_METHOD();
189 
190     switch (func_item->functype()) {
191     case Item_func::EQ_FUNC:
192     case Item_func::EQUAL_FUNC:
193     case Item_func::NE_FUNC:
194     case Item_func::LT_FUNC:
195     case Item_func::LE_FUNC:
196     case Item_func::GE_FUNC:
197     case Item_func::GT_FUNC:
198       {
199         Item **arguments = func_item->arguments();
200         Item *left_item = arguments[0];
201         if (left_item->type() != Item::FIELD_ITEM) {
202           GRN_LOG(ctx_, GRN_LOG_DEBUG,
203                   "[mroonga][count-skip][false] not field: %u:%u",
204                   func_item->functype(),
205                   left_item->type());
206           DBUG_RETURN(false);
207         }
208 
209         bool skippable = is_skippable(static_cast<Item_field *>(left_item));
210         DBUG_RETURN(skippable);
211       }
212       break;
213     case Item_func::BETWEEN:
214       {
215         Item **arguments = func_item->arguments();
216         Item *target_item = arguments[0];
217         if (target_item->type() != Item::FIELD_ITEM) {
218           GRN_LOG(ctx_, GRN_LOG_DEBUG,
219                   "[mroonga][count-skip][false] BETWEEN target isn't field: %u",
220                   target_item->type());
221           DBUG_RETURN(false);
222         }
223 
224         bool skippable = is_skippable(static_cast<Item_field *>(target_item));
225         DBUG_RETURN(skippable);
226       }
227       break;
228     case Item_func::MULT_EQUAL_FUNC:
229 #ifdef MRN_HAVE_ITEM_EQUAL_FIELDS_ITERATOR
230       {
231         Item_equal *equal_item = static_cast<Item_equal *>(func_item);
232         Item_equal_fields_iterator iterator(*equal_item);
233         Item *field_item;
234         while ((field_item = iterator++)) {
235           bool skippable = is_skippable(static_cast<Item_field *>(field_item));
236           if (!skippable) {
237             DBUG_RETURN(skippable);
238           }
239         }
240         DBUG_RETURN(true);
241       }
242 #endif
243       break;
244     default:
245       break;
246     }
247 
248     GRN_LOG(ctx_, GRN_LOG_DEBUG,
249             "[mroonga][count-skip][false] unsupported function item: %u",
250             func_item->functype());
251     DBUG_RETURN(false);
252   }
253 
is_skippable(Item_field * field_item)254   bool CountSkipChecker::is_skippable(Item_field *field_item) {
255     MRN_DBUG_ENTER_METHOD();
256 
257     Field *field = field_item->field;
258     if (!field) {
259       GRN_LOG(ctx_, GRN_LOG_DEBUG,
260               "[mroonga][count-skip][false] field is missing");
261       DBUG_RETURN(false);
262     }
263 
264     if (field->table != table_) {
265       GRN_LOG(ctx_, GRN_LOG_DEBUG,
266               "[mroonga][count-skip][false] external table's field");
267       DBUG_RETURN(false);
268     }
269 
270     if (!key_info_) {
271       GRN_LOG(ctx_, GRN_LOG_DEBUG,
272               "[mroonga][count-skip][false] no active index: <%s>:<%s>",
273               *(field->table_name),
274               field->field_name.str);
275       DBUG_RETURN(false);
276     }
277 
278     uint i;
279     KEY_PART_INFO *key_part = key_info_->key_part;
280     for (i = 0; i < KEY_N_KEY_PARTS(key_info_); i++) {
281       if (key_part[i].field == field) {
282         if ((target_key_part_map_ >> i) & 1) {
283           DBUG_RETURN(true);
284         } else {
285           GRN_LOG(ctx_, GRN_LOG_DEBUG,
286                   "[mroonga][count-skip][false] "
287                   "field's index are out of key part map: %u:%lu: <%s>:<%s>",
288                   i,
289                   target_key_part_map_,
290                   *(field->table_name),
291                   field->field_name.str);
292           DBUG_RETURN(false);
293         }
294       }
295     }
296 
297     GRN_LOG(ctx_, GRN_LOG_DEBUG,
298             "[mroonga][count-skip][false] field isn't indexed: <%s>:<%s>",
299             *(field->table_name),
300             field->field_name.str);
301     DBUG_RETURN(false);
302   }
303 }
304