diff options
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r-- | sql/item_cmpfunc.cc | 192 |
1 files changed, 159 insertions, 33 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3b09da68927..6f10e84a5f5 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -241,15 +241,15 @@ static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE) items[i]->cmp_type() == ROW_RESULT) && cmp_row_type(items[0], items[i])) return 0; - found_types|= 1<< (uint)item_cmp_type(left_result, - items[i]->cmp_type()); + found_types|= 1U << (uint)item_cmp_type(left_result, + items[i]->cmp_type()); } /* Even if all right-hand items are NULLs and we are skipping them all, we need at least one type bit in the found_type bitmask. */ if (skip_nulls && !found_types) - found_types= 1 << (uint)left_result; + found_types= 1U << (uint)left_result; return found_types; } @@ -906,7 +906,8 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, } if ((*is_null= item->null_value)) return ~(ulonglong) 0; - if (cache_arg && item->const_item() && item->type() != Item::CACHE_ITEM) + if (cache_arg && item->const_item() && + !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT)) { Query_arena backup; Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); @@ -1434,7 +1435,7 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) return FALSE; } -bool Item_in_optimizer::fix_left(THD *thd, Item **ref) +bool Item_in_optimizer::fix_left(THD *thd) { if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || (!cache && !(cache= Item_cache::get_cache(args[0])))) @@ -1482,6 +1483,13 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) cache->store(args[0]); cache->cache_value(); } + if (args[1]->fixed) + { + /* to avoid overriding is called to update left expression */ + used_tables_cache|= args[1]->used_tables(); + with_sum_func= with_sum_func || args[1]->with_sum_func; + const_item_cache= const_item_cache && args[1]->const_item(); + } return 0; } @@ -1489,15 +1497,17 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); - if (fix_left(thd, ref)) + if (fix_left(thd)) return TRUE; if (args[0]->maybe_null) maybe_null=1; if (!args[1]->fixed && args[1]->fix_fields(thd, args+1)) return TRUE; + Item_in_subselect * sub= (Item_in_subselect *)args[1]; - if (args[0]->cols() != sub->engine->cols()) + if (!invisible_mode() && + args[0]->cols() != sub->engine->cols()) { my_error(ER_OPERAND_COLUMNS, MYF(0), args[0]->cols()); return TRUE; @@ -1513,6 +1523,30 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) return FALSE; } +/** + Check if Item_in_optimizer should work as a pass-through item for its + arguments. + + @note + Item_in_optimizer should work as pass-through for + - subqueries that were processed by ALL/ANY->MIN/MAX rewrite + - subqueries taht were originally EXISTS subqueries (and were coverted by + the EXISTS->IN rewrite) + + When Item_in_optimizer is not not working as a pass-through, it + - caches its "left argument", args[0]. + - makes adjustments to subquery item's return value for proper NULL + value handling +*/ + +bool Item_in_optimizer::invisible_mode() +{ + /* MAX/MIN transformed or EXISTS->IN prepared => do nothing */ + return (args[1]->type() != Item::SUBSELECT_ITEM || + ((Item_subselect *)args[1])->substype() == + Item_subselect::EXISTS_SUBS); +} + /** Add an expression cache for this subquery if it is needed @@ -1536,8 +1570,9 @@ Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg) { THD *thd= (THD*) thd_arg; DBUG_ENTER("Item_in_optimizer::expr_cache_insert_transformer"); - if (args[1]->type() != Item::SUBSELECT_ITEM) - DBUG_RETURN(this); // MAX/MIN transformed => do nothing + + if (invisible_mode()) + DBUG_RETURN(this); if (expr_cache) DBUG_RETURN(expr_cache); @@ -1560,13 +1595,16 @@ Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg) void Item_in_optimizer::get_cache_parameters(List<Item> ¶meters) { /* Add left expression to the list of the parameters of the subquery */ - if (args[0]->cols() == 1) - parameters.add_unique(args[0], &cmp_items); - else + if (!invisible_mode()) { - for (uint i= 0; i < args[0]->cols(); i++) + if (args[0]->cols() == 1) + parameters.add_unique(args[0], &cmp_items); + else { - parameters.add_unique(args[0]->element_index(i), &cmp_items); + for (uint i= 0; i < args[0]->cols(); i++) + { + parameters.add_unique(args[0]->element_index(i), &cmp_items); + } } } args[1]->get_cache_parameters(parameters); @@ -1649,17 +1687,19 @@ longlong Item_in_optimizer::val_int() DBUG_ASSERT(fixed == 1); cache->store(args[0]); cache->cache_value(); + DBUG_ENTER(" Item_in_optimizer::val_int"); - if (args[1]->type() != Item::SUBSELECT_ITEM) + if (invisible_mode()) { - /* MAX/MIN transformed => pass through */ longlong res= args[1]->val_int(); null_value= args[1]->null_value; - return (res); + DBUG_PRINT("info", ("pass trough")); + DBUG_RETURN(res); } if (cache->null_value) { + DBUG_PRINT("info", ("Left NULL...")); /* We're evaluating "<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)" @@ -1731,11 +1771,11 @@ longlong Item_in_optimizer::val_int() for (uint i= 0; i < ncols; i++) item_subs->set_cond_guard_var(i, TRUE); } - return 0; + DBUG_RETURN(0); } tmp= args[1]->val_bool_result(); null_value= args[1]->null_value; - return tmp; + DBUG_RETURN(tmp); } @@ -1786,7 +1826,8 @@ bool Item_in_optimizer::is_null() @retval NULL if an error occurred */ -Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument) +Item *Item_in_optimizer::transform(Item_transformer transformer, + uchar *argument) { Item *new_item; @@ -1806,7 +1847,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument if ((*args) != new_item) current_thd->change_item_tree(args, new_item); - if (args[1]->type() != Item::SUBSELECT_ITEM) + if (invisible_mode()) { /* MAX/MIN transformed => pass through */ new_item= args[1]->transform(transformer, argument); @@ -2824,12 +2865,12 @@ Item *Item_func_case::find_item(String *str) cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type()); DBUG_ASSERT(cmp_type != ROW_RESULT); DBUG_ASSERT(cmp_items[(uint)cmp_type]); - if (!(value_added_map & (1<<(uint)cmp_type))) + if (!(value_added_map & (1U << (uint)cmp_type))) { cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]); if ((null_value=args[first_expr_num]->null_value)) return else_expr_num != -1 ? args[else_expr_num] : 0; - value_added_map|= 1<<(uint)cmp_type; + value_added_map|= 1U << (uint)cmp_type; } if (!cmp_items[(uint)cmp_type]->cmp(args[i]) && !args[i]->null_value) return args[i + 1]; @@ -3036,10 +3077,10 @@ void Item_func_case::fix_length_and_dec() return; Item *date_arg= 0; - if (found_types & (1 << TIME_RESULT)) + if (found_types & (1U << TIME_RESULT)) date_arg= find_date_time_item(args, arg_count, 0); - if (found_types & (1 << STRING_RESULT)) + if (found_types & (1U << STRING_RESULT)) { /* If we'll do string comparison, we also need to aggregate @@ -3080,7 +3121,7 @@ void Item_func_case::fix_length_and_dec() for (i= 0; i <= (uint)TIME_RESULT; i++) { - if (found_types & (1 << i) && !cmp_items[i]) + if (found_types & (1U << i) && !cmp_items[i]) { DBUG_ASSERT((Item_result)i != ROW_RESULT); @@ -3936,7 +3977,7 @@ void Item_func_in::fix_length_and_dec() } for (i= 0; i <= (uint)TIME_RESULT; i++) { - if (found_types & 1 << i) + if (found_types & (1U << i)) { (type_cnt)++; cmp_type= (Item_result) i; @@ -4063,14 +4104,14 @@ void Item_func_in::fix_length_and_dec() } else { - if (found_types & (1 << TIME_RESULT)) + if (found_types & (1U << TIME_RESULT)) date_arg= find_date_time_item(args, arg_count, 0); - if (found_types & (1 << STRING_RESULT) && + if (found_types & (1U << STRING_RESULT) && agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) return; for (i= 0; i <= (uint) TIME_RESULT; i++) { - if (found_types & (1 << i) && !cmp_items[i]) + if (found_types & (1U << i) && !cmp_items[i]) { if (!cmp_items[i] && !(cmp_items[i]= cmp_item::get_comparator((Item_result)i, date_arg, @@ -4156,12 +4197,12 @@ longlong Item_func_in::val_int() Item_result cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type()); in_item= cmp_items[(uint)cmp_type]; DBUG_ASSERT(in_item); - if (!(value_added_map & (1 << (uint)cmp_type))) + if (!(value_added_map & (1U << (uint)cmp_type))) { in_item->store_value(args[0]); if ((null_value= args[0]->null_value)) return 0; - value_added_map|= 1 << (uint)cmp_type; + value_added_map|= 1U << (uint)cmp_type; } if (!in_item->cmp(args[i]) && !args[i]->null_value) return (longlong) (!negated); @@ -5350,6 +5391,7 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ bool Item_func_not::fix_fields(THD *thd, Item **ref) { + args[0]->under_not(this); if (args[0]->type() == FIELD_ITEM) { /* replace "NOT <field>" with "<filed> == 0" */ @@ -5533,6 +5575,7 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item) equal_items.push_back(f1); equal_items.push_back(f2); compare_as_dates= with_const_item && f2->cmp_type() == TIME_RESULT; + upper_levels= NULL; } @@ -5561,10 +5604,11 @@ Item_equal::Item_equal(Item_equal *item_equal) with_const= item_equal->with_const; compare_as_dates= item_equal->compare_as_dates; cond_false= item_equal->cond_false; + upper_levels= item_equal->upper_levels; } -/* +/** @brief Add a constant item to the Item_equal object @@ -5618,6 +5662,7 @@ void Item_equal::add_const(Item *c, Item *f) const_item_cache= 1; } + /** @brief Check whether a field is referred to in the multiple equality @@ -5686,6 +5731,87 @@ void Item_equal::merge(Item_equal *item) /** @brief + Merge members of another Item_equal object into this one + + @param item multiple equality whose members are to be merged + + @details + If the Item_equal 'item' happened to have some elements of the list + of equal items belonging to 'this' object then the function merges + the equal items from 'item' into this list. + If both lists contains constants and they are different then + the value of the cond_false flag is set to TRUE. + + @retval + 1 the lists of equal items in 'item' and 'this' contain common elements + @retval + 0 otherwise + + @notes + The method 'merge' just joins the list of equal items belonging to 'item' + to the list of equal items belonging to this object assuming that the lists + are disjoint. It would be more correct to call the method 'join'. + The method 'merge_with_check' really merges two lists of equal items if they + have common members. +*/ + +bool Item_equal::merge_with_check(Item_equal *item) +{ + bool intersected= FALSE; + Item_equal_fields_iterator_slow fi(*this); + while (fi++) + { + if (item->contains(fi.get_curr_field())) + { + fi.remove(); + intersected= TRUE; + } + } + if (intersected) + item->merge(this); + return intersected; +} + + +/** + @brief + Merge this object into a list of Item_equal objects + + @param list the list of Item_equal objects to merge into + + @details + If the list of equal items from 'this' object contains common members + with the lists of equal items belonging to Item_equal objects from 'list' + then all involved Item_equal objects e1,...,ek are merged into one + Item equal that replaces e1,...,ek in the 'list'. Otherwise this + Item_equal is joined to the 'list'. +*/ + +void Item_equal::merge_into_list(List<Item_equal> *list) +{ + Item_equal *item; + List_iterator<Item_equal> it(*list); + Item_equal *merge_into= NULL; + while((item= it++)) + { + if (!merge_into) + { + if (merge_with_check(item)) + merge_into= item; + } + else + { + if (item->merge_with_check(merge_into)) + it.remove(); + } + } + if (!merge_into) + list->push_back(this); +} + + +/** + @brief Order equal items of the multiple equality according to a sorting criteria @param compare function to compare items from the equal_items list |