diff options
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r-- | sql/item_cmpfunc.cc | 1987 |
1 files changed, 989 insertions, 998 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d3a59e5b4f0..195d30c222b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -26,7 +26,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <my_global.h> +#include "mariadb.h" #include "sql_priv.h" #include <m_ctype.h> #include "sql_select.h" @@ -34,21 +34,14 @@ #include "sql_time.h" // make_truncated_value_warning #include "sql_base.h" // dynamic_column_error_message -static Item** cache_converted_constant(THD *thd, Item **value, - Item **cache_item, Item_result type,enum_field_types f_type); - /** find an temporal type (item) that others will be converted to for the purpose of comparison. - for IN/CASE conversion only happens if the first item defines the - comparison context. - this is the type that will be used in warnings like "Incorrect <<TYPE>> value". */ -static Item *find_date_time_item(THD *thd, Item **args, uint nargs, uint col, - bool in_case) +static Item *find_date_time_item(Item **args, uint nargs, uint col) { Item *date_arg= 0, **arg, **arg_end; for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++) @@ -56,22 +49,10 @@ static Item *find_date_time_item(THD *thd, Item **args, uint nargs, uint col, Item *item= arg[0]->element_index(col); if (item->cmp_type() != TIME_RESULT) continue; + if (item->field_type() == MYSQL_TYPE_DATETIME) + return item; if (!date_arg) date_arg= item; - if (item->field_type() == MYSQL_TYPE_DATETIME) - break; - } - if (in_case ? date_arg == args[0]->element_index(col) : date_arg != NULL) - { - enum_field_types f_type= date_arg->field_type(); - for (arg= args, arg_end= args + nargs; arg != arg_end ; arg++) - { - Item *cache, **a= arg[0]->addr(col); - if (!a) - a= arg; - if (cache_converted_constant(thd, a, &cache, TIME_RESULT, f_type) != a) - thd->change_item_tree(a, cache); - } } return date_arg; } @@ -116,37 +97,54 @@ static int cmp_row_type(Item* item1, Item* item2) /** Aggregates result types from the array of items. - SYNOPSIS: - agg_cmp_type() - type [out] the aggregated type - items array of items to aggregate the type from - nitems number of items in the array + This method aggregates comparison handler from the array of items. + The result handler is used later for comparison of values of these items. - DESCRIPTION - This function aggregates result types from the array of items. Found type - supposed to be used later for comparison of values of these items. - Aggregation itself is performed by the item_cmp_type() function. - @param[out] type the aggregated type - @param items array of items to aggregate the type from - @param nitems number of items in the array + aggregate_for_comparison() + funcname the function or operator name, + for error reporting + items array of items to aggregate the type from + nitems number of items in the array + int_uint_as_dec what to do when comparing INT to UINT: + set the comparison handler to decimal or int. - @retval - 1 type incompatibility has been detected - @retval - 0 otherwise + @retval true type incompatibility has been detected + @retval false otherwise */ -static int agg_cmp_type(Item_result *type, - Item **items, - uint nitems, - bool int_uint_as_dec) +bool +Type_handler_hybrid_field_type::aggregate_for_comparison(const char *funcname, + Item **items, + uint nitems, + bool int_uint_as_dec) { uint unsigned_count= items[0]->unsigned_flag; - type[0]= items[0]->cmp_type(); + /* + Convert sub-type to super-type (e.g. DATE to DATETIME, INT to BIGINT, etc). + Otherwise Predicant_to_list_comparator will treat sub-types of the same + super-type as different data types and won't be able to use bisection in + many cases. + */ + set_handler(items[0]->type_handler()->type_handler_for_comparison()); for (uint i= 1 ; i < nitems ; i++) { unsigned_count+= items[i]->unsigned_flag; - type[0]= item_cmp_type(type[0], items[i]); + if (aggregate_for_comparison(items[i]->type_handler()-> + type_handler_for_comparison())) + { + /* + For more precise error messages if aggregation failed on the first pair + {items[0],items[1]}, use the name of items[0]->data_handler(). + Otherwise use the name of this->type_handler(), which is already a + result of aggregation for items[0]..items[i-1]. + */ + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + i == 1 ? items[0]->type_handler()->name().ptr() : + type_handler()->name().ptr(), + items[i]->type_handler()->name().ptr(), + funcname); + return true; + } /* When aggregating types of two row expressions we have to check that they have the same cardinality and that each component @@ -154,102 +152,21 @@ static int agg_cmp_type(Item_result *type, the signature of the corresponding component of the second row expression. */ - if (type[0] == ROW_RESULT && cmp_row_type(items[0], items[i])) - return 1; // error found: invalid usage of rows + if (cmp_type() == ROW_RESULT && cmp_row_type(items[0], items[i])) + return true; // error found: invalid usage of rows } /** If all arguments are of INT type but have different unsigned_flag values, switch to DECIMAL_RESULT. */ if (int_uint_as_dec && - type[0] == INT_RESULT && + cmp_type() == INT_RESULT && unsigned_count != nitems && unsigned_count != 0) - type[0]= DECIMAL_RESULT; + set_handler(&type_handler_newdecimal); return 0; } -/** - @brief Aggregates field types from the array of items. - - @param[in] items array of items to aggregate the type from - @paran[in] nitems number of items in the array - @param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT - counterpart as a LONGLONG number or as a VARBINARY string. - - Currently behaviour depends on the function: - - LEAST/GREATEST treat BIT as VARBINARY when - aggregating with a non-BIT counterpart. - Note, UNION also works this way. - - - CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when - aggregating with a non-BIT counterpart; - - This inconsistency may be changed in the future. See MDEV-8867. - - Note, independently from "treat_bit_as_number": - - a single BIT argument gives BIT as a result - - two BIT couterparts give BIT as a result - - @details This function aggregates field types from the array of items. - Found type is supposed to be used later as the result field type - of a multi-argument function. - Aggregation itself is performed by the Field::field_type_merge() - function. - - @note The term "aggregation" is used here in the sense of inferring the - result type of a function from its argument types. - - @return aggregated field type. -*/ - -enum_field_types agg_field_type(Item **items, uint nitems, - bool treat_bit_as_number) -{ - uint i; - if (!nitems || items[0]->result_type() == ROW_RESULT) - { - DBUG_ASSERT(0); - return MYSQL_TYPE_NULL; - } - enum_field_types res= items[0]->field_type(); - uint unsigned_count= items[0]->unsigned_flag; - for (i= 1 ; i < nitems ; i++) - { - enum_field_types cur= items[i]->field_type(); - if (treat_bit_as_number && - ((res == MYSQL_TYPE_BIT) ^ (cur == MYSQL_TYPE_BIT))) - { - if (res == MYSQL_TYPE_BIT) - res= MYSQL_TYPE_LONGLONG; // BIT + non-BIT - else - cur= MYSQL_TYPE_LONGLONG; // non-BIT + BIT - } - res= Field::field_type_merge(res, cur); - unsigned_count+= items[i]->unsigned_flag; - } - switch (res) { - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_BIT: - if (unsigned_count != 0 && unsigned_count != nitems) - { - /* - If all arguments are of INT-alike type but have different - unsigned_flag, then convert to DECIMAL. - */ - return MYSQL_TYPE_NEWDECIMAL; - } - default: - break; - } - return res; -} - /* Collects different types for comparison of first item with each other items @@ -501,7 +418,8 @@ void Item_func::convert_const_compared_to_int_field(THD *thd) args[field= 1]->real_item()->type() == FIELD_ITEM) { Item_field *field_item= (Item_field*) (args[field]->real_item()); - if ((field_item->field_type() == MYSQL_TYPE_LONGLONG || + if (((field_item->field_type() == MYSQL_TYPE_LONGLONG && + field_item->type_handler() != &type_handler_vers_trx_id) || field_item->field_type() == MYSQL_TYPE_YEAR)) convert_const_to_int(thd, field_item, &args[!field]); } @@ -560,77 +478,6 @@ bool Item_bool_rowready_func2::fix_length_and_dec() } -int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type) -{ - owner= item; - func= comparator_matrix[type][is_owner_equal_func()]; - - switch (type) { - case TIME_RESULT: - m_compare_collation= &my_charset_numeric; - break; - case ROW_RESULT: - { - uint n= (*a)->cols(); - if (n != (*b)->cols()) - { - my_error(ER_OPERAND_COLUMNS, MYF(0), n); - comparators= 0; - return 1; - } - if (!(comparators= new Arg_comparator[n])) - return 1; - for (uint i=0; i < n; i++) - { - if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols()) - { - my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); - return 1; - } - if (comparators[i].set_cmp_func(owner, (*a)->addr(i), - (*b)->addr(i), set_null)) - return 1; - } - break; - } - case INT_RESULT: - { - if (func == &Arg_comparator::compare_int_signed) - { - if ((*a)->unsigned_flag) - func= (((*b)->unsigned_flag)? - &Arg_comparator::compare_int_unsigned : - &Arg_comparator::compare_int_unsigned_signed); - else if ((*b)->unsigned_flag) - func= &Arg_comparator::compare_int_signed_unsigned; - } - else if (func== &Arg_comparator::compare_e_int) - { - if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) - func= &Arg_comparator::compare_e_int_diff_signedness; - } - break; - } - case STRING_RESULT: - case DECIMAL_RESULT: - break; - case REAL_RESULT: - { - if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC) - { - precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1]; - if (func == &Arg_comparator::compare_real) - func= &Arg_comparator::compare_real_fixed; - else if (func == &Arg_comparator::compare_e_real) - func= &Arg_comparator::compare_e_real_fixed; - } - break; - } - } - return 0; -} - - /** Prepare the comparator (set the comparison function) for comparing items *a1 and *a2 in the context of 'type'. @@ -647,14 +494,62 @@ int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type) int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2) { - THD *thd= current_thd; owner= owner_arg; set_null= set_null && owner_arg; a= a1; b= a2; - m_compare_type= item_cmp_type(*a1, *a2); + Item *tmp_args[2]= {*a1, *a2}; + Type_handler_hybrid_field_type tmp; + if (tmp.aggregate_for_comparison(owner_arg->func_name(), tmp_args, 2, false)) + { + DBUG_ASSERT(current_thd->is_error()); + return 1; + } + m_compare_handler= tmp.type_handler(); + return m_compare_handler->set_comparator_func(this); +} - if (m_compare_type == STRING_RESULT && + +bool Arg_comparator::set_cmp_func_for_row_arguments() +{ + uint n= (*a)->cols(); + if (n != (*b)->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), n); + comparators= 0; + return true; + } + if (!(comparators= new Arg_comparator[n])) + return true; + for (uint i=0; i < n; i++) + { + if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); + return true; + } + if (comparators[i].set_cmp_func(owner, (*a)->addr(i), + (*b)->addr(i), set_null)) + return true; + } + return false; +} + + +bool Arg_comparator::set_cmp_func_row() +{ + func= is_owner_equal_func() ? &Arg_comparator::compare_e_row : + &Arg_comparator::compare_row; + return set_cmp_func_for_row_arguments(); +} + + +bool Arg_comparator::set_cmp_func_string() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_string : + &Arg_comparator::compare_string; + if (compare_type() == STRING_RESULT && (*a)->result_type() == STRING_RESULT && (*b)->result_type() == STRING_RESULT) { @@ -663,7 +558,7 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, generated item, like in natural join */ if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b)) - return 1; + return true; if ((*a)->type() == Item::FUNC_ITEM && ((Item_func *) (*a))->functype() == Item_func::JSON_EXTRACT_FUNC) @@ -681,29 +576,73 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, } } - if (m_compare_type == TIME_RESULT) + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} + + +bool Arg_comparator::set_cmp_func_time() +{ + THD *thd= current_thd; + m_compare_collation= &my_charset_numeric; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_time : + &Arg_comparator::compare_time; + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} + + +bool Arg_comparator::set_cmp_func_datetime() +{ + THD *thd= current_thd; + m_compare_collation= &my_charset_numeric; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : + &Arg_comparator::compare_datetime; + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} + + +bool Arg_comparator::set_cmp_func_int() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_int : + &Arg_comparator::compare_int_signed; + if ((*a)->field_type() == MYSQL_TYPE_YEAR && + (*b)->field_type() == MYSQL_TYPE_YEAR) { - enum_field_types f_type= a[0]->field_type_for_temporal_comparison(b[0]); - if (f_type == MYSQL_TYPE_TIME) - { - func= is_owner_equal_func() ? &Arg_comparator::compare_e_time : - &Arg_comparator::compare_time; - } - else - { - func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : - &Arg_comparator::compare_datetime; - } - a= cache_converted_constant(thd, a, &a_cache, m_compare_type, f_type); - b= cache_converted_constant(thd, b, &b_cache, m_compare_type, f_type); - return 0; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : + &Arg_comparator::compare_datetime; } + else if (func == &Arg_comparator::compare_int_signed) + { + if ((*a)->unsigned_flag) + func= (((*b)->unsigned_flag)? + &Arg_comparator::compare_int_unsigned : + &Arg_comparator::compare_int_unsigned_signed); + else if ((*b)->unsigned_flag) + func= &Arg_comparator::compare_int_signed_unsigned; + } + else if (func== &Arg_comparator::compare_e_int) + { + if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) + func= &Arg_comparator::compare_e_int_diff_signedness; + } + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} - if (m_compare_type == REAL_RESULT && - (((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() && + +bool Arg_comparator::set_cmp_func_real() +{ + if ((((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() && (*b)->result_type() == STRING_RESULT && (*b)->const_item()) || - ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() && - (*a)->result_type() == STRING_RESULT && (*a)->const_item()))) + ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() && + (*a)->result_type() == STRING_RESULT && (*a)->const_item()))) { /* <non-const decimal expression> <cmp> <const string expression> @@ -712,23 +651,34 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, Do comparison as decimal rather than float, in order not to lose precision. */ - m_compare_type= DECIMAL_RESULT; + m_compare_handler= &type_handler_newdecimal; + return set_cmp_func_decimal(); } - if (m_compare_type == INT_RESULT && - (*a)->field_type() == MYSQL_TYPE_YEAR && - (*b)->field_type() == MYSQL_TYPE_YEAR) - { - m_compare_type= TIME_RESULT; - func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : - &Arg_comparator::compare_datetime; - } - else - { - a= cache_converted_constant(thd, a, &a_cache, m_compare_type, (*a)->field_type()); - b= cache_converted_constant(thd, b, &b_cache, m_compare_type, (*b)->field_type()); - } - return set_compare_func(owner_arg, m_compare_type); + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_real : + &Arg_comparator::compare_real; + if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC) + { + precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1]; + if (func == &Arg_comparator::compare_real) + func= &Arg_comparator::compare_real_fixed; + else if (func == &Arg_comparator::compare_e_real) + func= &Arg_comparator::compare_e_real_fixed; + } + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} + +bool Arg_comparator::set_cmp_func_decimal() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_decimal : + &Arg_comparator::compare_decimal; + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; } @@ -749,15 +699,20 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, @return cache item or original value. */ -static Item** cache_converted_constant(THD *thd, Item **value, - Item **cache_item, Item_result type, enum_field_types f_type) +Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, + Item **cache_item, + const Type_handler *handler) { - /* Don't need cache if doing context analysis only. */ - if (!thd->lex->is_ps_or_view_context_analysis() && - (*value)->const_item() && type != (*value)->result_type()) + /* + Don't need cache if doing context analysis only. + */ + if (!thd_arg->lex->is_ps_or_view_context_analysis() && + (*value)->const_item() && + handler->type_handler_for_comparison() != + (*value)->type_handler_for_comparison()) { - Item_cache *cache= Item_cache::get_cache(thd, *value, type, f_type); - cache->setup(thd, *value); + Item_cache *cache= handler->Item_get_cache(thd_arg, *value); + cache->setup(thd_arg, *value); *cache_item= cache; return cache_item; } @@ -765,60 +720,57 @@ static Item** cache_converted_constant(THD *thd, Item **value, } -/* - Compare items values as dates. - - SYNOPSIS - Arg_comparator::compare_datetime() - - DESCRIPTION - Compare items values as DATE/DATETIME for both EQUAL_FUNC and from other - comparison functions. - - RETURN - -1 a < b or at least one item is null - 0 a == b - 1 a > b -*/ - -int Arg_comparator::compare_temporal(enum_field_types type) +int Arg_comparator::compare_time() { - longlong a_value, b_value; - + longlong val1= (*a)->val_time_packed(); + if (!(*a)->null_value) + { + longlong val2= (*b)->val_time_packed(); + if (!(*b)->null_value) + return compare_not_null_values(val1, val2); + } if (set_null) - owner->null_value= 1; + owner->null_value= true; + return -1; +} - /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= (*a)->val_temporal_packed(type); - if ((*a)->null_value) - return -1; - /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= (*b)->val_temporal_packed(type); - if ((*b)->null_value) - return -1; +int Arg_comparator::compare_e_time() +{ + longlong val1= (*a)->val_time_packed(); + longlong val2= (*b)->val_time_packed(); + if ((*a)->null_value || (*b)->null_value) + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(val1 == val2); +} - /* Here we have two not-NULL values. */ - if (set_null) - owner->null_value= 0; - /* Compare values. */ - return a_value < b_value ? -1 : a_value > b_value ? 1 : 0; -} -int Arg_comparator::compare_e_temporal(enum_field_types type) +int Arg_comparator::compare_datetime() { - longlong a_value, b_value; + longlong val1= (*a)->val_datetime_packed(); + if (!(*a)->null_value) + { + longlong val2= (*b)->val_datetime_packed(); + if (!(*b)->null_value) + return compare_not_null_values(val1, val2); + } + if (set_null) + owner->null_value= true; + return -1; +} - /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= (*a)->val_temporal_packed(type); - /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= (*b)->val_temporal_packed(type); - return (*a)->null_value || (*b)->null_value ? - (*a)->null_value == (*b)->null_value : a_value == b_value; +int Arg_comparator::compare_e_datetime() +{ + longlong val1= (*a)->val_datetime_packed(); + longlong val2= (*b)->val_datetime_packed(); + if ((*a)->null_value || (*b)->null_value) + return MY_TEST((*a)->null_value && (*b)->null_value); + return MY_TEST(val1 == val2); } + int Arg_comparator::compare_string() { String *res1,*res2; @@ -965,13 +917,7 @@ int Arg_comparator::compare_int_signed() { longlong val2= (*b)->val_int(); if (!(*b)->null_value) - { - if (set_null) - owner->null_value= 0; - if (val1 < val2) return -1; - if (val1 == val2) return 0; - return 1; - } + return compare_not_null_values(val1, val2); } if (set_null) owner->null_value= 1; @@ -1230,6 +1176,8 @@ longlong Item_func_truth::val_int() bool Item_in_optimizer::is_top_level_item() { + if (invisible_mode()) + return FALSE; return ((Item_in_subselect *)args[1])->is_top_level_item(); } @@ -1312,8 +1260,8 @@ bool Item_in_optimizer::fix_left(THD *thd) ref0= &(((Item_in_subselect *)args[1])->left_expr); args[0]= ((Item_in_subselect *)args[1])->left_expr; } - if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) || - (!cache && !(cache= Item_cache::get_cache(thd, *ref0)))) + if ((*ref0)->fix_fields_if_needed(thd, ref0) || + (!cache && !(cache= (*ref0)->get_cache(thd)))) DBUG_RETURN(1); /* During fix_field() expression could be substituted. @@ -1397,7 +1345,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) if (args[0]->maybe_null) maybe_null=1; - if (!args[1]->fixed && args[1]->fix_fields(thd, args+1)) + if (args[1]->fix_fields_if_needed(thd, args + 1)) return TRUE; if (!invisible_mode() && ((sub && ((col= args[0]->cols()) != sub->engine->cols())) || @@ -1408,7 +1356,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) } if (args[1]->maybe_null) maybe_null=1; - with_subselect= 1; + m_with_subquery= true; with_sum_func= with_sum_func || args[1]->with_sum_func; with_field= with_field || args[1]->with_field; with_param= args[0]->with_param || args[1]->with_param; @@ -1882,16 +1830,13 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const return 0; if (negated != ((Item_func_opt_neg *) item_func)->negated) return 0; - for (uint i=0; i < arg_count ; i++) - if (!args[i]->eq(item_func->arguments()[i], binary_cmp)) - return 0; - return 1; + return Item_args::eq(item_func, binary_cmp); } bool Item_func_interval::fix_fields(THD *thd, Item **ref) { - if (Item_int_func::fix_fields(thd, ref)) + if (Item_long_func::fix_fields(thd, ref)) return true; for (uint i= 0 ; i < row->cols(); i++) { @@ -2130,9 +2075,7 @@ void Item_func_between::fix_after_pullout(st_select_lex *new_parent, bool Item_func_between::fix_length_and_dec() { - THD *thd= current_thd; max_length= 1; - compare_as_dates= 0; /* As some compare functions are generated after sql_yacc, @@ -2140,23 +2083,21 @@ bool Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return TRUE; - if (agg_cmp_type(&m_compare_type, args, 3, false)) + if (m_comparator.aggregate_for_comparison(Item_func_between::func_name(), + args, 3, false)) + { + DBUG_ASSERT(current_thd->is_error()); return TRUE; + } - if (m_compare_type == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, 3)) - return TRUE; + return m_comparator.type_handler()-> + Item_func_between_fix_length_and_dec(this); +} - /* - When comparing as date/time, we need to convert non-temporal values - (e.g. strings) to MYSQL_TIME. - For this to work, we need to know what date/time type we compare - strings as. - */ - if (m_compare_type == TIME_RESULT) - compare_as_dates= find_date_time_item(thd, args, 3, 0, false); - /* See the comment for Item_func::convert_const_compared_to_int_field */ +bool Item_func_between::fix_length_and_dec_numeric(THD *thd) +{ + /* See the comment about the similar block in Item_bool_func2 */ if (args[0]->real_item()->type() == FIELD_ITEM && !thd->lex->is_ps_or_view_context_analysis()) { @@ -2167,13 +2108,56 @@ bool Item_func_between::fix_length_and_dec() const bool cvt_arg1= convert_const_to_int(thd, field_item, &args[1]); const bool cvt_arg2= convert_const_to_int(thd, field_item, &args[2]); if (cvt_arg1 && cvt_arg2) - m_compare_type= INT_RESULT; // Works for all types. + { + // Works for all types + m_comparator.set_handler(&type_handler_longlong); + } } } return FALSE; } +bool Item_func_between::fix_length_and_dec_temporal(THD *thd) +{ + if (!thd->lex->is_ps_or_view_context_analysis()) + { + for (uint i= 0; i < 3; i ++) + { + if (args[i]->const_item() && + args[i]->type_handler_for_comparison() != m_comparator.type_handler()) + { + Item_cache *cache= m_comparator.type_handler()->Item_get_cache(thd, args[i]); + if (!cache || cache->setup(thd, args[i])) + return true; + thd->change_item_tree(&args[i], cache); + } + } + } + return false; +} + + +longlong Item_func_between::val_int_cmp_temporal() +{ + enum_field_types f_type= m_comparator.type_handler()->field_type(); + longlong value= args[0]->val_temporal_packed(f_type), a, b; + if ((null_value= args[0]->null_value)) + return 0; + a= args[1]->val_temporal_packed(f_type); + b= args[2]->val_temporal_packed(f_type); + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((value >= a && value <= b) != negated); + if (args[1]->null_value && args[2]->null_value) + null_value= true; + else if (args[1]->null_value) + null_value= value <= b; // not null if false range. + else + null_value= value >= a; + return (longlong) (!null_value && negated); +} + + longlong Item_func_between::val_int_cmp_string() { String *value,*a,*b; @@ -2265,51 +2249,6 @@ longlong Item_func_between::val_int_cmp_real() } -longlong Item_func_between::val_int() -{ - DBUG_ASSERT(fixed == 1); - - switch (m_compare_type) { - case TIME_RESULT: - { - longlong value, a, b; - - enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates); - value= args[0]->val_temporal_packed(f_type); - - if ((null_value= args[0]->null_value)) - return 0; - - a= args[1]->val_temporal_packed(f_type); - b= args[2]->val_temporal_packed(f_type); - - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((value >= a && value <= b) != negated); - if (args[1]->null_value && args[2]->null_value) - null_value=1; - else if (args[1]->null_value) - null_value= value <= b; // not null if false range. - else - null_value= value >= a; - break; - } - case STRING_RESULT: - return val_int_cmp_string(); - case INT_RESULT: - return val_int_cmp_int(); - case DECIMAL_RESULT: - return val_int_cmp_decimal(); - case REAL_RESULT: - return val_int_cmp_real(); - case ROW_RESULT: - DBUG_ASSERT(0); - null_value= 1; - return 0; - } - return (longlong) (!null_value && negated); -} - - void Item_func_between::print(String *str, enum_query_type query_type) { args[0]->print_parenthesised(str, query_type, higher_precedence()); @@ -2322,16 +2261,6 @@ void Item_func_between::print(String *str, enum_query_type query_type) } -uint Item_func_case_abbreviation2::decimal_precision2(Item **args) const -{ - int arg0_int_part= args[0]->decimal_int_part(); - int arg1_int_part= args[1]->decimal_int_part(); - int max_int_part= MY_MAX(arg0_int_part, arg1_int_part); - int precision= max_int_part + decimals; - return MY_MIN(precision, DECIMAL_MAX_PRECISION); -} - - double Item_func_ifnull::real_op() { @@ -2400,12 +2329,28 @@ Item_func_ifnull::str_op(String *str) } -bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, uint fuzzydate) +bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) { DBUG_ASSERT(fixed == 1); - if (!args[0]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES)) - return (null_value= false); - return (null_value= args[1]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES)); + for (uint i= 0; i < 2; i++) + { + Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))) + return (null_value= false); + } + return (null_value= true); +} + + +bool Item_func_ifnull::time_op(MYSQL_TIME *ltime) +{ + DBUG_ASSERT(fixed == 1); + for (uint i= 0; i < 2; i++) + { + if (!Time(args[i]).copy_to_mysql_time(ltime)) + return (null_value= false); + } + return (null_value= true); } @@ -2471,91 +2416,6 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, } -void Item_func_if::cache_type_info(Item *source) -{ - Type_std_attributes::set(source); - set_handler_by_field_type(source->field_type()); - maybe_null= source->maybe_null; -} - - -bool -Item_func_if::fix_length_and_dec() -{ - // Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr. - if (args[1]->type() == NULL_ITEM) - { - cache_type_info(args[2]); - maybe_null= true; - // If both arguments are NULL, make resulting type BINARY(0). - if (args[2]->type() == NULL_ITEM) - set_handler_by_field_type(MYSQL_TYPE_STRING); - return FALSE; - } - if (args[2]->type() == NULL_ITEM) - { - cache_type_info(args[1]); - maybe_null= true; - return FALSE; - } - return Item_func_case_abbreviation2::fix_length_and_dec2(args + 1); -} - - -double -Item_func_if::real_op() -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - double value= arg->val_real(); - null_value=arg->null_value; - return value; -} - -longlong -Item_func_if::int_op() -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - longlong value=arg->val_int(); - null_value=arg->null_value; - return value; -} - -String * -Item_func_if::str_op(String *str) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - String *res=arg->val_str(str); - if (res) - res->set_charset(collation.collation); - if ((null_value=arg->null_value)) - res= NULL; - return res; -} - - -my_decimal * -Item_func_if::decimal_op(my_decimal *decimal_value) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - my_decimal *value= arg->val_decimal(decimal_value); - if ((null_value= arg->null_value)) - value= NULL; - return value; -} - - -bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - return (null_value= arg->get_date_with_conversion(ltime, fuzzydate)); -} - - void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, uint flags) { @@ -2761,7 +2621,7 @@ Item_func_nullif::fix_length_and_dec() */ m_cache= args[0]->cmp_type() == STRING_RESULT ? new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) : - Item_cache::get_cache(thd, args[0]); + args[0]->get_cache(thd); if (!m_cache) return TRUE; m_cache->setup(thd, args[0]); @@ -2770,7 +2630,7 @@ Item_func_nullif::fix_length_and_dec() thd->change_item_tree(&args[0], m_cache); thd->change_item_tree(&args[2], m_cache); } - set_handler_by_field_type(args[2]->field_type()); + set_handler(args[2]->type_handler()); collation.set(args[2]->collation); decimals= args[2]->decimals; unsigned_flag= args[2]->unsigned_flag; @@ -2969,59 +2829,56 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value) bool -Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate) +Item_func_nullif::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) { DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - return (null_value= args[2]->get_date(ltime, fuzzydate)); + Datetime dt(current_thd, args[2], fuzzydate); + return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } bool -Item_func_nullif::is_null() +Item_func_nullif::time_op(MYSQL_TIME *ltime) { - return (null_value= (!compare() ? 1 : args[2]->null_value)); + DBUG_ASSERT(fixed == 1); + if (!compare()) + return (null_value= true); + return (null_value= Time(args[2]).copy_to_mysql_time(ltime)); + } -Item_func_case::Item_func_case(THD *thd, List<Item> &list, - Item *first_expr_arg, Item *else_expr_arg): - Item_func_hybrid_field_type(thd), first_expr_num(-1), else_expr_num(-1), - left_cmp_type(INT_RESULT), case_item(0), m_found_types(0) +bool +Item_func_nullif::is_null() { - DBUG_ASSERT(list.elements % 2 == 0); - nwhens= list.elements / 2; - if (first_expr_arg) - { - first_expr_num= 0; - list.push_front(first_expr_arg, thd->mem_root); - } - if (else_expr_arg) - { - else_expr_num= list.elements; - list.push_back(else_expr_arg, thd->mem_root); - } - set_arguments(thd, list); + return (null_value= (!compare() ? 1 : args[2]->is_null())); +} +void Item_func_case::reorder_args(uint start) +{ /* Reorder args, to have at first the optional CASE expression, then all WHEN - expressions, then all THEN expressions. And the optional ELSE expression + expressions, then all THEN expressions. And the optional ELSE expression at the end. + + We reorder an even number of arguments, starting from start. */ - const size_t size= sizeof(Item*)*nwhens*2; + uint count = (arg_count - start) / 2; + const size_t size= sizeof(Item*) * count * 2; Item **arg_buffer= (Item **)my_safe_alloca(size); - memcpy(arg_buffer, args + first_expr_num + 1, size); - for (uint i= 0; i < nwhens ; i++) + memcpy(arg_buffer, &args[start], size); + for (uint i= 0; i < count; i++) { - args[first_expr_num + 1 + i]= arg_buffer[i*2]; - args[first_expr_num + 1 + i + nwhens] = arg_buffer[i*2 + 1]; + args[start + i]= arg_buffer[i*2]; + args[start + i + count]= arg_buffer[i*2 + 1]; } my_safe_afree(arg_buffer, size); - - bzero(&cmp_items, sizeof(cmp_items)); } + + /** Find and return matching items for CASE or ELSE item if all compares are failed or NULL if ELSE item isn't defined. @@ -3043,42 +2900,37 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list, failed */ -Item *Item_func_case::find_item(String *str) +Item *Item_func_case_searched::find_item() { - uint value_added_map= 0; - - if (first_expr_num == -1) - { - for (uint i=0 ; i < nwhens ; i++) - { - // No expression between CASE and the first WHEN - if (args[i]->val_bool()) - return args[i+nwhens]; - } - } - else + uint count= when_count(); + for (uint i= 0 ; i < count ; i++) { - /* Compare every WHEN argument with it and return the first match */ - for (uint i=1 ; i <= nwhens; i++) - { - if (args[i]->real_item()->type() == NULL_ITEM) - continue; - cmp_type= item_cmp_type(left_cmp_type, args[i]); - DBUG_ASSERT(cmp_type != ROW_RESULT); - DBUG_ASSERT(cmp_items[(uint)cmp_type]); - if (!(value_added_map & (1U << (uint)cmp_type))) - { - cmp_items[(uint)cmp_type]->store_value(args[0]); - if ((null_value= args[0]->null_value)) - return else_expr_num != -1 ? args[else_expr_num] : 0; - value_added_map|= 1U << (uint)cmp_type; - } - if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE) - return args[i + nwhens]; - } + if (args[i]->val_bool()) + return args[i + count]; } - // No, WHEN clauses all missed, return ELSE expression - return else_expr_num != -1 ? args[else_expr_num] : 0; + Item **pos= Item_func_case_searched::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_case_simple::find_item() +{ + /* Compare every WHEN argument with it and return the first match */ + uint idx; + if (!Predicant_to_list_comparator::cmp(this, &idx, NULL)) + return args[idx + when_count()]; + Item **pos= Item_func_case_simple::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_decode_oracle::find_item() +{ + uint idx; + if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) + return args[idx + when_count()]; + Item **pos= Item_func_decode_oracle::else_expr_addr(); + return pos ? pos[0] : 0; } @@ -3086,7 +2938,7 @@ String *Item_func_case::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; - Item *item=find_item(str); + Item *item= find_item(); if (!item) { @@ -3103,9 +2955,7 @@ String *Item_func_case::str_op(String *str) longlong Item_func_case::int_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); longlong res; if (!item) @@ -3121,9 +2971,7 @@ longlong Item_func_case::int_op() double Item_func_case::real_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); double res; if (!item) @@ -3140,9 +2988,7 @@ double Item_func_case::real_op() my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); my_decimal *res; if (!item) @@ -3157,33 +3003,34 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) } -bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate) +bool Item_func_case::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); if (!item) return (null_value= true); - return (null_value= item->get_date_with_conversion(ltime, fuzzydate)); + Datetime dt(current_thd, item, fuzzydate); + return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } -bool Item_func_case::fix_fields(THD *thd, Item **ref) +bool Item_func_case::time_op(MYSQL_TIME *ltime) { - /* - buff should match stack usage from - Item_func_case::val_int() -> Item_func_case::find_item() - */ - uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; + DBUG_ASSERT(fixed == 1); + Item *item= find_item(); + if (!item) + return (null_value= true); + return (null_value= Time(item).copy_to_mysql_time(ltime)); +} + +bool Item_func_case::fix_fields(THD *thd, Item **ref) +{ bool res= Item_func::fix_fields(thd, ref); - /* - Call check_stack_overrun after fix_fields to be sure that stack variable - is not optimized away - */ - if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) - return TRUE; // Fatal error flag is set! + + Item **pos= else_expr_addr(); + if (!pos || pos[0]->maybe_null) + maybe_null= 1; return res; } @@ -3193,108 +3040,151 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) THD::change_item_tree() if needed. */ -static void change_item_tree_if_needed(THD *thd, Item **place, Item *new_value) +static void propagate_and_change_item_tree(THD *thd, Item **place, + COND_EQUAL *cond, + const Item::Context &ctx) { + Item *new_value= (*place)->propagate_equal_fields(thd, ctx, cond); if (new_value && *place != new_value) thd->change_item_tree(place, new_value); } -bool Item_func_case::fix_length_and_dec() +bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, + uint *found_types, + bool nulls_equal) { - m_found_types= 0; - if (else_expr_num == -1 || args[else_expr_num]->maybe_null) - maybe_null= 1; - - /* - Aggregate all THEN and ELSE expression types - and collations when string result - */ - Item **rets= args + first_expr_num + 1 + nwhens; - uint nrets= nwhens + (else_expr_num != -1); - set_handler_by_field_type(agg_field_type(rets, nrets, true)); - - if (Item_func_case::result_type() == STRING_RESULT) + bool have_null= false; + uint type_cnt; + Type_handler_hybrid_field_type tmp; + uint ncases= when_count(); + add_predicant(this, 0); + for (uint i= 0 ; i < ncases; i++) { - if (count_string_result_length(Item_func_case::field_type(), rets, nrets)) - return TRUE; + if (nulls_equal ? + add_value("case..when", this, i + 1) : + add_value_skip_null("case..when", this, i + 1, &have_null)) + return true; } - else - fix_attributes(rets, nrets); + all_values_added(&tmp, &type_cnt, &m_found_types); +#ifndef DBUG_OFF + Predicant_to_list_comparator::debug_print(thd); +#endif + return false; +} + + +bool Item_func_case_searched::fix_length_and_dec() +{ + THD *thd= current_thd; + return aggregate_then_and_else_arguments(thd, when_count()); +} + + +bool Item_func_case_simple::fix_length_and_dec() +{ + THD *thd= current_thd; + return (aggregate_then_and_else_arguments(thd, when_count() + 1) || + aggregate_switch_and_when_arguments(thd, false)); +} + + +bool Item_func_decode_oracle::fix_length_and_dec() +{ + THD *thd= current_thd; + return (aggregate_then_and_else_arguments(thd, when_count() + 1) || + aggregate_switch_and_when_arguments(thd, true)); +} + + +/* + Aggregate all THEN and ELSE expression types + and collations when string result - /* - Aggregate first expression and all WHEN expression types - and collations when string comparison - */ - if (first_expr_num != -1) - { - left_cmp_type= args[0]->cmp_type(); + @param THD - current thd + @param start - an element in args to start aggregating from +*/ +bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, uint start) +{ + if (aggregate_for_result(func_name(), args + start, arg_count - start, true)) + return true; - if (!(m_found_types= collect_cmp_types(args, nwhens + 1))) - return TRUE; + if (fix_attributes(args + start, arg_count - start)) + return true; - Item *date_arg= 0; - if (m_found_types & (1U << TIME_RESULT)) - date_arg= find_date_time_item(current_thd, args, nwhens + 1, 0, true); + return false; +} - if (m_found_types & (1U << STRING_RESULT)) - { - /* - If we'll do string comparison, we also need to aggregate - character set and collation for first/WHEN items and - install converters for some of them to cmp_collation when necessary. - This is done because cmp_item comparators cannot compare - strings in two different character sets. - Some examples when we install converters: - 1. Converter installed for the first expression: +/* + Aggregate the predicant expression and all WHEN expression types + and collations when string comparison +*/ +bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, + bool nulls_eq) +{ + uint ncases= when_count(); + m_found_types= 0; + if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq)) + { + /* + If Predicant_to_list_comparator() fails to prepare components, + it must put an error into the diagnostics area. This is needed + to make fix_fields() catches such errors. + */ + DBUG_ASSERT(thd->is_error()); + return true; + } - CASE latin1_item WHEN utf16_item THEN ... END + if (!(m_found_types= collect_cmp_types(args, ncases + 1))) + return true; - is replaced to: + if (m_found_types & (1U << STRING_RESULT)) + { + /* + If we'll do string comparison, we also need to aggregate + character set and collation for first/WHEN items and + install converters for some of them to cmp_collation when necessary. + This is done because cmp_item compatators cannot compare + strings in two different character sets. + Some examples when we install converters: - CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END + 1. Converter installed for the first expression: - 2. Converter installed for the left WHEN item: + CASE latin1_item WHEN utf16_item THEN ... END - CASE utf16_item WHEN latin1_item THEN ... END + is replaced to: - is replaced to: + CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END - CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END - */ - if (agg_arg_charsets_for_comparison(cmp_collation, args, nwhens + 1)) - return TRUE; - } + 2. Converter installed for the left WHEN item: - for (uint i= 0; i <= (uint)TIME_RESULT; i++) - { - if (m_found_types & (1U << i) && !cmp_items[i]) - { - DBUG_ASSERT((Item_result)i != ROW_RESULT); - if (!(cmp_items[i]= - cmp_item::get_comparator((Item_result)i, date_arg, - cmp_collation.collation))) - return TRUE; - } - } + CASE utf16_item WHEN latin1_item THEN ... END + + is replaced to: + + CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END + */ + if (agg_arg_charsets_for_comparison(cmp_collation, args, ncases + 1)) + return true; } - return FALSE; + + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + + return false; } -Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) +Item* Item_func_case_simple::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *cond) { - if (first_expr_num == -1) - { - // None of the arguments are in a comparison context - Item_args::propagate_equal_fields(thd, Context_identity(), cond); - return this; - } + const Type_handler *first_expr_cmp_handler; + first_expr_cmp_handler= args[0]->type_handler_for_comparison(); /* - First, replace CASE expression. - We cannot replace the CASE (the switch) argument if + Cannot replace the CASE (the switch) argument if there are multiple comparison types were found, or found a single comparison type that is not equal to args[0]->cmp_type(). @@ -3320,87 +3210,103 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ WHEN 'str2' THEN TRUE ELSE FALSE END; */ - if (m_found_types == (1UL << left_cmp_type)) - change_item_tree_if_needed(thd, args, - args[0]->propagate_equal_fields(thd, Context(ANY_SUBST, left_cmp_type, - cmp_collation.collation), - cond)); - uint i= 1; - for (; i <= nwhens ; i++) // WHEN expressions + if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type())) + propagate_and_change_item_tree(thd, &args[0], cond, + Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation)); + + /* + These arguments are in comparison. + Allow invariants of the same value during propagation. + Note, as we pass ANY_SUBST, none of the WHEN arguments will be + replaced to zero-filled constants (only IDENTITY_SUBST allows this). + Such a change for WHEN arguments would require rebuilding cmp_items. + */ + uint i, count= when_count(); + for (i= 1; i <= count; i++) { - /* - These arguments are in comparison. - Allow invariants of the same value during propagation. - Note, as we pass ANY_SUBST, none of the WHEN arguments will be - replaced to zero-filled constants (only IDENTITY_SUBST allows this). - Such a change for WHEN arguments would require rebuilding cmp_items. - */ - Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]); - change_item_tree_if_needed(thd, args + i, - args[i]->propagate_equal_fields(thd, Context(ANY_SUBST, tmp_cmp_type, - cmp_collation.collation), - cond)); + Type_handler_hybrid_field_type tmp(first_expr_cmp_handler); + if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison())) + propagate_and_change_item_tree(thd, &args[i], cond, + Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation)); } - for (; i < arg_count ; i++) // THEN expressions and optional ELSE expression + + // THEN and ELSE arguments (they are not in comparison) + for (; i < arg_count; i++) + propagate_and_change_item_tree(thd, &args[i], cond, Context_identity()); + + return this; +} + + +inline void Item_func_case::print_when_then_arguments(String *str, + enum_query_type + query_type, + Item **items, uint count) +{ + for (uint i= 0; i < count; i++) { - change_item_tree_if_needed(thd, args + i, - args[i]->propagate_equal_fields(thd, Context_identity(), cond)); + str->append(STRING_WITH_LEN("when ")); + items[i]->print(str, query_type); + str->append(STRING_WITH_LEN(" then ")); + items[i + count]->print(str, query_type); + str->append(' '); } - return this; } -uint Item_func_case::decimal_precision() const +inline void Item_func_case::print_else_argument(String *str, + enum_query_type query_type, + Item *item) { - int max_int_part=0; - for (uint i=first_expr_num + 1 + nwhens ; i < arg_count; i++) - set_if_bigger(max_int_part, args[i]->decimal_int_part()); - return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION); + str->append(STRING_WITH_LEN("else ")); + item->print(str, query_type); + str->append(' '); } -/** - @todo - Fix this so that it prints the whole CASE expression -*/ +void Item_func_case_searched::print(String *str, enum_query_type query_type) +{ + Item **pos; + str->append(STRING_WITH_LEN("case ")); + print_when_then_arguments(str, query_type, &args[0], when_count()); + if ((pos= Item_func_case_searched::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); + str->append(STRING_WITH_LEN("end")); +} + -void Item_func_case::print(String *str, enum_query_type query_type) +void Item_func_case_simple::print(String *str, enum_query_type query_type) { + Item **pos; str->append(STRING_WITH_LEN("case ")); - if (first_expr_num != -1) - { - args[0]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - for (uint i= first_expr_num + 1 ; i < nwhens + first_expr_num + 1; i++) - { - str->append(STRING_WITH_LEN("when ")); - args[i]->print(str, query_type); - str->append(STRING_WITH_LEN(" then ")); - args[i+nwhens]->print(str, query_type); - str->append(' '); - } - if (else_expr_num != -1) - { - str->append(STRING_WITH_LEN("else ")); - args[else_expr_num]->print(str, query_type); - str->append(' '); - } + args[0]->print_parenthesised(str, query_type, precedence()); + str->append(' '); + print_when_then_arguments(str, query_type, &args[1], when_count()); + if ((pos= Item_func_case_simple::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); str->append(STRING_WITH_LEN("end")); } -void Item_func_case::cleanup() +void Item_func_decode_oracle::print(String *str, enum_query_type query_type) { - uint i; - DBUG_ENTER("Item_func_case::cleanup"); - Item_func::cleanup(); - for (i= 0; i <= (uint)TIME_RESULT; i++) + str->append(func_name()); + str->append('('); + args[0]->print(str, query_type); + for (uint i= 1, count= when_count() ; i <= count; i++) + { + str->append(','); + args[i]->print(str, query_type); + str->append(','); + args[i+count]->print(str, query_type); + } + Item **else_expr= Item_func_case_simple::else_expr_addr(); + if (else_expr) { - delete cmp_items[i]; - cmp_items[i]= 0; + str->append(','); + (*else_expr)->print(str, query_type); } - DBUG_VOID_RETURN; + str->append(')'); } @@ -3451,12 +3357,25 @@ double Item_func_coalesce::real_op() } -bool Item_func_coalesce::date_op(MYSQL_TIME *ltime,uint fuzzydate) +bool Item_func_coalesce::date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + for (uint i= 0; i < arg_count; i++) + { + Datetime dt(current_thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type())) + return (null_value= false); + } + return (null_value= true); +} + + +bool Item_func_coalesce::time_op(MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - if (!args[i]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES)) + if (!Time(args[i]).copy_to_mysql_time(ltime)) return (null_value= false); } return (null_value= true); @@ -3478,33 +3397,6 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value) } -void Item_hybrid_func::fix_attributes(Item **items, uint nitems) -{ - switch (Item_hybrid_func::result_type()) { - case STRING_RESULT: - if (count_string_result_length(Item_hybrid_func::field_type(), - items, nitems)) - return; - break; - case DECIMAL_RESULT: - collation.set_numeric(); - count_decimal_length(items, nitems); - break; - case REAL_RESULT: - collation.set_numeric(); - count_real_length(items, nitems); - break; - case INT_RESULT: - collation.set_numeric(); - count_only_length(items, nitems); - decimals= 0; - break; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - } -} - /**************************************************************************** Classes and function for the IN operator ****************************************************************************/ @@ -3771,13 +3663,20 @@ void in_datetime::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_temporal_packed(warn_item); + buff->val= item->val_datetime_packed(); + buff->unsigned_flag= 1L; +} + +void in_time::set(uint pos,Item *item) +{ + struct packed_longlong *buff= &((packed_longlong*) base)[pos]; + + buff->val= item->val_time_packed(); buff->unsigned_flag= 1L; } -uchar *in_datetime::get_value(Item *item) +uchar *in_temporal::get_value_internal(Item *item, enum_field_types f_type) { - enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item); tmp.val= item->val_temporal_packed(f_type); if (item->null_value) return 0; @@ -3785,7 +3684,7 @@ uchar *in_datetime::get_value(Item *item) return (uchar*) &tmp; } -Item *in_datetime::create_item(THD *thd) +Item *in_temporal::create_item(THD *thd) { return new (thd->mem_root) Item_datetime(thd); } @@ -3846,25 +3745,95 @@ Item *in_decimal::create_item(THD *thd) } -cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item, - CHARSET_INFO *cs) +bool Predicant_to_list_comparator::alloc_comparators(THD *thd, uint nargs) { - switch (type) { - case STRING_RESULT: - return new cmp_item_sort_string(cs); - case INT_RESULT: - return new cmp_item_int; - case REAL_RESULT: - return new cmp_item_real; - case ROW_RESULT: - return new cmp_item_row; - case DECIMAL_RESULT: - return new cmp_item_decimal; - case TIME_RESULT: - DBUG_ASSERT(warn_item); - return new cmp_item_datetime(warn_item); + size_t nbytes= sizeof(Predicant_to_value_comparator) * nargs; + if (!(m_comparators= (Predicant_to_value_comparator *) thd->alloc(nbytes))) + return true; + memset(m_comparators, 0, nbytes); + return false; +} + + +bool Predicant_to_list_comparator::add_value(const char *funcname, + Item_args *args, + uint value_index) +{ + DBUG_ASSERT(m_predicant_index < args->argument_count()); + DBUG_ASSERT(value_index < args->argument_count()); + Type_handler_hybrid_field_type tmp; + Item *tmpargs[2]; + tmpargs[0]= args->arguments()[m_predicant_index]; + tmpargs[1]= args->arguments()[value_index]; + if (tmp.aggregate_for_comparison(funcname, tmpargs, 2, true)) + { + DBUG_ASSERT(current_thd->is_error()); + return true; + } + m_comparators[m_comparator_count].m_handler= tmp.type_handler(); + m_comparators[m_comparator_count].m_arg_index= value_index; + m_comparator_count++; + return false; +} + + +bool Predicant_to_list_comparator::add_value_skip_null(const char *funcname, + Item_args *args, + uint value_index, + bool *nulls_found) +{ + /* + Skip explicit NULL constant items. + Using real_item() to correctly detect references to explicit NULLs + in HAVING clause, e.g. in this example "b" is skipped: + SELECT a,NULL AS b FROM t1 GROUP BY a HAVING 'A' IN (b,'A'); + */ + if (args->arguments()[value_index]->real_item()->type() == Item::NULL_ITEM) + { + *nulls_found= true; + return false; + } + return add_value(funcname, args, value_index); +} + + +void Predicant_to_list_comparator:: + detect_unique_handlers(Type_handler_hybrid_field_type *compatible, + uint *unique_count, + uint *found_types) +{ + *unique_count= 0; + *found_types= 0; + for (uint i= 0; i < m_comparator_count; i++) + { + uint idx; + if (find_handler(&idx, m_comparators[i].m_handler, i)) + { + m_comparators[i].m_handler_index= i; // New unique handler + (*unique_count)++; + (*found_types)|= 1U << m_comparators[i].m_handler->cmp_type(); + compatible->set_handler(m_comparators[i].m_handler); + } + else + { + m_comparators[i].m_handler_index= idx; // Non-unique handler + } } - return 0; // to satisfy compiler :) +} + + +bool Predicant_to_list_comparator::make_unique_cmp_items(THD *thd, + CHARSET_INFO *cs) +{ + for (uint i= 0; i < m_comparator_count; i++) + { + if (m_comparators[i].m_handler && // Skip implicit NULLs + m_comparators[i].m_handler_index == i && // Skip non-unuque + !(m_comparators[i].m_cmp_item= + m_comparators[i].m_handler->make_cmp_item(thd, cs))) + return true; + } + return false; } @@ -3905,19 +3874,23 @@ cmp_item_row::~cmp_item_row() } -void cmp_item_row::alloc_comparators() +bool cmp_item_row::alloc_comparators(THD *thd, uint cols) { - if (!comparators) - comparators= (cmp_item **) current_thd->calloc(sizeof(cmp_item *)*n); + if (comparators) + { + DBUG_ASSERT(cols == n); + return false; + } + return + !(comparators= (cmp_item **) thd->calloc(sizeof(cmp_item *) * (n= cols))); } void cmp_item_row::store_value(Item *item) { DBUG_ENTER("cmp_item_row::store_value"); - n= item->cols(); - alloc_comparators(); - if (comparators) + THD *thd= current_thd; + if (!alloc_comparators(thd, item->cols())) { item->bring_value(); item->null_value= 0; @@ -3925,10 +3898,25 @@ void cmp_item_row::store_value(Item *item) { if (!comparators[i]) { - DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT); + /** + Comparators for the row elements that have temporal data types + are installed at initialization time by prepare_comparators(). + Here we install comparators for the other data types. + There is a bug in the below code. See MDEV-11511. + When performing: + (predicant0,predicant1) IN ((value00,value01),(value10,value11)) + It uses only the data type and the collation of the predicant + elements only. It should be fixed to aggregate the data type and + the collation for all elements at the N-th positions of the + predicate and all values: + - predicate0, value00, value01 + - predicate1, value10, value11 + */ + Item *elem= item->element_index(i); + const Type_handler *handler= elem->type_handler(); + DBUG_ASSERT(elem->cmp_type() != TIME_RESULT); if (!(comparators[i]= - cmp_item::get_comparator(item->element_index(i)->result_type(), 0, - item->element_index(i)->collation.collation))) + handler->make_cmp_item(thd, elem->collation.collation))) break; // new failed } comparators[i]->store_value(item->element_index(i)); @@ -4016,6 +4004,14 @@ void cmp_item_decimal::store_value(Item *item) } +int cmp_item_decimal::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_decimal()); + return my_decimal_cmp(&value, &val->m_decimal); +} + + int cmp_item_decimal::cmp(Item *arg) { my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); @@ -4037,31 +4033,60 @@ cmp_item* cmp_item_decimal::make_same() } -void cmp_item_datetime::store_value(Item *item) +void cmp_item_temporal::store_value_internal(Item *item, + enum_field_types f_type) { - enum_field_types f_type= item->field_type_for_temporal_comparison(warn_item); value= item->val_temporal_packed(f_type); m_null_value= item->null_value; } +int cmp_item_datetime::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + int cmp_item_datetime::cmp(Item *arg) { - const bool rc= value != arg->val_temporal_packed(warn_item); + const bool rc= value != arg->val_datetime_packed(); + return (m_null_value || arg->null_value) ? UNKNOWN : rc; +} + + +int cmp_item_time::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + +int cmp_item_time::cmp(Item *arg) +{ + const bool rc= value != arg->val_time_packed(); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } -int cmp_item_datetime::compare(cmp_item *ci) +int cmp_item_temporal::compare(cmp_item *ci) { - cmp_item_datetime *l_cmp= (cmp_item_datetime *)ci; + cmp_item_temporal *l_cmp= (cmp_item_temporal *)ci; return (value < l_cmp->value) ? -1 : ((value == l_cmp->value) ? 0 : 1); } cmp_item *cmp_item_datetime::make_same() { - return new cmp_item_datetime(warn_item); + return new cmp_item_datetime(); +} + + +cmp_item *cmp_item_time::make_same() +{ + return new cmp_item_time(); } @@ -4153,55 +4178,74 @@ void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref, eval_not_null_tables(NULL); } -static int srtcmp_in(const void *cs_, const void *x_, const void *y_) + +bool Item_func_in::prepare_predicant_and_values(THD *thd, uint *found_types) { - const CHARSET_INFO *cs= static_cast<const CHARSET_INFO *>(cs_); - const String *x= static_cast<const String *>(x_); - const String *y= static_cast<const String *>(y_); - return cs->coll->strnncollsp(cs, - (uchar *) x->ptr(),x->length(), - (uchar *) y->ptr(),y->length()); + uint type_cnt; + have_null= false; + + add_predicant(this, 0); + for (uint i= 1 ; i < arg_count; i++) + { + if (add_value_skip_null(Item_func_in::func_name(), this, i, &have_null)) + return true; + } + all_values_added(&m_comparator, &type_cnt, found_types); + arg_types_compatible= type_cnt < 2; + +#ifndef DBUG_OFF + Predicant_to_list_comparator::debug_print(thd); +#endif + return false; } -/* - Create 'array' for this IN predicate with the respect to its result type - and put values from <in value list> in 'array'. -*/ -bool Item_func_in::create_array(THD *thd) +bool Item_func_in::fix_length_and_dec() { - Item *date_arg= 0; + THD *thd= current_thd; + uint found_types; + m_comparator.set_handler(type_handler_varchar.type_handler_for_comparison()); + max_length= 1; - switch (m_compare_type) { - case STRING_RESULT: - array=new (thd->mem_root) in_string(thd, arg_count - 1, - (qsort2_cmp) srtcmp_in, - cmp_collation.collation); - break; - case INT_RESULT: - array= new (thd->mem_root) in_longlong(thd, arg_count - 1); - break; - case REAL_RESULT: - array= new (thd->mem_root) in_double(thd, arg_count - 1); - break; - case ROW_RESULT: - /* - The row comparator was created at the beginning but only DATETIME - items comparators were initialized. Call store_value() to setup - others. - */ - ((in_row*)array)->tmp.store_value(args[0]); - break; - case DECIMAL_RESULT: - array= new (thd->mem_root) in_decimal(thd, arg_count - 1); - break; - case TIME_RESULT: - date_arg= find_date_time_item(thd, args, arg_count, 0, true); - array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1); - break; + if (prepare_predicant_and_values(thd, &found_types)) + { + DBUG_ASSERT(thd->is_error()); // Must set error + return TRUE; } - if (!array || thd->is_fatal_error) // OOM - return true; + + if (arg_types_compatible) // Bisection condition #1 + { + if (m_comparator.type_handler()-> + Item_func_in_fix_comparator_compatible_types(thd, this)) + return TRUE; + } + else + { + DBUG_ASSERT(m_comparator.cmp_type() != ROW_RESULT); + if ( fix_for_scalar_comparison_using_cmp_items(thd, found_types)) + return TRUE; + } + + DBUG_EXECUTE_IF("Item_func_in", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: types_compatible=%s bisect=%s", + arg_types_compatible ? "yes" : "no", + array != NULL ? "yes" : "no");); + return FALSE; +} + + +/** + Populate Item_func_in::array with constant not-NULL arguments and sort them. + + Sets "have_null" to true if some of the values appeared to be NULL. + Note, explicit NULLs were found during prepare_predicant_and_values(). + So "have_null" can already be true before the fix_in_vector() call. + Here we additionally catch implicit NULLs. +*/ +void Item_func_in::fix_in_vector() +{ + DBUG_ASSERT(array); uint j=0; for (uint i=1 ; i < arg_count ; i++) { @@ -4211,7 +4255,7 @@ bool Item_func_in::create_array(THD *thd) else { /* - We don't put NULL values in array to avoid erronous matches in + We don't put NULL values in array, to avoid erronous matches in bisection. */ have_null= 1; @@ -4219,134 +4263,33 @@ bool Item_func_in::create_array(THD *thd) } if ((array->used_count= j)) array->sort(); - return false; } -bool Item_func_in::fix_length_and_dec() -{ - Item **arg, **arg_end; - bool const_itm= 1; - THD *thd= current_thd; - /* TRUE <=> arguments values will be compared as DATETIMEs. */ - Item *date_arg= 0; - uint found_types= 0; - uint type_cnt= 0, i; - m_compare_type= STRING_RESULT; - left_cmp_type= args[0]->cmp_type(); - if (!(found_types= collect_cmp_types(args, arg_count, true))) - return TRUE; - - for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++) - { - if (!arg[0]->const_item()) - { - const_itm= 0; - break; - } - } - for (i= 0; i <= (uint)TIME_RESULT; i++) - { - if (found_types & (1U << i)) - { - (type_cnt)++; - m_compare_type= (Item_result) i; - } - } - - /* - First conditions for bisection to be possible: - 1. All types are similar, and - 2. All expressions in <in value list> are const - */ - bool bisection_possible= - type_cnt == 1 && // 1 - const_itm; // 2 - if (bisection_possible) - { - /* - In the presence of NULLs, the correct result of evaluating this item - must be UNKNOWN or FALSE. To achieve that: - - If type is scalar, we can use bisection and the "have_null" boolean. - - If type is ROW, we will need to scan all of <in value list> when - searching, so bisection is impossible. Unless: - 3. UNKNOWN and FALSE are equivalent results - 4. Neither left expression nor <in value list> contain any NULL value - */ - - if (m_compare_type == ROW_RESULT && - ((!is_top_level_item() || negated) && // 3 - (list_contains_null() || args[0]->maybe_null))) // 4 - bisection_possible= false; - } - - if (type_cnt == 1) - { - if (m_compare_type == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return TRUE; - arg_types_compatible= TRUE; - - if (m_compare_type == ROW_RESULT) - { - uint cols= args[0]->cols(); - cmp_item_row *cmp= 0; - - if (bisection_possible) - { - array= new (thd->mem_root) in_row(thd, arg_count-1, 0); - if (!array) - return TRUE; - cmp= &((in_row*)array)->tmp; - } - else - { - if (!(cmp= new (thd->mem_root) cmp_item_row)) - return TRUE; - cmp_items[ROW_RESULT]= cmp; - } - cmp->n= cols; - cmp->alloc_comparators(); +/** + Convert all items in <in value list> to INT. - for (uint col= 0; col < cols; col++) - { - date_arg= find_date_time_item(thd, args, arg_count, col, true); - if (date_arg) - { - cmp_item **cmp= 0; - if (array) - cmp= ((in_row*)array)->tmp.comparators + col; - else - cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col; - *cmp= new (thd->mem_root) cmp_item_datetime(date_arg); - if (!(*cmp)) - return TRUE; - } - } - } - } + IN must compare INT columns and constants as int values (the same + way as equality does). + So we must check here if the column on the left and all the constant + values on the right can be compared as integers and adjust the + comparison type accordingly. - if (bisection_possible) + See the comment about the similar block in Item_bool_func2 +*/ +bool Item_func_in::value_list_convert_const_to_int(THD *thd) +{ + if (args[0]->real_item()->type() == FIELD_ITEM && + !thd->lex->is_view_context_analysis()) { - /* - IN must compare INT columns and constants as int values (the same - way as equality does). - So we must check here if the column on the left and all the constant - values on the right can be compared as integers and adjust the - comparison type accordingly. - - And see the comment for Item_func::convert_const_compared_to_int_field - */ - if (args[0]->real_item()->type() == FIELD_ITEM && - !thd->lex->is_view_context_analysis() && m_compare_type != INT_RESULT) + Item_field *field_item= (Item_field*) (args[0]->real_item()); + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { - Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->field_type() == MYSQL_TYPE_LONGLONG || - field_item->field_type() == MYSQL_TYPE_YEAR) + bool all_converted= true; + Item **arg, **arg_end; + for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) { - bool all_converted= true; - for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) - { /* Explicit NULLs should not affect data cmp_type resolution: - we ignore NULLs when calling collect_cmp_type() @@ -4358,34 +4301,96 @@ bool Item_func_in::fix_length_and_dec() if (arg[0]->type() != Item::NULL_ITEM && !convert_const_to_int(thd, field_item, &arg[0])) all_converted= false; - } - if (all_converted) - m_compare_type= INT_RESULT; } + if (all_converted) + m_comparator.set_handler(&type_handler_longlong); } - if (create_array(thd)) - return TRUE; } - else + return thd->is_fatal_error; // Catch errrors in convert_const_to_int +} + + +/** + Historically this code installs comparators at initialization time + for temporal ROW elements only. All other comparators are installed later, + during the first store_value(). This causes the bug MDEV-11511. + See also comments in cmp_item_row::store_value(). +*/ +bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count) +{ + for (uint col= 0; col < n; col++) { - if (found_types & (1U << TIME_RESULT)) - date_arg= find_date_time_item(thd, args, arg_count, 0, true); - if (found_types & (1U << STRING_RESULT) && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return TRUE; - for (i= 0; i <= (uint) TIME_RESULT; i++) + Item *date_arg= find_date_time_item(args, arg_count, col); + if (date_arg) { - if (found_types & (1U << i) && !cmp_items[i]) - { - if (!cmp_items[i] && !(cmp_items[i]= - cmp_item::get_comparator((Item_result)i, date_arg, - cmp_collation.collation))) - return TRUE; - } + // TODO: do like the scalar comparators do + const Type_handler *h= date_arg->type_handler(); + comparators[col]= h->field_type() == MYSQL_TYPE_TIME ? + (cmp_item *) new (thd->mem_root) cmp_item_time() : + (cmp_item *) new (thd->mem_root) cmp_item_datetime(); + if (!comparators[col]) + return true; } } - max_length= 1; - return FALSE; + return false; +} + + +bool Item_func_in::fix_for_row_comparison_using_bisection(THD *thd) +{ + uint cols= args[0]->cols(); + if (unlikely(!(array= new (thd->mem_root) in_row(thd, arg_count-1, 0)))) + return true; + cmp_item_row *cmp= &((in_row*)array)->tmp; + if (cmp->alloc_comparators(thd, cols) || + cmp->prepare_comparators(thd, args, arg_count)) + return true; + /* + Only DATETIME items comparators were initialized. + Call store_value() to setup others. + */ + cmp->store_value(args[0]); + if (unlikely(thd->is_fatal_error)) // OOM + return true; + fix_in_vector(); + return false; +} + + +/** + This method is called for scalar data types when bisection is not possible, + for example: + - Some of args[1..arg_count] are not constants. + - args[1..arg_count] are constants, but pairs {args[0],args[1..arg_count]} + are compared by different data types, e.g.: + WHERE decimal_expr IN (1, 1e0) + The pair {args[0],args[1]} is compared by type_handler_decimal. + The pair {args[0],args[2]} is compared by type_handler_double. +*/ +bool Item_func_in::fix_for_scalar_comparison_using_cmp_items(THD *thd, + uint found_types) +{ + if (found_types & (1U << STRING_RESULT) && + agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) + return true; + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + return false; +} + + +/** + This method is called for the ROW data type when bisection is not possible. +*/ +bool Item_func_in::fix_for_row_comparison_using_cmp_items(THD *thd) +{ + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row); + DBUG_ASSERT(get_comparator_cmp_item(0)); + cmp_item_row *cmp_row= (cmp_item_row*) get_comparator_cmp_item(0); + return cmp_row->alloc_comparators(thd, args[0]->cols()) || + cmp_row->prepare_comparators(thd, args, arg_count); } @@ -4427,9 +4432,7 @@ void Item_func_in::print(String *str, enum_query_type query_type) longlong Item_func_in::val_int() { - cmp_item *in_item; DBUG_ASSERT(fixed == 1); - uint value_added_map= 0; if (array) { bool tmp=array->find(args[0]); @@ -4447,42 +4450,34 @@ longlong Item_func_in::val_int() if ((null_value= args[0]->real_item()->type() == NULL_ITEM)) return 0; - have_null= 0; - for (uint i= 1 ; i < arg_count ; i++) + null_value= have_null; + uint idx; + if (!Predicant_to_list_comparator::cmp(this, &idx, &null_value)) { - if (args[i]->real_item()->type() == NULL_ITEM) - { - have_null= TRUE; - continue; - } - Item_result cmp_type= item_cmp_type(left_cmp_type, args[i]); - in_item= cmp_items[(uint)cmp_type]; - DBUG_ASSERT(in_item); - if (!(value_added_map & (1U << (uint)cmp_type))) - { - in_item->store_value(args[0]); - value_added_map|= 1U << (uint)cmp_type; - } - const int rc= in_item->cmp(args[i]); - if (rc == FALSE) - return (longlong) (!negated); - have_null|= (rc == UNKNOWN); + null_value= false; + return (longlong) (!negated); } - - null_value= have_null; return (longlong) (!null_value && negated); } -Item *Item_func_in::build_clone(THD *thd, MEM_ROOT *mem_root) +void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding) { - Item_func_in *clone= (Item_func_in *) Item_func::build_clone(thd, mem_root); - if (clone) + THD *thd= current_thd; + + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (to_be_transformed_into_in_subq(thd)) { - bzero(&clone->cmp_items, sizeof(cmp_items)); - clone->fix_length_and_dec(); + transform_into_subq= true; + thd->lex->current_select->in_funcs.push_back(this, thd->mem_root); } - return clone; + + if (arena) + thd->restore_active_arena(arena, &backup); + + emb_on_expr_nest= embedding; } @@ -4625,11 +4620,9 @@ Item_cond::fix_fields(THD *thd, Item **ref) thd->restore_active_arena(arena, &backup); } - // item can be substituted in fix_fields - if ((!item->fixed && - item->fix_fields(thd, li.ref())) || - (item= *li.ref())->check_cols(1)) + if (item->fix_fields_if_needed_for_bool(thd, li.ref())) return TRUE; /* purecov: inspected */ + item= *li.ref(); // item can be substituted in fix_fields used_tables_cache|= item->used_tables(); if (item->const_item() && !item->with_param && !item->is_expensive() && !cond_has_datetime_is_null(item)) @@ -4671,7 +4664,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) with_sum_func|= item->with_sum_func; with_param|= item->with_param; with_field|= item->with_field; - with_subselect|= item->has_subquery(); + m_with_subquery|= item->with_subquery(); with_window_func|= item->with_window_func; maybe_null|= item->maybe_null; } @@ -4868,17 +4861,14 @@ Item *Item_cond::propagate_equal_fields(THD *thd, DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); DBUG_ASSERT(arg_count == 0); List_iterator<Item> li(list); - Item *item; - while ((item= li++)) + while (li++) { /* - The exact value of the second parameter to propagate_equal_fields() + The exact value of the last parameter to propagate_and_change_item_tree() is not important at this point. Item_func derivants will create and pass their own context to the arguments. */ - Item *new_item= item->propagate_equal_fields(thd, Context_boolean(), cond); - if (new_item && new_item != item) - thd->change_item_tree(li.ref(), new_item); + propagate_and_change_item_tree(thd, li.ref(), cond, Context_boolean()); } return this; } @@ -4989,23 +4979,23 @@ void Item_cond::neg_arguments(THD *thd) @retval clone of the item - 0 if an error occured + 0 if an error occurred */ -Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root) +Item *Item_cond::build_clone(THD *thd) { List_iterator_fast<Item> li(list); Item *item; - Item_cond *copy= (Item_cond *) get_copy(thd, mem_root); + Item_cond *copy= (Item_cond *) get_copy(thd); if (!copy) return 0; copy->list.empty(); while ((item= li++)) { - Item *arg_clone= item->build_clone(thd, mem_root); + Item *arg_clone= item->build_clone(thd); if (!arg_clone) return 0; - if (copy->list.push_back(arg_clone, mem_root)) + if (copy->list.push_back(arg_clone, thd->mem_root)) return 0; } return copy; @@ -5404,12 +5394,11 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, return FALSE; } - bool Item_func_like::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); if (Item_bool_func2::fix_fields(thd, ref) || - escape_item->fix_fields(thd, &escape_item) || + escape_item->fix_fields_if_needed_for_scalar(thd, &escape_item) || fix_escape_item(thd, escape_item, &cmp_value1, escape_used_in_parsing, cmp_collation.collation, &escape)) return TRUE; @@ -5558,7 +5547,7 @@ bool Regexp_processor_pcre::compile(String *pattern, bool send_error) m_pcre= pcre_compile(pattern->c_ptr_safe(), m_library_flags, &pcreErrorStr, &pcreErrorOffset, NULL); - if (m_pcre == NULL) + if (unlikely(m_pcre == NULL)) { if (send_error) { @@ -5577,7 +5566,7 @@ bool Regexp_processor_pcre::compile(Item *item, bool send_error) char buff[MAX_FIELD_WIDTH]; String tmp(buff, sizeof(buff), &my_charset_bin); String *pattern= item->val_str(&tmp); - if (item->null_value || compile(pattern, send_error)) + if (unlikely(item->null_value) || (unlikely(compile(pattern, send_error)))) return true; return false; } @@ -5696,15 +5685,15 @@ int Regexp_processor_pcre::pcre_exec_with_warn(const pcre *code, int rc= pcre_exec(code, extra, subject, length, startoffset, options, ovector, ovecsize); DBUG_EXECUTE_IF("pcre_exec_error_123", rc= -123;); - if (rc < PCRE_ERROR_NOMATCH) + if (unlikely(rc < PCRE_ERROR_NOMATCH)) pcre_exec_warn(rc); return rc; } -bool Regexp_processor_pcre::exec(const char *str, int length, int offset) +bool Regexp_processor_pcre::exec(const char *str, size_t length, size_t offset) { - m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra, str, length, offset, 0, + m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra, str, (int)length, (int)offset, 0, m_SubStrVec, array_elements(m_SubStrVec)); return false; } @@ -5815,6 +5804,7 @@ Item_func_regexp_instr::fix_length_and_dec() re.init(cmp_collation.collation, 0); re.fix_owner(this, args[0], args[1]); + max_length= MY_INT32_NUM_DECIMAL_DIGITS; // See also Item_func_locate return FALSE; } @@ -6295,10 +6285,11 @@ Item *Item_bool_rowready_func2::negated_item(THD *thd) of the type Item_field or Item_direct_view_ref(Item_field). */ -Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item): +Item_equal::Item_equal(THD *thd, const Type_handler *handler, + Item *f1, Item *f2, bool with_const_item): Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0), context_field(NULL), link_equal_fields(FALSE), - m_compare_type(item_cmp_type(f1, f2)), + m_compare_handler(handler), m_compare_collation(f2->collation.collation) { const_item_cache= 0; @@ -6324,7 +6315,7 @@ Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item): Item_equal::Item_equal(THD *thd, Item_equal *item_equal): Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0), context_field(NULL), link_equal_fields(FALSE), - m_compare_type(item_equal->m_compare_type), + m_compare_handler(item_equal->m_compare_handler), m_compare_collation(item_equal->m_compare_collation) { const_item_cache= 0; @@ -6367,7 +6358,7 @@ void Item_equal::add_const(THD *thd, Item *c) return; } Item *const_item= get_const(); - switch (Item_equal::compare_type()) { + switch (Item_equal::compare_type_handler()->cmp_type()) { case TIME_RESULT: { enum_field_types f_type= context_field->field_type(); @@ -6740,7 +6731,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; - DBUG_ASSERT(!item->with_sum_func && !item->with_subselect); + DBUG_ASSERT(!item->with_sum_func && !item->with_subquery()); if (item->maybe_null) maybe_null= 1; if (!item->get_item_equal()) @@ -6847,9 +6838,9 @@ longlong Item_equal::val_int() bool Item_equal::fix_length_and_dec() { Item *item= get_first(NO_PARTICULAR_TAB, NULL); - eval_item= cmp_item::get_comparator(item->cmp_type(), item, - item->collation.collation); - return FALSE; + const Type_handler *handler= item->type_handler(); + eval_item= handler->make_cmp_item(current_thd, item->collation.collation); + return eval_item == NULL; } |