summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc1987
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;
}