1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2013-2017 Kouhei Sutou <kou@clear-code.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#include "mrn_condition_converter.hpp"
21#include "mrn_time_converter.hpp"
22#include "mrn_smart_grn_obj.hpp"
23
24// for debug
25#define MRN_CLASS_NAME "mrn::ConditionConverter"
26
27#ifdef MRN_ITEM_HAVE_ITEM_NAME
28# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->item_name.ptr())
29# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->item_name.length())
30#else
31# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->name.str)
32# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->name.length)
33#endif
34
35namespace mrn {
36 ConditionConverter::ConditionConverter(grn_ctx *ctx, grn_obj *table,
37 bool is_storage_mode)
38 : ctx_(ctx),
39 table_(table),
40 is_storage_mode_(is_storage_mode) {
41 GRN_TEXT_INIT(&column_name_, 0);
42 GRN_VOID_INIT(&value_);
43 }
44
45 ConditionConverter::~ConditionConverter() {
46 grn_obj_unlink(ctx_, &column_name_);
47 grn_obj_unlink(ctx_, &value_);
48 }
49
50 bool ConditionConverter::is_convertable(const Item *item) {
51 MRN_DBUG_ENTER_METHOD();
52
53 if (!item) {
54 DBUG_RETURN(false);
55 }
56
57 switch (item->type()) {
58 case Item::COND_ITEM:
59 {
60 const Item_cond *cond_item = reinterpret_cast<const Item_cond *>(item);
61 bool convertable = is_convertable(cond_item);
62 DBUG_RETURN(convertable);
63 }
64 break;
65 case Item::FUNC_ITEM:
66 {
67 const Item_func *func_item = reinterpret_cast<const Item_func *>(item);
68 bool convertable = is_convertable(func_item);
69 DBUG_RETURN(convertable);
70 }
71 break;
72 default:
73 DBUG_RETURN(false);
74 break;
75 }
76
77 DBUG_RETURN(false);
78 }
79
80 bool ConditionConverter::is_convertable(const Item_cond *cond_item) {
81 MRN_DBUG_ENTER_METHOD();
82
83 if (!is_storage_mode_) {
84 DBUG_RETURN(false);
85 }
86
87 if (cond_item->functype() != Item_func::COND_AND_FUNC) {
88 DBUG_RETURN(false);
89 }
90
91 List<Item> *argument_list =
92 const_cast<Item_cond *>(cond_item)->argument_list();
93 List_iterator<Item> iterator(*argument_list);
94 const Item *sub_item;
95 while ((sub_item = iterator++)) {
96 if (!is_convertable(sub_item)) {
97 DBUG_RETURN(false);
98 }
99 }
100
101 DBUG_RETURN(true);
102 }
103
104 bool ConditionConverter::is_convertable(const Item_func *func_item) {
105 MRN_DBUG_ENTER_METHOD();
106
107 switch (func_item->functype()) {
108 case Item_func::EQ_FUNC:
109 case Item_func::LT_FUNC:
110 case Item_func::LE_FUNC:
111 case Item_func::GE_FUNC:
112 case Item_func::GT_FUNC:
113 if (!is_storage_mode_) {
114 DBUG_RETURN(false);
115 }
116 {
117 Item **arguments = func_item->arguments();
118 Item *left_item = arguments[0];
119 Item *right_item = arguments[1];
120 if (left_item->type() != Item::FIELD_ITEM) {
121 DBUG_RETURN(false);
122 }
123 if (!right_item->basic_const_item()) {
124 DBUG_RETURN(false);
125 }
126
127 bool convertable =
128 is_convertable_binary_operation(static_cast<Item_field *>(left_item),
129 right_item,
130 func_item->functype());
131 DBUG_RETURN(convertable);
132 }
133 break;
134 case Item_func::FT_FUNC:
135 DBUG_RETURN(true);
136 break;
137 case Item_func::BETWEEN:
138 if (!is_storage_mode_) {
139 DBUG_RETURN(false);
140 }
141 {
142 Item **arguments = func_item->arguments();
143 Item *target_item = arguments[0];
144 Item *min_item = arguments[1];
145 Item *max_item = arguments[2];
146 if (target_item->type() != Item::FIELD_ITEM) {
147 DBUG_RETURN(false);
148 }
149 if (!min_item->basic_const_item()) {
150 DBUG_RETURN(false);
151 }
152 if (!max_item->basic_const_item()) {
153 DBUG_RETURN(false);
154 }
155
156 bool convertable =
157 is_convertable_between(static_cast<Item_field *>(target_item),
158 min_item,
159 max_item);
160 DBUG_RETURN(convertable);
161 }
162 default:
163 DBUG_RETURN(false);
164 break;
165 }
166
167 DBUG_RETURN(true);
168 }
169
170 bool ConditionConverter::is_convertable_binary_operation(
171 const Item_field *field_item,
172 Item *value_item,
173 Item_func::Functype func_type) {
174 MRN_DBUG_ENTER_METHOD();
175
176 bool convertable = false;
177
178 enum_field_types field_type = field_item->field->real_type();
179 NormalizedType normalized_type = normalize_field_type(field_type);
180 switch (normalized_type) {
181 case STRING_TYPE:
182 if (value_item->type() == Item::STRING_ITEM &&
183 func_type == Item_func::EQ_FUNC) {
184 convertable = have_index(field_item, GRN_OP_EQUAL);
185 }
186 break;
187 case INT_TYPE:
188 if (field_type == MYSQL_TYPE_ENUM) {
189 convertable = (value_item->type() == Item::STRING_ITEM ||
190 value_item->type() == Item::INT_ITEM);
191 } else {
192 convertable = value_item->type() == Item::INT_ITEM;
193 }
194 break;
195 case TIME_TYPE:
196 if (is_valid_time_value(field_item, value_item)) {
197 convertable = have_index(field_item, func_type);
198 }
199 break;
200 case UNSUPPORTED_TYPE:
201 break;
202 }
203
204 DBUG_RETURN(convertable);
205 }
206
207 bool ConditionConverter::is_convertable_between(const Item_field *field_item,
208 Item *min_item,
209 Item *max_item) {
210 MRN_DBUG_ENTER_METHOD();
211
212 bool convertable = false;
213
214 enum_field_types field_type = field_item->field->type();
215 NormalizedType normalized_type = normalize_field_type(field_type);
216 switch (normalized_type) {
217 case STRING_TYPE:
218 if (min_item->type() == Item::STRING_ITEM &&
219 max_item->type() == Item::STRING_ITEM) {
220 convertable = have_index(field_item, GRN_OP_LESS);
221 }
222 break;
223 case INT_TYPE:
224 if (min_item->type() == Item::INT_ITEM &&
225 max_item->type() == Item::INT_ITEM) {
226 convertable = have_index(field_item, GRN_OP_LESS);
227 }
228 break;
229 case TIME_TYPE:
230 if (is_valid_time_value(field_item, min_item) &&
231 is_valid_time_value(field_item, max_item)) {
232 convertable = have_index(field_item, GRN_OP_LESS);
233 }
234 break;
235 case UNSUPPORTED_TYPE:
236 break;
237 }
238
239 DBUG_RETURN(convertable);
240 }
241
242 bool ConditionConverter::is_valid_time_value(const Item_field *field_item,
243 Item *value_item) {
244 MRN_DBUG_ENTER_METHOD();
245
246 MYSQL_TIME mysql_time;
247 bool error = get_time_value(field_item, value_item, &mysql_time);
248
249 DBUG_RETURN(!error);
250 }
251
252 bool ConditionConverter::get_time_value(const Item_field *field_item,
253 Item *value_item,
254 MYSQL_TIME *mysql_time) {
255 MRN_DBUG_ENTER_METHOD();
256
257 bool error;
258 Item *real_value_item = value_item->real_item();
259 switch (field_item->field->type()) {
260 case MYSQL_TYPE_TIME:
261 error = real_value_item->get_time(mysql_time);
262 break;
263 case MYSQL_TYPE_YEAR:
264 mysql_time->year = static_cast<int>(value_item->val_int());
265 mysql_time->month = 1;
266 mysql_time->day = 1;
267 mysql_time->hour = 0;
268 mysql_time->hour = 0;
269 mysql_time->minute = 0;
270 mysql_time->second_part = 0;
271 mysql_time->neg = false;
272 mysql_time->time_type = MYSQL_TIMESTAMP_DATE;
273 error = false;
274 break;
275 default:
276 error = real_value_item->get_date(mysql_time, TIME_FUZZY_DATE);
277 break;
278 }
279
280 DBUG_RETURN(error);
281 }
282
283 ConditionConverter::NormalizedType
284 ConditionConverter::normalize_field_type(enum_field_types field_type) {
285 MRN_DBUG_ENTER_METHOD();
286
287 NormalizedType type = UNSUPPORTED_TYPE;
288
289 switch (field_type) {
290 case MYSQL_TYPE_DECIMAL:
291 type = STRING_TYPE;
292 break;
293 case MYSQL_TYPE_TINY:
294 case MYSQL_TYPE_SHORT:
295 case MYSQL_TYPE_LONG:
296 type = INT_TYPE;
297 break;
298 case MYSQL_TYPE_FLOAT:
299 case MYSQL_TYPE_DOUBLE:
300 type = UNSUPPORTED_TYPE;
301 break;
302 case MYSQL_TYPE_NULL:
303 type = UNSUPPORTED_TYPE;
304 break;
305 case MYSQL_TYPE_TIMESTAMP:
306 type = TIME_TYPE;
307 break;
308 case MYSQL_TYPE_LONGLONG:
309 case MYSQL_TYPE_INT24:
310 type = INT_TYPE;
311 break;
312 case MYSQL_TYPE_DATE:
313 case MYSQL_TYPE_TIME:
314 case MYSQL_TYPE_DATETIME:
315 case MYSQL_TYPE_YEAR:
316 case MYSQL_TYPE_NEWDATE:
317 type = TIME_TYPE;
318 break;
319 case MYSQL_TYPE_VARCHAR:
320 type = STRING_TYPE;
321 break;
322 case MYSQL_TYPE_BIT:
323 type = INT_TYPE;
324 break;
325#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
326 case MYSQL_TYPE_TIMESTAMP2:
327 type = TIME_TYPE;
328 break;
329#endif
330#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
331 case MYSQL_TYPE_DATETIME2:
332 type = TIME_TYPE;
333 break;
334#endif
335#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
336 case MYSQL_TYPE_TIME2:
337 type = TIME_TYPE;
338 break;
339#endif
340 case MYSQL_TYPE_NEWDECIMAL:
341 type = STRING_TYPE;
342 break;
343 case MYSQL_TYPE_ENUM:
344 type = INT_TYPE;
345 break;
346 case MYSQL_TYPE_SET:
347 type = INT_TYPE;
348 break;
349 case MYSQL_TYPE_TINY_BLOB:
350 case MYSQL_TYPE_MEDIUM_BLOB:
351 case MYSQL_TYPE_LONG_BLOB:
352 case MYSQL_TYPE_BLOB:
353 case MYSQL_TYPE_VAR_STRING:
354 case MYSQL_TYPE_STRING:
355 type = STRING_TYPE;
356 break;
357 case MYSQL_TYPE_GEOMETRY:
358 type = UNSUPPORTED_TYPE;
359 break;
360 case MYSQL_TYPE_VARCHAR_COMPRESSED:
361 case MYSQL_TYPE_BLOB_COMPRESSED:
362 DBUG_ASSERT(0);
363#ifdef MRN_HAVE_MYSQL_TYPE_JSON
364 case MYSQL_TYPE_JSON:
365 type = STRING_TYPE;
366 break;
367#endif
368 }
369
370 DBUG_RETURN(type);
371 }
372
373 bool ConditionConverter::have_index(const Item_field *field_item,
374 grn_operator _operator) {
375 MRN_DBUG_ENTER_METHOD();
376
377 grn_obj *column;
378 column = grn_obj_column(ctx_, table_,
379 MRN_ITEM_FIELD_GET_NAME(field_item),
380 MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
381 if (!column) {
382 DBUG_RETURN(false);
383 }
384 mrn::SmartGrnObj smart_column(ctx_, column);
385
386 int n_indexes = grn_column_index(ctx_, column, _operator, NULL, 0, NULL);
387 bool convertable = (n_indexes > 0);
388
389 DBUG_RETURN(convertable);
390 }
391
392 bool ConditionConverter::have_index(const Item_field *field_item,
393 Item_func::Functype func_type) {
394 MRN_DBUG_ENTER_METHOD();
395
396 bool have = false;
397 switch (func_type) {
398 case Item_func::EQ_FUNC:
399 have = have_index(field_item, GRN_OP_EQUAL);
400 break;
401 case Item_func::LT_FUNC:
402 have = have_index(field_item, GRN_OP_LESS);
403 break;
404 case Item_func::LE_FUNC:
405 have = have_index(field_item, GRN_OP_LESS_EQUAL);
406 break;
407 case Item_func::GE_FUNC:
408 have = have_index(field_item, GRN_OP_GREATER_EQUAL);
409 break;
410 case Item_func::GT_FUNC:
411 have = have_index(field_item, GRN_OP_GREATER);
412 break;
413 default:
414 break;
415 }
416
417 DBUG_RETURN(have);
418 }
419
420 unsigned int ConditionConverter::count_match_against(const Item *item) {
421 MRN_DBUG_ENTER_METHOD();
422
423 if (!item) {
424 DBUG_RETURN(0);
425 }
426
427 switch (item->type()) {
428 case Item::COND_ITEM:
429 if (is_storage_mode_) {
430 Item_cond *cond_item = (Item_cond *)item;
431 if (cond_item->functype() == Item_func::COND_AND_FUNC) {
432 unsigned int n_match_againsts = 0;
433 List_iterator<Item> iterator(*((cond_item)->argument_list()));
434 const Item *sub_item;
435 while ((sub_item = iterator++)) {
436 n_match_againsts += count_match_against(sub_item);
437 }
438 DBUG_RETURN(n_match_againsts);
439 }
440 }
441 break;
442 case Item::FUNC_ITEM:
443 {
444 const Item_func *func_item = (const Item_func *)item;
445 switch (func_item->functype()) {
446 case Item_func::FT_FUNC:
447 DBUG_RETURN(1);
448 break;
449 default:
450 break;
451 }
452 }
453 break;
454 default:
455 break;
456 }
457
458 DBUG_RETURN(0);
459 }
460
461 void ConditionConverter::convert(const Item *where, grn_obj *expression) {
462 MRN_DBUG_ENTER_METHOD();
463
464 if (!where || where->type() != Item::COND_ITEM) {
465 DBUG_VOID_RETURN;
466 }
467
468 Item_cond *cond_item = (Item_cond *)where;
469 List_iterator<Item> iterator(*((cond_item)->argument_list()));
470 const Item *sub_item;
471 while ((sub_item = iterator++)) {
472 switch (sub_item->type()) {
473 case Item::FUNC_ITEM:
474 {
475 const Item_func *func_item = (const Item_func *)sub_item;
476 switch (func_item->functype()) {
477 case Item_func::EQ_FUNC:
478 convert_binary_operation(func_item, expression, GRN_OP_EQUAL);
479 break;
480 case Item_func::LT_FUNC:
481 convert_binary_operation(func_item, expression, GRN_OP_LESS);
482 break;
483 case Item_func::LE_FUNC:
484 convert_binary_operation(func_item, expression, GRN_OP_LESS_EQUAL);
485 break;
486 case Item_func::GE_FUNC:
487 convert_binary_operation(func_item, expression,
488 GRN_OP_GREATER_EQUAL);
489 break;
490 case Item_func::GT_FUNC:
491 convert_binary_operation(func_item, expression, GRN_OP_GREATER);
492 break;
493 case Item_func::BETWEEN:
494 convert_between(func_item, expression);
495 break;
496 default:
497 break;
498 }
499 }
500 break;
501 default:
502 break;
503 }
504 }
505
506 DBUG_VOID_RETURN;
507 }
508
509 void ConditionConverter::convert_binary_operation(const Item_func *func_item,
510 grn_obj *expression,
511 grn_operator _operator) {
512 Item **arguments = func_item->arguments();
513 Item *left_item = arguments[0];
514 Item *right_item = arguments[1];
515 if (left_item->type() == Item::FIELD_ITEM) {
516 const Item_field *field_item = static_cast<const Item_field *>(left_item);
517 append_field_value(field_item, expression);
518 append_const_item(field_item, right_item, expression);
519 grn_expr_append_op(ctx_, expression, _operator, 2);
520 grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
521 }
522 }
523
524 void ConditionConverter::convert_between(const Item_func *func_item,
525 grn_obj *expression) {
526 MRN_DBUG_ENTER_METHOD();
527
528 Item **arguments = func_item->arguments();
529 Item *target_item = arguments[0];
530 Item *min_item = arguments[1];
531 Item *max_item = arguments[2];
532
533 grn_obj *between_func = grn_ctx_get(ctx_, "between", strlen("between"));
534 grn_expr_append_obj(ctx_, expression, between_func, GRN_OP_PUSH, 1);
535
536 const Item_field *field_item = static_cast<const Item_field *>(target_item);
537 append_field_value(field_item, expression);
538
539 grn_obj include;
540 mrn::SmartGrnObj smart_include(ctx_, &include);
541 GRN_TEXT_INIT(&include, 0);
542 GRN_TEXT_PUTS(ctx_, &include, "include");
543 append_const_item(field_item, min_item, expression);
544 grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
545 append_const_item(field_item, max_item, expression);
546 grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
547
548 grn_expr_append_op(ctx_, expression, GRN_OP_CALL, 5);
549
550 grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
551
552 DBUG_VOID_RETURN;
553 }
554
555 void ConditionConverter::append_field_value(const Item_field *field_item,
556 grn_obj *expression) {
557 MRN_DBUG_ENTER_METHOD();
558
559 GRN_BULK_REWIND(&column_name_);
560 GRN_TEXT_PUT(ctx_, &column_name_,
561 MRN_ITEM_FIELD_GET_NAME(field_item),
562 MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
563 grn_expr_append_const(ctx_, expression, &column_name_,
564 GRN_OP_PUSH, 1);
565 grn_expr_append_op(ctx_, expression, GRN_OP_GET_VALUE, 1);
566
567 DBUG_VOID_RETURN;
568 }
569
570 void ConditionConverter::append_const_item(const Item_field *field_item,
571 Item *const_item,
572 grn_obj *expression) {
573 MRN_DBUG_ENTER_METHOD();
574
575 enum_field_types field_type = field_item->field->real_type();
576 NormalizedType normalized_type = normalize_field_type(field_type);
577
578 switch (normalized_type) {
579 case STRING_TYPE:
580 grn_obj_reinit(ctx_, &value_, GRN_DB_TEXT, 0);
581 {
582 String *string;
583 string = const_item->val_str(NULL);
584 GRN_TEXT_SET(ctx_, &value_, string->ptr(), string->length());
585 }
586 break;
587 case INT_TYPE:
588 grn_obj_reinit(ctx_, &value_, GRN_DB_INT64, 0);
589 if (field_type == MYSQL_TYPE_ENUM) {
590 if (const_item->type() == Item::STRING_ITEM) {
591 String *string;
592 string = const_item->val_str(NULL);
593 Field_enum *enum_field = static_cast<Field_enum *>(field_item->field);
594 int enum_value = find_type(string->c_ptr(),
595 enum_field->typelib,
596 FIND_TYPE_BASIC);
597 GRN_INT64_SET(ctx_, &value_, enum_value);
598 } else {
599 GRN_INT64_SET(ctx_, &value_, const_item->val_int());
600 }
601 } else {
602 GRN_INT64_SET(ctx_, &value_, const_item->val_int());
603 }
604 break;
605 case TIME_TYPE:
606 grn_obj_reinit(ctx_, &value_, GRN_DB_TIME, 0);
607 {
608 MYSQL_TIME mysql_time;
609 get_time_value(field_item, const_item, &mysql_time);
610 bool truncated = false;
611 TimeConverter time_converter;
612 long long int time =
613 time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
614 GRN_TIME_SET(ctx_, &value_, time);
615 }
616 break;
617 case UNSUPPORTED_TYPE:
618 // Should not be occurred.
619 DBUG_PRINT("error",
620 ("mroonga: append_const_item: unsupported type: <%d> "
621 "This case should not be occurred.",
622 field_type));
623 grn_obj_reinit(ctx_, &value_, GRN_DB_VOID, 0);
624 break;
625 }
626 grn_expr_append_const(ctx_, expression, &value_, GRN_OP_PUSH, 1);
627
628 DBUG_VOID_RETURN;
629 }
630}
631