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-1301 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
28namespace mrn {
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
43 CountSkipChecker::~CountSkipChecker() {
44 }
45
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
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
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
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
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