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.cc1984
1 files changed, 1152 insertions, 832 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 192e06566ff..d2ffa0e64f9 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -34,47 +34,6 @@
#include "sql_time.h" // make_truncated_value_warning
#include "sql_base.h" // dynamic_column_error_message
-static Item_result item_store_type(Item_result a, Item *item,
- my_bool unsigned_flag)
-{
- Item_result b= item->result_type();
-
- if (a == STRING_RESULT || b == STRING_RESULT)
- return STRING_RESULT;
- else if (a == REAL_RESULT || b == REAL_RESULT)
- return REAL_RESULT;
- else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT ||
- unsigned_flag != item->unsigned_flag)
- return DECIMAL_RESULT;
- else
- return INT_RESULT;
-}
-
-static void agg_result_type(Item_result *type, Item **items, uint nitems)
-{
- Item **item, **item_end;
- my_bool unsigned_flag= 0;
-
- *type= STRING_RESULT;
- /* Skip beginning NULL items */
- for (item= items, item_end= item + nitems; item < item_end; item++)
- {
- if ((*item)->type() != Item::NULL_ITEM)
- {
- *type= (*item)->result_type();
- unsigned_flag= (*item)->unsigned_flag;
- item++;
- break;
- }
- }
- /* Combine result types. Note: NULL items don't affect the result */
- for (; item < item_end; item++)
- {
- if ((*item)->type() != Item::NULL_ITEM)
- *type= item_store_type(*type, *item, unsigned_flag);
- }
-}
-
/**
find an temporal type (item) that others will be converted to
@@ -166,7 +125,7 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
for (uint i= 1 ; i < nitems ; i++)
{
unsigned_count+= items[i]->unsigned_flag;
- type[0]= item_cmp_type(type[0], items[i]->cmp_type());
+ type[0]= item_cmp_type(type[0], items[i]);
/*
When aggregating types of two row expressions we have to check
that they have the same cardinality and that each component
@@ -192,6 +151,22 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
@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
@@ -205,14 +180,50 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
@return aggregated field type.
*/
-enum_field_types agg_field_type(Item **items, uint nitems)
+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 )
- return (enum_field_types)-1;
+ 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++)
- res= Field::field_type_merge(res, items[i]->field_type());
+ {
+ 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;
}
@@ -238,98 +249,28 @@ static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE)
{
uint i;
uint found_types;
- Item_result left_result= items[0]->cmp_type();
+ Item_result left_cmp_type= items[0]->cmp_type();
DBUG_ASSERT(nitems > 1);
found_types= 0;
for (i= 1; i < nitems ; i++)
{
if (skip_nulls && items[i]->type() == Item::NULL_ITEM)
continue; // Skip NULL constant items
- if ((left_result == ROW_RESULT ||
+ if ((left_cmp_type == ROW_RESULT ||
items[i]->cmp_type() == ROW_RESULT) &&
cmp_row_type(items[0], items[i]))
return 0;
- found_types|= 1U << (uint)item_cmp_type(left_result,
- items[i]->cmp_type());
+ found_types|= 1U << (uint) item_cmp_type(left_cmp_type, items[i]);
}
/*
Even if all right-hand items are NULLs and we are skipping them all, we need
at least one type bit in the found_type bitmask.
*/
if (skip_nulls && !found_types)
- found_types= 1U << (uint)left_result;
+ found_types= 1U << (uint) left_cmp_type;
return found_types;
}
-static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
- const char *fname)
-{
- my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0),
- c1.collation->name,c1.derivation_name(),
- c2.collation->name,c2.derivation_name(),
- fname);
-}
-
-
-Item_bool_func2* Eq_creator::create(Item *a, Item *b) const
-{
- return new Item_func_eq(a, b);
-}
-
-Item_bool_func2* Eq_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_eq(b, a);
-}
-
-Item_bool_func2* Ne_creator::create(Item *a, Item *b) const
-{
- return new Item_func_ne(a, b);
-}
-
-Item_bool_func2* Ne_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_ne(b, a);
-}
-
-Item_bool_func2* Gt_creator::create(Item *a, Item *b) const
-{
- return new Item_func_gt(a, b);
-}
-
-Item_bool_func2* Gt_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_lt(b, a);
-}
-
-Item_bool_func2* Lt_creator::create(Item *a, Item *b) const
-{
- return new Item_func_lt(a, b);
-}
-
-Item_bool_func2* Lt_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_gt(b, a);
-}
-
-Item_bool_func2* Ge_creator::create(Item *a, Item *b) const
-{
- return new Item_func_ge(a, b);
-}
-
-Item_bool_func2* Ge_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_le(b, a);
-}
-
-Item_bool_func2* Le_creator::create(Item *a, Item *b) const
-{
- return new Item_func_le(a, b);
-}
-
-Item_bool_func2* Le_creator::create_swap(Item *a, Item *b) const
-{
- return new Item_func_ge(b, a);
-}
/*
Test functions
@@ -477,8 +418,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
my_bitmap_map *old_maps[2];
ulonglong UNINIT_VAR(orig_field_val); /* original field value if valid */
- LINT_INIT(old_maps[0]);
- LINT_INIT(old_maps[1]);
+ LINT_INIT_STRUCT(old_maps);
/* table->read_set may not be set if we come here from a CREATE TABLE */
if (table && table->read_set)
@@ -510,7 +450,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
if (0 == field_cmp)
{
- Item *tmp= new Item_int_with_ref(field->val_int(), *item,
+ Item *tmp= new (thd->mem_root) Item_int_with_ref(thd, field->val_int(), *item,
MY_TEST(field->flags & UNSIGNED_FLAG));
if (tmp)
thd->change_item_tree(item, tmp);
@@ -533,49 +473,17 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
}
-void Item_bool_func2::fix_length_and_dec()
+/*
+ Make a special case of compare with fields to get nicer comparisons
+ of bigint numbers with constant string.
+ This directly contradicts the manual (number and a string should
+ be compared as doubles), but seems to provide more
+ "intuitive" behavior in some cases (but less intuitive in others).
+*/
+void Item_func::convert_const_compared_to_int_field(THD *thd)
{
- max_length= 1; // Function returns 0 or 1
-
- /*
- As some compare functions are generated after sql_yacc,
- we have to check for out of memory conditions here
- */
- if (!args[0] || !args[1])
- return;
-
- /*
- We allow to convert to Unicode character sets in some cases.
- The conditions when conversion is possible are:
- - arguments A and B have different charsets
- - A wins according to coercibility rules
- - character set of A is superset for character set of B
-
- If all of the above is true, then it's possible to convert
- B into the character set of A, and then compare according
- to the collation of A.
- */
-
- DTCollation coll;
- if (args[0]->cmp_type() == STRING_RESULT &&
- args[1]->cmp_type() == STRING_RESULT &&
- agg_arg_charsets_for_comparison(coll, args, 2))
- return;
-
- args[0]->cmp_context= args[1]->cmp_context=
- item_cmp_type(args[0]->result_type(), args[1]->result_type());
-
- /*
- Make a special case of compare with fields to get nicer comparisons
- of bigint numbers with constant string.
- This directly contradicts the manual (number and a string should
- be compared as doubles), but seems to provide more
- "intuitive" behavior in some cases (but less intuitive in others).
-
- But disable conversion in case of LIKE function.
- */
- THD *thd= current_thd;
- if (functype() != LIKE_FUNC && !thd->lex->is_ps_or_view_context_analysis())
+ DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3
+ if (!thd->lex->is_ps_or_view_context_analysis())
{
int field;
if (args[field= 0]->real_item()->type() == FIELD_ITEM ||
@@ -583,16 +491,48 @@ void Item_bool_func2::fix_length_and_dec()
{
Item_field *field_item= (Item_field*) (args[field]->real_item());
if ((field_item->field_type() == MYSQL_TYPE_LONGLONG ||
- field_item->field_type() == MYSQL_TYPE_YEAR) &&
- convert_const_to_int(thd, field_item, &args[!field]))
- args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
+ field_item->field_type() == MYSQL_TYPE_YEAR))
+ convert_const_to_int(thd, field_item, &args[!field]);
}
}
- set_cmp_func();
}
-int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
+bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp)
+{
+ DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3
+
+ if (args[0]->cmp_type() == STRING_RESULT &&
+ args[1]->cmp_type() == STRING_RESULT)
+ {
+ DTCollation tmp;
+ if (agg_arg_charsets_for_comparison(tmp, args, 2))
+ return true;
+ cmp->m_compare_collation= tmp.collation;
+ }
+ // Convert constants when compared to int/year field
+ DBUG_ASSERT(functype() != LIKE_FUNC);
+ convert_const_compared_to_int_field(thd);
+
+ return cmp->set_cmp_func(this, &args[0], &args[1], true);
+}
+
+
+void Item_bool_rowready_func2::fix_length_and_dec()
+{
+ max_length= 1; // Function returns 0 or 1
+
+ /*
+ As some compare functions are generated after sql_yacc,
+ we have to check for out of memory conditions here
+ */
+ if (!args[0] || !args[1])
+ return;
+ setup_args_and_comparator(current_thd, &cmp);
+}
+
+
+int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type)
{
owner= item;
func= comparator_matrix[type]
@@ -600,7 +540,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
switch (type) {
case TIME_RESULT:
- cmp_collation.collation= &my_charset_numeric;
+ m_compare_collation= &my_charset_numeric;
break;
case ROW_RESULT:
{
@@ -620,38 +560,12 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
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))
+ if (comparators[i].set_cmp_func(owner, (*a)->addr(i),
+ (*b)->addr(i), set_null))
return 1;
}
break;
}
- case STRING_RESULT:
- {
- if (cmp_collation.collation == &my_charset_bin)
- {
- /*
- We are using BLOB/BINARY/VARBINARY, change to compare byte by byte,
- without removing end space
- */
- if (func == &Arg_comparator::compare_string)
- func= &Arg_comparator::compare_binary_string;
- else if (func == &Arg_comparator::compare_e_string)
- func= &Arg_comparator::compare_e_binary_string;
-
- /*
- As this is binary compassion, mark all fields that they can't be
- transformed. Otherwise we would get into trouble with comparisons
- like:
- WHERE col= 'j' AND col LIKE BINARY 'j'
- which would be transformed to:
- WHERE col= 'j'
- */
- (*a)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0);
- (*b)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0);
- }
- break;
- }
case INT_RESULT:
{
if (func == &Arg_comparator::compare_int_signed)
@@ -670,6 +584,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
}
break;
}
+ case STRING_RESULT:
case DECIMAL_RESULT:
break;
case REAL_RESULT:
@@ -684,102 +599,10 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
}
break;
}
- case IMPOSSIBLE_RESULT:
- DBUG_ASSERT(0);
- break;
}
return 0;
}
-/**
- Parse date provided in a string to a MYSQL_TIME.
-
- @param[in] thd Thread handle
- @param[in] str A string to convert
- @param[in] warn_type Type of the timestamp for issuing the warning
- @param[in] warn_name Field name for issuing the warning
- @param[out] l_time The MYSQL_TIME objects is initialized.
-
- Parses a date provided in the string str into a MYSQL_TIME object.
- The date is used for comparison, that is fuzzy dates are allowed
- independently of sql_mode.
- If the string contains an incorrect date or doesn't correspond to a date at
- all then a warning is issued. The warn_type and the warn_name arguments are
- used as the name and the type of the field when issuing the warning. If any
- input was discarded (trailing or non-timestamp-y characters), return value
- will be TRUE.
-
- @return Status flag
- @retval FALSE Success.
- @retval True Indicates failure.
-*/
-
-bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
- const char *warn_name, MYSQL_TIME *l_time)
-{
- bool value;
- MYSQL_TIME_STATUS status;
- int flags= TIME_FUZZY_DATES | MODE_INVALID_DATES;
- ErrConvString err(str);
-
- DBUG_ASSERT(warn_type != MYSQL_TIMESTAMP_TIME);
-
- if (!str_to_datetime(str->charset(), str->ptr(), str->length(),
- l_time, flags, &status))
- {
- DBUG_ASSERT(l_time->time_type == MYSQL_TIMESTAMP_DATETIME ||
- l_time->time_type == MYSQL_TIMESTAMP_DATE);
- /*
- Do not return yet, we may still want to throw a "trailing garbage"
- warning.
- */
- value= FALSE;
- }
- else
- {
- DBUG_ASSERT(l_time->time_type != MYSQL_TIMESTAMP_TIME);
- DBUG_ASSERT(status.warnings != 0); // Must be set by set_to_datetime()
- value= TRUE;
- }
-
- if (status.warnings > 0)
- make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- &err, warn_type, warn_name);
-
- return value;
-}
-
-
-/**
- Aggregate comparator argument charsets for comparison.
- One of the arguments ("a" or "b") can be replaced,
- typically by Item_string or Item_func_conv_charset.
-
- @return Aggregation result
- @retval false - if no conversion is needed,
- or if one of the arguments was converted
- @retval true - on error, if arguments are not comparable.
-
- TODO: get rid of this method eventually and refactor the calling code.
- Argument conversion should happen on the Item_func level.
- Arg_comparator should get comparable arguments.
-*/
-bool Arg_comparator::agg_arg_charsets_for_comparison()
-{
- if (cmp_collation.set((*a)->collation, (*b)->collation, MY_COLL_CMP_CONV) ||
- cmp_collation.derivation == DERIVATION_NONE)
- {
- my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name());
- return true;
- }
- if (agg_item_set_converter(cmp_collation, owner->func_name(),
- a, 1, MY_COLL_CMP_CONV, 1) ||
- agg_item_set_converter(cmp_collation, owner->func_name(),
- b, 1, MY_COLL_CMP_CONV, 1))
- return true;
- return false;
-}
-
/**
Prepare the comparator (set the comparison function) for comparing
@@ -794,17 +617,17 @@ bool Arg_comparator::agg_arg_charsets_for_comparison()
items, holding the cached converted value of the original (constant) item.
*/
-int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
- Item **a1, Item **a2,
- 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);
- if (type == STRING_RESULT &&
+ if (m_compare_type == STRING_RESULT &&
(*a)->result_type() == STRING_RESULT &&
(*b)->result_type() == STRING_RESULT)
{
@@ -812,17 +635,54 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
We must set cmp_collation here as we may be called from for an automatic
generated item, like in natural join
*/
- if (agg_arg_charsets_for_comparison())
+ if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
return 1;
}
- if (type == INT_RESULT &&
+
+ if (m_compare_type == TIME_RESULT)
+ {
+ 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;
+ }
+ return 0;
+ }
+
+ if (m_compare_type == REAL_RESULT &&
+ (((*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())))
+ {
+ /*
+ <non-const decimal expression> <cmp> <const string expression>
+ or
+ <const string expression> <cmp> <non-const decimal expression>
+
+ Do comparison as decimal rather than float, in order not to lose precision.
+ */
+ m_compare_type= DECIMAL_RESULT;
+ }
+
+ if (m_compare_type == INT_RESULT &&
(*a)->field_type() == MYSQL_TYPE_YEAR &&
(*b)->field_type() == MYSQL_TYPE_YEAR)
- type= TIME_RESULT;
+ {
+ m_compare_type= TIME_RESULT;
+ func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
+ &Arg_comparator::compare_datetime;
+ }
- a= cache_converted_constant(thd, a, &a_cache, type);
- b= cache_converted_constant(thd, b, &b_cache, type);
- return set_compare_func(owner_arg, type);
+ a= cache_converted_constant(thd, a, &a_cache, m_compare_type);
+ b= cache_converted_constant(thd, b, &b_cache, m_compare_type);
+ return set_compare_func(owner_arg, m_compare_type);
}
@@ -856,8 +716,8 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
(*value)->const_item() && type != (*value)->result_type() &&
type != TIME_RESULT)
{
- Item_cache *cache= Item_cache::get_cache(*value, type);
- cache->setup(*value);
+ Item_cache *cache= Item_cache::get_cache(thd_arg, *value, type);
+ cache->setup(thd_arg, *value);
*cache_item= cache;
return cache_item;
}
@@ -865,17 +725,6 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
}
-void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
- Item **a1, Item **b1)
-{
- owner= owner_arg;
- a= a1;
- b= b1;
- a_cache= 0;
- b_cache= 0;
- func= comparator_matrix[TIME_RESULT][is_owner_equal_func()];
-}
-
/**
Retrieves correct DATETIME value from given item.
@@ -909,34 +758,11 @@ void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
longlong
get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
- Item *warn_item, bool *is_null)
+ enum_field_types f_type, bool *is_null)
{
longlong UNINIT_VAR(value);
Item *item= **item_arg;
- enum_field_types f_type= item->cmp_type() == TIME_RESULT ?
- item->field_type() : warn_item->field_type();
-
- if (item->result_type() == INT_RESULT &&
- item->cmp_type() == TIME_RESULT &&
- item->type() == Item::CACHE_ITEM)
- {
- /* it's our Item_cache_temporal, as created below */
- DBUG_ASSERT(is_temporal_type(((Item_cache *) item)->field_type()));
- value= ((Item_cache_temporal*) item)->val_temporal_packed();
- }
- else
- {
- MYSQL_TIME ltime;
- uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
- if ((item->field_type() == MYSQL_TYPE_TIME &&
- is_temporal_type_with_date(warn_item->field_type())) ?
- item->get_date_with_conversion(&ltime, fuzzydate) :
- item->get_date(&ltime, fuzzydate |
- (f_type == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)))
- value= 0; /* invalid date */
- else
- value= pack_time(&ltime);
- }
+ value= item->val_temporal_packed(f_type);
if ((*is_null= item->null_value))
return ~(ulonglong) 0;
if (cache_arg && item->const_item() &&
@@ -945,7 +771,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
if (!thd)
thd= current_thd;
- Item_cache_temporal *cache= new Item_cache_temporal(f_type);
+ Item_cache_temporal *cache= new (thd->mem_root) Item_cache_temporal(thd, f_type);
cache->store_packed(value, item);
*cache_arg= cache;
*item_arg= cache_arg;
@@ -971,7 +797,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
1 a > b
*/
-int Arg_comparator::compare_datetime()
+int Arg_comparator::compare_temporal(enum_field_types type)
{
bool a_is_null, b_is_null;
longlong a_value, b_value;
@@ -980,12 +806,12 @@ int Arg_comparator::compare_datetime()
owner->null_value= 1;
/* Get DATE/DATETIME/TIME value of the 'a' item. */
- a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null);
+ a_value= get_datetime_value(0, &a, &a_cache, type, &a_is_null);
if (a_is_null)
return -1;
/* Get DATE/DATETIME/TIME value of the 'b' item. */
- b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null);
+ b_value= get_datetime_value(0, &b, &b_cache, type, &b_is_null);
if (b_is_null)
return -1;
@@ -997,16 +823,16 @@ int Arg_comparator::compare_datetime()
return a_value < b_value ? -1 : a_value > b_value ? 1 : 0;
}
-int Arg_comparator::compare_e_datetime()
+int Arg_comparator::compare_e_temporal(enum_field_types type)
{
bool a_is_null, b_is_null;
longlong a_value, b_value;
/* Get DATE/DATETIME/TIME value of the 'a' item. */
- a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null);
+ a_value= get_datetime_value(0, &a, &a_cache, type, &a_is_null);
/* Get DATE/DATETIME/TIME value of the 'b' item. */
- b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null);
+ b_value= get_datetime_value(0, &b, &b_cache, type, &b_is_null);
return a_is_null || b_is_null ? a_is_null == b_is_null
: a_value == b_value;
}
@@ -1020,39 +846,7 @@ int Arg_comparator::compare_string()
{
if (set_null)
owner->null_value= 0;
- return sortcmp(res1,res2,cmp_collation.collation);
- }
- }
- if (set_null)
- owner->null_value= 1;
- return -1;
-}
-
-
-/**
- Compare strings byte by byte. End spaces are also compared.
-
- @retval
- <0 *a < *b
- @retval
- 0 *b == *b
- @retval
- >0 *a > *b
-*/
-
-int Arg_comparator::compare_binary_string()
-{
- String *res1,*res2;
- if ((res1= (*a)->val_str(&value1)))
- {
- if ((res2= (*b)->val_str(&value2)))
- {
- if (set_null)
- owner->null_value= 0;
- uint res1_length= res1->length();
- uint res2_length= res2->length();
- int cmp= memcmp(res1->ptr(), res2->ptr(), MY_MIN(res1_length,res2_length));
- return cmp ? cmp : (int) (res1_length - res2_length);
+ return sortcmp(res1, res2, compare_collation());
}
}
if (set_null)
@@ -1073,18 +867,7 @@ int Arg_comparator::compare_e_string()
res2= (*b)->val_str(&value2);
if (!res1 || !res2)
return MY_TEST(res1 == res2);
- return MY_TEST(sortcmp(res1, res2, cmp_collation.collation) == 0);
-}
-
-
-int Arg_comparator::compare_e_binary_string()
-{
- String *res1,*res2;
- res1= (*a)->val_str(&value1);
- res2= (*b)->val_str(&value2);
- if (!res1 || !res2)
- return MY_TEST(res1 == res2);
- return MY_TEST(stringcmp(res1, res2) == 0);
+ return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0);
}
@@ -1345,9 +1128,13 @@ int Arg_comparator::compare_row()
case Item_func::GT_FUNC:
case Item_func::GE_FUNC:
return -1; // <, <=, > and >= always fail on NULL
- default: // EQ_FUNC
- if (((Item_bool_func2*)owner)->abort_on_null)
+ case Item_func::EQ_FUNC:
+ if (((Item_func_eq*)owner)->abort_on_null)
return -1; // We do not need correct NULL returning
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
}
was_null= 1;
owner->null_value= 0;
@@ -1492,7 +1279,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
args[0]= ((Item_in_subselect *)args[1])->left_expr;
}
if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) ||
- (!cache && !(cache= Item_cache::get_cache(*ref0))))
+ (!cache && !(cache= Item_cache::get_cache(thd, *ref0))))
DBUG_RETURN(1);
/*
During fix_field() expression could be substituted.
@@ -1502,7 +1289,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
args[0]= (*ref0);
DBUG_PRINT("info", ("actual fix fields"));
- cache->setup(args[0]);
+ cache->setup(thd, args[0]);
if (cache->cols() == 1)
{
DBUG_ASSERT(args[0]->type() != ROW_ITEM);
@@ -1551,9 +1338,8 @@ bool Item_in_optimizer::fix_left(THD *thd)
if (args[1]->fixed)
{
/* to avoid overriding is called to update left expression */
- used_tables_cache|= args[1]->used_tables();
+ used_tables_and_const_cache_join(args[1]);
with_sum_func= with_sum_func || args[1]->with_sum_func;
- const_item_cache= const_item_cache && args[1]->const_item();
}
DBUG_RETURN(0);
}
@@ -1591,8 +1377,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
with_subselect= 1;
with_sum_func= with_sum_func || args[1]->with_sum_func;
with_field= with_field || args[1]->with_field;
- used_tables_cache|= args[1]->used_tables();
- const_item_cache&= args[1]->const_item();
+ used_tables_and_const_cache_join(args[1]);
fixed= 1;
return FALSE;
}
@@ -1630,7 +1415,7 @@ bool Item_in_optimizer::invisible_mode()
@details
The function checks whether an expression cache is needed for this item
and if if so wraps the item into an item of the class
- Item_exp_cache_wrapper with an appropriate expression cache set up there.
+ Item_cache_wrapper with an appropriate expression cache set up there.
@note
used from Item::transform()
@@ -1640,9 +1425,8 @@ bool Item_in_optimizer::invisible_mode()
this item - otherwise
*/
-Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg)
+Item *Item_in_optimizer::expr_cache_insert_transformer(THD *thd, uchar *unused)
{
- THD *thd= (THD*) thd_arg;
DBUG_ENTER("Item_in_optimizer::expr_cache_insert_transformer");
if (invisible_mode())
@@ -1900,16 +1684,16 @@ bool Item_in_optimizer::is_null()
@retval NULL if an error occurred
*/
-Item *Item_in_optimizer::transform(Item_transformer transformer,
+Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
uchar *argument)
{
Item *new_item;
- DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
+ DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT(arg_count == 2);
/* Transform the left IN operand. */
- new_item= (*args)->transform(transformer, argument);
+ new_item= (*args)->transform(thd, transformer, argument);
if (!new_item)
return 0;
/*
@@ -1919,16 +1703,16 @@ Item *Item_in_optimizer::transform(Item_transformer transformer,
change records at each execution.
*/
if ((*args) != new_item)
- current_thd->change_item_tree(args, new_item);
+ thd->change_item_tree(args, new_item);
if (invisible_mode())
{
/* MAX/MIN transformed => pass through */
- new_item= args[1]->transform(transformer, argument);
+ new_item= args[1]->transform(thd, transformer, argument);
if (!new_item)
return 0;
if (args[1] != new_item)
- current_thd->change_item_tree(args + 1, new_item);
+ thd->change_item_tree(args + 1, new_item);
}
else
{
@@ -1948,9 +1732,9 @@ Item *Item_in_optimizer::transform(Item_transformer transformer,
Item_subselect::ANY_SUBS));
Item_in_subselect *in_arg= (Item_in_subselect*)args[1];
- current_thd->change_item_tree(&in_arg->left_expr, args[0]);
+ thd->change_item_tree(&in_arg->left_expr, args[0]);
}
- return (this->*transformer)(argument);
+ return (this->*transformer)(thd, argument);
}
@@ -1979,7 +1763,7 @@ longlong Item_func_eq::val_int()
void Item_func_equal::fix_length_and_dec()
{
- Item_bool_func2::fix_length_and_dec();
+ Item_bool_rowready_func2::fix_length_and_dec();
maybe_null=null_value=0;
}
@@ -2052,7 +1836,7 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const
if (item->type() != FUNC_ITEM)
return 0;
Item_func *item_func=(Item_func*) item;
- if (arg_count != item_func->arg_count ||
+ if (arg_count != item_func->argument_count() ||
functype() != item_func->functype())
return 0;
if (negated != ((Item_func_opt_neg *) item_func)->negated)
@@ -2121,11 +1905,10 @@ void Item_func_interval::fix_length_and_dec()
}
maybe_null= 0;
max_length= 2;
- used_tables_cache|= row->used_tables();
+ used_tables_and_const_cache_join(row);
not_null_tables_cache= row->not_null_tables();
with_sum_func= with_sum_func || row->with_sum_func;
with_field= with_field || row->with_field;
- const_item_cache&= row->const_item();
}
@@ -2250,17 +2033,6 @@ longlong Item_func_interval::val_int()
1 got error
*/
-bool Item_func_between::fix_fields(THD *thd, Item **ref)
-{
- if (Item_func_opt_neg::fix_fields(thd, ref))
- return 1;
-
- thd->lex->current_select->between_count++;
-
-
- return 0;
-}
-
bool Item_func_between::eval_not_null_tables(uchar *opt_arg)
{
@@ -2308,9 +2080,10 @@ void Item_func_between::fix_length_and_dec()
*/
if (!args[0] || !args[1] || !args[2])
return;
- if ( agg_cmp_type(&cmp_type, args, 3))
+ if (agg_cmp_type(&m_compare_type, args, 3))
return;
- if (cmp_type == STRING_RESULT &&
+
+ if (m_compare_type == STRING_RESULT &&
agg_arg_charsets_for_comparison(cmp_collation, args, 3))
return;
@@ -2322,7 +2095,7 @@ void Item_func_between::fix_length_and_dec()
For this to work, we need to know what date/time type we compare
strings as.
*/
- if (cmp_type == TIME_RESULT)
+ if (m_compare_type == TIME_RESULT)
compare_as_dates= find_date_time_item(args, 3, 0);
/* See the comment about the similar block in Item_bool_func2 */
@@ -2336,7 +2109,7 @@ void 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)
- cmp_type=INT_RESULT; // Works for all types.
+ m_compare_type= INT_RESULT; // Works for all types.
}
}
}
@@ -2346,7 +2119,7 @@ longlong Item_func_between::val_int()
{
DBUG_ASSERT(fixed == 1);
- switch (cmp_type) {
+ switch (m_compare_type) {
case TIME_RESULT:
{
THD *thd= current_thd;
@@ -2355,8 +2128,8 @@ longlong Item_func_between::val_int()
bool value_is_null, a_is_null, b_is_null;
ptr= &args[0];
- value= get_datetime_value(thd, &ptr, &cache, compare_as_dates,
- &value_is_null);
+ enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates);
+ value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null);
if (ptr != &args[0])
thd->change_item_tree(&args[0], *ptr);
@@ -2364,12 +2137,12 @@ longlong Item_func_between::val_int()
return 0;
ptr= &args[1];
- a= get_datetime_value(thd, &ptr, &cache, compare_as_dates, &a_is_null);
+ a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null);
if (ptr != &args[1])
thd->change_item_tree(&args[1], *ptr);
ptr= &args[2];
- b= get_datetime_value(thd, &ptr, &cache, compare_as_dates, &b_is_null);
+ b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null);
if (ptr != &args[2])
thd->change_item_tree(&args[2], *ptr);
@@ -2472,7 +2245,6 @@ longlong Item_func_between::val_int()
break;
}
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
null_value= 1;
return 0;
@@ -2494,50 +2266,8 @@ void Item_func_between::print(String *str, enum_query_type query_type)
str->append(')');
}
-void
-Item_func_ifnull::fix_length_and_dec()
-{
- uint32 char_length;
- agg_result_type(&cached_result_type, args, 2);
- cached_field_type= agg_field_type(args, 2);
- maybe_null=args[1]->maybe_null;
- decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
- unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag;
-
- if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT)
- {
- int len0= args[0]->max_char_length() - args[0]->decimals
- - (args[0]->unsigned_flag ? 0 : 1);
-
- int len1= args[1]->max_char_length() - args[1]->decimals
- - (args[1]->unsigned_flag ? 0 : 1);
-
- char_length= MY_MAX(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
- }
- else
- char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
-
- switch (cached_result_type) {
- case STRING_RESULT:
- if (count_string_result_length(cached_field_type, args, arg_count))
- return;
- break;
- case DECIMAL_RESULT:
- case REAL_RESULT:
- break;
- case INT_RESULT:
- decimals= 0;
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- case IMPOSSIBLE_RESULT:
- DBUG_ASSERT(0);
- }
- fix_char_length(char_length);
-}
-
-uint Item_func_ifnull::decimal_precision() const
+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();
@@ -2547,11 +2277,6 @@ uint Item_func_ifnull::decimal_precision() const
}
-Field *Item_func_ifnull::tmp_table_field(TABLE *table)
-{
- return tmp_table_field_from_field_type(table, 0);
-}
-
double
Item_func_ifnull::real_op()
{
@@ -2692,13 +2417,9 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
void Item_func_if::cache_type_info(Item *source)
{
- collation.set(source->collation);
- cached_field_type= source->field_type();
- cached_result_type= source->result_type();
- decimals= source->decimals;
- max_length= source->max_length;
+ Type_std_attributes::set(source);
+ set_handler_by_field_type(source->field_type());
maybe_null= source->maybe_null;
- unsigned_flag= source->unsigned_flag;
}
@@ -2712,7 +2433,7 @@ Item_func_if::fix_length_and_dec()
maybe_null= true;
// If both arguments are NULL, make resulting type BINARY(0).
if (args[2]->type() == NULL_ITEM)
- cached_field_type= MYSQL_TYPE_STRING;
+ set_handler_by_field_type(MYSQL_TYPE_STRING);
return;
}
if (args[2]->type() == NULL_ITEM)
@@ -2721,47 +2442,7 @@ Item_func_if::fix_length_and_dec()
maybe_null= true;
return;
}
-
- agg_result_type(&cached_result_type, args + 1, 2);
- cached_field_type= agg_field_type(args + 1, 2);
- maybe_null= args[1]->maybe_null || args[2]->maybe_null;
- decimals= MY_MAX(args[1]->decimals, args[2]->decimals);
- unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
-
- if (cached_result_type == STRING_RESULT)
- {
- count_string_result_length(cached_field_type, args + 1, 2);
- return;
- }
- else
- {
- collation.set_numeric(); // Number
- }
-
- uint32 char_length;
- if ((cached_result_type == DECIMAL_RESULT )
- || (cached_result_type == INT_RESULT))
- {
- int len1= args[1]->max_length - args[1]->decimals
- - (args[1]->unsigned_flag ? 0 : 1);
-
- int len2= args[2]->max_length - args[2]->decimals
- - (args[2]->unsigned_flag ? 0 : 1);
-
- char_length= MY_MAX(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
- }
- else
- char_length= MY_MAX(args[1]->max_char_length(), args[2]->max_char_length());
- fix_char_length(char_length);
-}
-
-
-uint Item_func_if::decimal_precision() const
-{
- int arg1_prec= args[1]->decimal_int_part();
- int arg2_prec= args[2]->decimal_int_part();
- int precision=MY_MAX(arg1_prec,arg2_prec) + decimals;
- return MY_MIN(precision, DECIMAL_MAX_PRECISION);
+ Item_func_case_abbreviation2::fix_length_and_dec2(args + 1);
}
@@ -2819,24 +2500,336 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
}
+void Item_func_nullif::split_sum_func(THD *thd, Item **ref_pointer_array,
+ List<Item> &fields, uint flags)
+{
+ if (m_cache)
+ {
+ flags|= SPLIT_SUM_SKIP_REGISTERED; // See Item_func::split_sum_func
+ m_cache->split_sum_func2_example(thd, ref_pointer_array, fields, flags);
+ args[1]->split_sum_func2(thd, ref_pointer_array, fields, &args[1], flags);
+ }
+ else
+ {
+ Item_func::split_sum_func(thd, ref_pointer_array, fields, flags);
+ }
+}
+
+
+bool Item_func_nullif::walk(Item_processor processor,
+ bool walk_subquery, uchar *arg)
+{
+ /*
+ No needs to iterate through args[2] when it's just a copy of args[0].
+ See MDEV-9712 Performance degradation of nested NULLIF
+ */
+ uint tmp_count= arg_count == 2 || args[0] == args[2] ? 2 : 3;
+ for (uint i= 0; i < tmp_count; i++)
+ {
+ if (args[i]->walk(processor, walk_subquery, arg))
+ return true;
+ }
+ return (this->*processor)(arg);
+}
+
+
+void Item_func_nullif::update_used_tables()
+{
+ if (m_cache)
+ {
+ used_tables_and_const_cache_init();
+ used_tables_and_const_cache_update_and_join(m_cache->get_example());
+ used_tables_and_const_cache_update_and_join(arg_count, args);
+ }
+ else
+ {
+ /*
+ MDEV-9712 Performance degradation of nested NULLIF
+ No needs to iterate through args[2] when it's just a copy of args[0].
+ */
+ DBUG_ASSERT(arg_count == 3);
+ used_tables_and_const_cache_init();
+ used_tables_and_const_cache_update_and_join(args[0] == args[2] ? 2 : 3,
+ args);
+ }
+}
+
+
+
void
Item_func_nullif::fix_length_and_dec()
{
- Item_bool_func2::fix_length_and_dec();
+ /*
+ If this is the first invocation of fix_length_and_dec(), create the
+ third argument as a copy of the first. This cannot be done before
+ fix_fields(), because fix_fields() might replace items,
+ for exampe NOT x --> x==0, or (SELECT 1) --> 1.
+ See also class Item_func_nullif declaration.
+ */
+ if (arg_count == 2)
+ args[arg_count++]= m_arg0 ? m_arg0 : args[0];
+
+ THD *thd= current_thd;
+ /*
+ At prepared statement EXECUTE time, args[0] can already
+ point to a different Item, created during PREPARE time fix_length_and_dec().
+ For example, if character set conversion was needed, arguments can look
+ like this:
+
+ args[0]= > Item_func_conv_charset \
+ l_expr
+ args[2]= >------------------------/
+
+ Otherwise (during PREPARE or convensional execution),
+ args[0] and args[2] should still point to the same original l_expr.
+ */
+ DBUG_ASSERT(args[0] == args[2] || thd->stmt_arena->is_stmt_execute());
+ if (args[0]->type() == SUM_FUNC_ITEM &&
+ !thd->lex->is_ps_or_view_context_analysis())
+ {
+ /*
+ NULLIF(l_expr, r_expr)
+
+ is calculated in the way to return a result equal to:
+
+ CASE WHEN l_expr = r_expr THEN NULL ELSE r_expr END.
+
+ There's nothing special with r_expr, because it's referenced
+ only by args[1] and nothing else.
+
+ l_expr needs a special treatment, as it's referenced by both
+ args[0] and args[2] initially.
+
+ args[2] is used to return the value. Afrer all transformations
+ (e.g. in fix_length_and_dec(), equal field propagation, etc)
+ args[2] points to a an Item which preserves the exact data type and
+ attributes (e.g. collation) of the original l_expr.
+ It can point:
+ - to the original l_expr
+ - to an Item_cache pointing to l_expr
+ - to a constant of the same data type with l_expr.
+
+ args[0] is used for comparison. It can be replaced:
+
+ - to Item_func_conv_charset by character set aggregation routines
+ - to a constant Item by equal field propagation routines
+ (in case of Item_field)
+
+ The data type and/or the attributes of args[0] can differ from
+ the data type and the attributes of the original l_expr, to make
+ it comparable to args[1] (which points to r_expr or its replacement).
+
+ For aggregate functions we have to wrap the original args[0]/args[2]
+ into Item_cache (see MDEV-9181). In this case the Item_cache
+ instance becomes the subject to character set conversion instead of
+ the original args[0]/args[2], while the original args[0]/args[2] get
+ hidden inside the cache.
+
+ Some examples of what NULLIF can end up with after argument
+ substitution (we don't mention args[1] in some cases for simplicity):
+
+ 1. l_expr is not an aggragate function:
+
+ a. No conversion happened.
+ args[0] and args[2] were not replaced to something else
+ (i.e. neither by character set conversion, nor by propagation):
+
+ args[1] > r_expr
+ args[0] \
+ l_expr
+ args[2] /
+
+ b. Conversion of args[0] happened:
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10) CHARACTER SET latin1,
+ b CHAR(10) CHARACTER SET utf8);
+ SELECT * FROM t1 WHERE NULLIF(a,b);
+
+ args[1] > r_expr (Item_field for t1.b)
+ args[0] > Item_func_conv_charset\
+ l_expr (Item_field for t1.a)
+ args[2] > ----------------------/
+
+ c. Conversion of args[1] happened:
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10) CHARACTER SET utf8,
+ b CHAR(10) CHARACTER SET latin1);
+ SELECT * FROM t1 WHERE NULLIF(a,b);
+
+ args[1] > Item_func_conv_charset -> r_expr (Item_field for t1.b)
+ args[0] \
+ l_expr (Item_field for t1.a)
+ args[2] /
+
+ d. Conversion of only args[0] happened (by equal field proparation):
+
+ CREATE OR REPLACE TABLE t1 (
+ a CHAR(10),
+ b CHAR(10));
+ SELECT * FROM t1 WHERE NULLIF(a,b) AND a='a';
+
+ args[1] > r_expr (Item_field for t1.b)
+ args[0] > Item_string('a') (constant replacement for t1.a)
+ args[2] > l_expr (Item_field for t1.a)
+
+ e. Conversion of both args[0] and args[2] happened
+ (by equal field propagation):
+
+ CREATE OR REPLACE TABLE t1 (a INT,b INT);
+ SELECT * FROM t1 WHERE NULLIF(a,b) AND a=5;
+
+ args[1] > r_expr (Item_field for "b")
+ args[0] \
+ Item_int (5) (constant replacement for "a")
+ args[2] /
+
+ 2. In case if l_expr is an aggregate function:
+
+ a. No conversion happened:
+
+ args[0] \
+ Item_cache > l_expr
+ args[2] /
+
+ b. Conversion of args[0] happened:
+
+ args[0] > Item_func_conv_charset \
+ Item_cache > l_expr
+ args[2] >------------------------/
+
+ c. Conversion of both args[0] and args[2] happened.
+ (e.g. by equal expression propagation)
+ TODO: check if it's possible (and add an example query if so).
+ */
+ 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]);
+ m_cache->setup(thd, args[0]);
+ m_cache->store(args[0]);
+ m_cache->set_used_tables(args[0]->used_tables());
+ 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());
+ collation.set(args[2]->collation);
+ decimals= args[2]->decimals;
+ unsigned_flag= args[2]->unsigned_flag;
+ fix_char_length(args[2]->max_char_length());
maybe_null=1;
- if (args[0]) // Only false if EOM
+ m_arg0= args[0];
+ setup_args_and_comparator(thd, &cmp);
+ /*
+ A special code for EXECUTE..PREPARE.
+
+ If args[0] did not change, then we don't remember it, as it can point
+ to a temporary Item object which will be destroyed between PREPARE
+ and EXECUTE. EXECUTE time fix_length_and_dec() will correctly set args[2]
+ from args[0] again.
+
+ If args[0] changed, then it can be Item_func_conv_charset() for the
+ original args[0], which was permanently installed during PREPARE time
+ into the item tree as a wrapper for args[0], using change_item_tree(), i.e.
+
+ NULLIF(latin1_field, 'a' COLLATE utf8_bin)
+
+ was "rewritten" to:
+
+ CASE WHEN CONVERT(latin1_field USING utf8) = 'a' COLLATE utf8_bin
+ THEN NULL
+ ELSE latin1_field
+
+ - m_args0 points to Item_field corresponding to latin1_field
+ - args[0] points to Item_func_conv_charset
+ - args[0]->args[0] is equal to m_args0
+ - args[1] points to Item_func_set_collation
+ - args[2] points is eqial to m_args0
+
+ In this case we remember and reuse m_arg0 during EXECUTE time as args[2].
+
+ QQ: How to make sure that m_args0 does not point
+ to something temporary which will be destoyed between PREPARE and EXECUTE.
+ The condition below should probably be more strict and somehow check that:
+ - change_item_tree() was called for the new args[0]
+ - m_args0 is referenced from inside args[0], e.g. as a function argument,
+ and therefore it is also something that won't be destroyed between
+ PREPARE and EXECUTE.
+ Any ideas?
+ */
+ if (args[0] == m_arg0)
+ m_arg0= NULL;
+}
+
+
+void Item_func_nullif::print(String *str, enum_query_type query_type)
+{
+ /*
+ NULLIF(a,b) is implemented according to the SQL standard as a short for
+ CASE WHEN a=b THEN NULL ELSE a END
+
+ The constructor of Item_func_nullif sets args[0] and args[2] to the
+ same item "a", and sets args[1] to "b".
+
+ If "this" is a part of a WHERE or ON condition, then:
+ - the left "a" is a subject to equal field propagation with ANY_SUBST.
+ - the right "a" is a subject to equal field propagation with IDENTITY_SUBST.
+ Therefore, after equal field propagation args[0] and args[2] can point
+ to different items.
+ */
+ if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) || args[0] == args[2])
{
- decimals=args[0]->decimals;
- unsigned_flag= args[0]->unsigned_flag;
- cached_result_type= args[0]->result_type();
- if (cached_result_type == STRING_RESULT &&
- agg_arg_charsets_for_comparison(collation, args, arg_count))
- return;
- fix_char_length(args[0]->max_char_length());
+ /*
+ If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested,
+ that means we want the original NULLIF() representation,
+ e.g. when we are in:
+ SHOW CREATE {VIEW|FUNCTION|PROCEDURE}
+
+ The original representation is possible only if
+ args[0] and args[2] still point to the same Item.
+
+ The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF
+ if an expression has undergone some optimization
+ (e.g. equal field propagation done in optimize_cond()) already and
+ NULLIF() potentially has two different representations of "a":
+ - one "a" for comparison
+ - another "a" for the returned value!
+ */
+ DBUG_ASSERT(args[0] == args[2] || current_thd->lex->context_analysis_only);
+ str->append(func_name());
+ str->append('(');
+ args[2]->print(str, query_type);
+ str->append(',');
+ args[1]->print(str, query_type);
+ str->append(')');
+ }
+ else
+ {
+ /*
+ args[0] and args[2] are different items.
+ This is possible after WHERE optimization (equal fields propagation etc),
+ e.g. in EXPLAIN EXTENDED or EXPLAIN FORMAT=JSON.
+ As it's not possible to print as a function with 2 arguments any more,
+ do it in the CASE style.
+ */
+ str->append(STRING_WITH_LEN("(case when "));
+ args[0]->print(str, query_type);
+ str->append(STRING_WITH_LEN(" = "));
+ args[1]->print(str, query_type);
+ str->append(STRING_WITH_LEN(" then NULL else "));
+ args[2]->print(str, query_type);
+ str->append(STRING_WITH_LEN(" end)"));
}
}
+int Item_func_nullif::compare()
+{
+ if (m_cache)
+ m_cache->cache_value();
+ return cmp.compare();
+}
+
/**
@note
Note that we have to evaluate the first argument twice as the compare
@@ -2848,74 +2841,104 @@ Item_func_nullif::fix_length_and_dec()
*/
double
-Item_func_nullif::val_real()
+Item_func_nullif::real_op()
{
DBUG_ASSERT(fixed == 1);
double value;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0.0;
}
- value= args[0]->val_real();
- null_value=args[0]->null_value;
+ value= args[2]->val_real();
+ null_value= args[2]->null_value;
return value;
}
longlong
-Item_func_nullif::val_int()
+Item_func_nullif::int_op()
{
DBUG_ASSERT(fixed == 1);
longlong value;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
}
- value=args[0]->val_int();
- null_value=args[0]->null_value;
+ value= args[2]->val_int();
+ null_value= args[2]->null_value;
return value;
}
String *
-Item_func_nullif::val_str(String *str)
+Item_func_nullif::str_op(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
}
- res=args[0]->val_str(str);
- null_value=args[0]->null_value;
+ res= args[2]->val_str(str);
+ null_value= args[2]->null_value;
return res;
}
my_decimal *
-Item_func_nullif::val_decimal(my_decimal * decimal_value)
+Item_func_nullif::decimal_op(my_decimal * decimal_value)
{
DBUG_ASSERT(fixed == 1);
my_decimal *res;
- if (!cmp.compare())
+ if (!compare())
{
null_value=1;
return 0;
}
- res= args[0]->val_decimal(decimal_value);
- null_value= args[0]->null_value;
+ res= args[2]->val_decimal(decimal_value);
+ null_value= args[2]->null_value;
return res;
}
bool
+Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (!compare())
+ return (null_value= true);
+ return (null_value= args[2]->get_date(ltime, fuzzydate));
+}
+
+
+bool
Item_func_nullif::is_null()
{
- return (null_value= (!cmp.compare() ? 1 : args[0]->null_value));
+ return (null_value= (!compare() ? 1 : args[2]->null_value));
}
+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)
+{
+ ncases= list.elements;
+ if (first_expr_arg)
+ {
+ first_expr_num= list.elements;
+ list.push_back(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);
+ 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.
@@ -2958,7 +2981,7 @@ Item *Item_func_case::find_item(String *str)
{
if (args[i]->real_item()->type() == NULL_ITEM)
continue;
- cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
+ 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)))
@@ -2968,7 +2991,7 @@ Item *Item_func_case::find_item(String *str)
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]) && !args[i]->null_value)
+ if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE)
return args[i + 1];
}
}
@@ -3071,6 +3094,10 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
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];
+
+ if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(ncases+1))))
+ return TRUE;
+
bool res= Item_func::fix_fields(thd, ref);
/*
Call check_stack_overrun after fix_fields to be sure that stack variable
@@ -3106,14 +3133,11 @@ static void change_item_tree_if_needed(THD *thd,
void Item_func_case::fix_length_and_dec()
{
- Item **agg;
+ Item **agg= arg_buffer;
uint nagg;
- uint found_types= 0;
THD *thd= current_thd;
- if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1))))
- return;
-
+ m_found_types= 0;
if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
maybe_null= 1;
@@ -3128,12 +3152,11 @@ void Item_func_case::fix_length_and_dec()
if (else_expr_num != -1)
agg[nagg++]= args[else_expr_num];
- agg_result_type(&cached_result_type, agg, nagg);
- cached_field_type= agg_field_type(agg, nagg);
+ set_handler_by_field_type(agg_field_type(agg, nagg, true));
- if (cached_result_type == STRING_RESULT)
+ if (Item_func_case::result_type() == STRING_RESULT)
{
- if (count_string_result_length(cached_field_type, agg, nagg))
+ if (count_string_result_length(Item_func_case::field_type(), agg, nagg))
return;
/*
Copy all THEN and ELSE items back to args[] array.
@@ -3158,7 +3181,7 @@ void Item_func_case::fix_length_and_dec()
{
uint i;
agg[0]= args[first_expr_num];
- left_result_type= agg[0]->cmp_type();
+ left_cmp_type= agg[0]->cmp_type();
/*
As the first expression and WHEN expressions
@@ -3169,14 +3192,14 @@ void Item_func_case::fix_length_and_dec()
for (nagg= 0; nagg < ncases/2 ; nagg++)
agg[nagg+1]= args[nagg*2];
nagg++;
- if (!(found_types= collect_cmp_types(agg, nagg)))
+ if (!(m_found_types= collect_cmp_types(agg, nagg)))
return;
Item *date_arg= 0;
- if (found_types & (1U << TIME_RESULT))
+ if (m_found_types & (1U << TIME_RESULT))
date_arg= find_date_time_item(args, arg_count, 0);
- if (found_types & (1U << STRING_RESULT))
+ if (m_found_types & (1U << STRING_RESULT))
{
/*
If we'll do string comparison, we also need to aggregate
@@ -3217,7 +3240,7 @@ void Item_func_case::fix_length_and_dec()
for (i= 0; i <= (uint)TIME_RESULT; i++)
{
- if (found_types & (1U << i) && !cmp_items[i])
+ if (m_found_types & (1U << i) && !cmp_items[i])
{
DBUG_ASSERT((Item_result)i != ROW_RESULT);
@@ -3227,16 +3250,90 @@ void Item_func_case::fix_length_and_dec()
return;
}
}
+ }
+}
+
+
+Item* Item_func_case::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;
+ }
+
+ for (uint i= 0; i < arg_count; i++)
+ {
/*
- Set cmp_context of all WHEN arguments. This prevents
- Item_field::equal_fields_propagator() from transforming a
- zerofill argument into a string constant. Such a change would
- require rebuilding cmp_items.
+ Even "i" values cover items that are in a comparison context:
+ CASE x0 WHEN x1 .. WHEN x2 .. WHEN x3 ..
+ Odd "i" values cover items that are not in comparison:
+ CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END
*/
- for (i= 0; i < ncases; i+= 2)
- args[i]->cmp_context= item_cmp_type(left_result_type,
- args[i]->result_type());
+ Item *new_item= 0;
+ if ((int) i == first_expr_num) // Then CASE (the switch) argument
+ {
+ /*
+ 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().
+
+ - Example: multiple comparison types, can't propagate:
+ WHERE CASE str_column
+ WHEN 'string' THEN TRUE
+ WHEN 1 THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single incompatible comparison type, can't propagate:
+ WHERE CASE str_column
+ WHEN DATE'2001-01-01' THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single incompatible comparison type, can't propagate:
+ WHERE CASE str_column
+ WHEN 1 THEN TRUE
+ ELSE FALSE END;
+
+ - Example: a single compatible comparison type, ok to propagate:
+ WHERE CASE str_column
+ WHEN 'str1' THEN TRUE
+ WHEN 'str2' THEN TRUE
+ ELSE FALSE END;
+ */
+ if (m_found_types == (1UL << left_cmp_type))
+ new_item= args[i]->propagate_equal_fields(thd,
+ Context(
+ ANY_SUBST,
+ left_cmp_type,
+ cmp_collation.collation),
+ cond);
+ }
+ else if ((i % 2) == 0) // WHEN arguments
+ {
+ /*
+ 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]);
+ new_item= args[i]->propagate_equal_fields(thd,
+ Context(
+ ANY_SUBST,
+ tmp_cmp_type,
+ cmp_collation.collation),
+ cond);
+ }
+ else // THEN and ELSE arguments (they are not in comparison)
+ {
+ new_item= args[i]->propagate_equal_fields(thd, Context_identity(), cond);
+ }
+ if (new_item && new_item != args[i])
+ thd->change_item_tree(&args[i], new_item);
}
+ return this;
}
@@ -3371,22 +3468,11 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
}
-void Item_func_coalesce::fix_length_and_dec()
-{
- cached_field_type= agg_field_type(args, arg_count);
- agg_result_type(&cached_result_type, args, arg_count);
- fix_attributes(args, arg_count);
-}
-
-
-#if MYSQL_VERSION_ID > 100100
-#error Rename this to Item_hybrid_func::fix_attributes() when mering to 10.1
-#endif
-void Item_func_hybrid_result_type::fix_attributes(Item **items, uint nitems)
+void Item_hybrid_func::fix_attributes(Item **items, uint nitems)
{
- switch (cached_result_type) {
+ switch (Item_hybrid_func::result_type()) {
case STRING_RESULT:
- if (count_string_result_length(field_type(),
+ if (count_string_result_length(Item_hybrid_func::field_type(),
items, nitems))
return;
break;
@@ -3405,7 +3491,6 @@ void Item_func_hybrid_result_type::fix_attributes(Item **items, uint nitems)
break;
case ROW_RESULT:
case TIME_RESULT:
- case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
}
}
@@ -3533,11 +3618,11 @@ static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b)
}
-int in_vector::find(Item *item)
+bool in_vector::find(Item *item)
{
uchar *result=get_value(item);
if (!result || !used_count)
- return 0; // Null value
+ return false; // Null value
uint start,end;
start=0; end=used_count-1;
@@ -3546,13 +3631,13 @@ int in_vector::find(Item *item)
uint mid=(start+end+1)/2;
int res;
if ((res=(*compare)(collation, base+mid*size, result)) == 0)
- return 1;
+ return true;
if (res < 0)
start=mid;
else
end=mid-1;
}
- return (int) ((*compare)(collation, base+start*size, result) == 0);
+ return ((*compare)(collation, base+start*size, result) == 0);
}
in_string::in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs)
@@ -3598,9 +3683,15 @@ uchar *in_string::get_value(Item *item)
return (uchar*) item->val_str(&tmp);
}
-in_row::in_row(uint elements, Item * item)
+Item *in_string::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_string_for_in_vector(thd, collation);
+}
+
+
+in_row::in_row(THD *thd, uint elements, Item * item)
{
- base= (char*) new cmp_item_row[count= elements];
+ base= (char*) new (thd->mem_root) cmp_item_row[count= elements];
size= sizeof(cmp_item_row);
compare= (qsort2_cmp) cmp_row;
/*
@@ -3629,7 +3720,7 @@ void in_row::set(uint pos, Item *item)
{
DBUG_ENTER("in_row::set");
DBUG_PRINT("enter", ("pos: %u item: 0x%lx", pos, (ulong) item));
- ((cmp_item_row*) base)[pos].store_value_by_template(&tmp, item);
+ ((cmp_item_row*) base)[pos].store_value_by_template(current_thd, &tmp, item);
DBUG_VOID_RETURN;
}
@@ -3654,13 +3745,21 @@ uchar *in_longlong::get_value(Item *item)
return (uchar*) &tmp;
}
+Item *in_longlong::create_item(THD *thd)
+{
+ /*
+ We're created a signed INT, this may not be correct in
+ general case (see BUG#19342).
+ */
+ return new (thd->mem_root) Item_int(thd, (longlong)0);
+}
+
+
void in_datetime::set(uint pos,Item *item)
{
- Item **tmp_item= &item;
- bool is_null;
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
- buff->val= get_datetime_value(0, &tmp_item, 0, warn_item, &is_null);
+ buff->val= item->val_temporal_packed(warn_item);
buff->unsigned_flag= 1L;
}
@@ -3668,13 +3767,21 @@ uchar *in_datetime::get_value(Item *item)
{
bool is_null;
Item **tmp_item= lval_cache ? &lval_cache : &item;
- tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null);
+ enum_field_types f_type=
+ tmp_item[0]->field_type_for_temporal_comparison(warn_item);
+ tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null);
if (item->null_value)
return 0;
tmp.unsigned_flag= 1L;
return (uchar*) &tmp;
}
+Item *in_datetime::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_datetime(thd);
+}
+
+
in_double::in_double(uint elements)
:in_vector(elements,sizeof(double),(qsort2_cmp) cmp_double, 0)
{}
@@ -3692,6 +3799,11 @@ uchar *in_double::get_value(Item *item)
return (uchar*) &tmp;
}
+Item *in_double::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_float(thd, 0.0, 0);
+}
+
in_decimal::in_decimal(uint elements)
:in_vector(elements, sizeof(my_decimal),(qsort2_cmp) cmp_decimal, 0)
@@ -3719,6 +3831,11 @@ uchar *in_decimal::get_value(Item *item)
return (uchar *)result;
}
+Item *in_decimal::create_item(THD *thd)
+{
+ return new (thd->mem_root) Item_decimal(thd, 0, FALSE);
+}
+
cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item,
CHARSET_INFO *cs)
@@ -3737,9 +3854,6 @@ cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item,
case TIME_RESULT:
DBUG_ASSERT(warn_item);
return new cmp_item_datetime(warn_item);
- case IMPOSSIBLE_RESULT:
- DBUG_ASSERT(0);
- break;
}
return 0; // to satisfy compiler :)
}
@@ -3816,7 +3930,7 @@ void cmp_item_row::store_value(Item *item)
}
-void cmp_item_row::store_value_by_template(cmp_item *t, Item *item)
+void cmp_item_row::store_value_by_template(THD *thd, cmp_item *t, Item *item)
{
cmp_item_row *tmpl= (cmp_item_row*) t;
if (tmpl->n != item->cols())
@@ -3825,7 +3939,7 @@ void cmp_item_row::store_value_by_template(cmp_item *t, Item *item)
return;
}
n= tmpl->n;
- if ((comparators= (cmp_item **) sql_alloc(sizeof(cmp_item *)*n)))
+ if ((comparators= (cmp_item **) thd->alloc(sizeof(cmp_item *)*n)))
{
item->bring_value();
item->null_value= 0;
@@ -3833,7 +3947,7 @@ void cmp_item_row::store_value_by_template(cmp_item *t, Item *item)
{
if (!(comparators[i]= tmpl->comparators[i]->make_same()))
break; // new failed
- comparators[i]->store_value_by_template(tmpl->comparators[i],
+ comparators[i]->store_value_by_template(thd, tmpl->comparators[i],
item->element_index(i));
item->null_value|= item->element_index(i)->null_value;
}
@@ -3853,14 +3967,20 @@ int cmp_item_row::cmp(Item *arg)
arg->bring_value();
for (uint i=0; i < n; i++)
{
- if (comparators[i]->cmp(arg->element_index(i)))
+ const int rc= comparators[i]->cmp(arg->element_index(i));
+ switch (rc)
{
- if (!arg->element_index(i)->null_value)
- return 1;
- was_null= 1;
+ case UNKNOWN:
+ was_null= true;
+ break;
+ case TRUE:
+ return TRUE;
+ case FALSE:
+ break; // elements #i are equal
}
+ arg->null_value|= arg->element_index(i)->null_value;
}
- return (arg->null_value= was_null);
+ return was_null ? UNKNOWN : FALSE;
}
@@ -3883,15 +4003,15 @@ void cmp_item_decimal::store_value(Item *item)
/* val may be zero if item is nnull */
if (val && val != &value)
my_decimal2decimal(val, &value);
+ m_null_value= item->null_value;
}
int cmp_item_decimal::cmp(Item *arg)
{
my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf);
- if (arg->null_value)
- return 1;
- return my_decimal_cmp(&value, tmp);
+ return (m_null_value || arg->null_value) ?
+ UNKNOWN : (my_decimal_cmp(&value, tmp) != 0);
}
@@ -3912,16 +4032,17 @@ void cmp_item_datetime::store_value(Item *item)
{
bool is_null;
Item **tmp_item= lval_cache ? &lval_cache : &item;
- value= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null);
+ enum_field_types f_type=
+ tmp_item[0]->field_type_for_temporal_comparison(warn_item);
+ value= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null);
+ m_null_value= item->null_value;
}
int cmp_item_datetime::cmp(Item *arg)
{
- bool is_null;
- Item **tmp_item= &arg;
- return value !=
- get_datetime_value(0, &tmp_item, 0, warn_item, &is_null);
+ const bool rc= value != arg->val_temporal_packed(warn_item);
+ return (m_null_value || arg->null_value) ? UNKNOWN : rc;
}
@@ -3938,10 +4059,17 @@ cmp_item *cmp_item_datetime::make_same()
}
-bool Item_func_in::nulls_in_row()
+bool Item_func_in::count_sargable_conds(uchar *arg)
+{
+ ((SELECT_LEX*) arg)->cond_count++;
+ return 0;
+}
+
+
+bool Item_func_in::list_contains_null()
{
Item **arg,**arg_end;
- for (arg= args+1, arg_end= args+arg_count; arg != arg_end ; arg++)
+ for (arg= args + 1, arg_end= args+arg_count; arg != arg_end ; arg++)
{
if ((*arg)->null_inside())
return 1;
@@ -4034,8 +4162,8 @@ void Item_func_in::fix_length_and_dec()
Item *date_arg= 0;
uint found_types= 0;
uint type_cnt= 0, i;
- Item_result cmp_type= STRING_RESULT;
- left_result_type= args[0]->cmp_type();
+ m_compare_type= STRING_RESULT;
+ left_cmp_type= args[0]->cmp_type();
if (!(found_types= collect_cmp_types(args, arg_count, true)))
return;
@@ -4052,30 +4180,56 @@ void Item_func_in::fix_length_and_dec()
if (found_types & (1U << i))
{
(type_cnt)++;
- cmp_type= (Item_result) i;
+ 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 (cmp_type == STRING_RESULT &&
+ if (m_compare_type == STRING_RESULT &&
agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
return;
arg_types_compatible= TRUE;
- if (cmp_type == ROW_RESULT)
+ if (m_compare_type == ROW_RESULT)
{
uint cols= args[0]->cols();
cmp_item_row *cmp= 0;
- if (const_itm && !nulls_in_row())
+ if (bisection_possible)
{
- array= new in_row(arg_count-1, 0);
+ array= new (thd->mem_root) in_row(thd, arg_count-1, 0);
cmp= &((in_row*)array)->tmp;
}
else
{
- if (!(cmp= new cmp_item_row))
+ if (!(cmp= new (thd->mem_root) cmp_item_row))
return;
cmp_items[ROW_RESULT]= cmp;
}
@@ -4092,16 +4246,13 @@ void Item_func_in::fix_length_and_dec()
cmp= ((in_row*)array)->tmp.comparators + col;
else
cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col;
- *cmp= new cmp_item_datetime(date_arg);
+ *cmp= new (thd->mem_root) cmp_item_datetime(date_arg);
}
}
}
}
- /*
- Row item with NULLs inside can return NULL or FALSE =>
- they can't be processed as static
- */
- if (type_cnt == 1 && const_itm && !nulls_in_row())
+
+ if (bisection_possible)
{
/*
IN must compare INT columns and constants as int values (the same
@@ -4113,7 +4264,7 @@ void Item_func_in::fix_length_and_dec()
See the comment about the similar block in Item_bool_func2
*/
if (args[0]->real_item()->type() == FIELD_ITEM &&
- !thd->lex->is_view_context_analysis() && cmp_type != INT_RESULT)
+ !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 ||
@@ -4126,19 +4277,19 @@ void Item_func_in::fix_length_and_dec()
all_converted= FALSE;
}
if (all_converted)
- cmp_type= INT_RESULT;
+ m_compare_type= INT_RESULT;
}
}
- switch (cmp_type) {
+ switch (m_compare_type) {
case STRING_RESULT:
- array=new in_string(arg_count-1,(qsort2_cmp) srtcmp_in,
- cmp_collation.collation);
+ array=new (thd->mem_root) in_string(arg_count-1,(qsort2_cmp) srtcmp_in,
+ cmp_collation.collation);
break;
case INT_RESULT:
- array= new in_longlong(arg_count-1);
+ array= new (thd->mem_root) in_longlong(arg_count-1);
break;
case REAL_RESULT:
- array= new in_double(arg_count-1);
+ array= new (thd->mem_root) in_double(arg_count-1);
break;
case ROW_RESULT:
/*
@@ -4149,30 +4300,32 @@ void Item_func_in::fix_length_and_dec()
((in_row*)array)->tmp.store_value(args[0]);
break;
case DECIMAL_RESULT:
- array= new in_decimal(arg_count - 1);
+ array= new (thd->mem_root) in_decimal(arg_count - 1);
break;
case TIME_RESULT:
date_arg= find_date_time_item(args, arg_count, 0);
- array= new in_datetime(date_arg, arg_count - 1);
- break;
- case IMPOSSIBLE_RESULT:
- DBUG_ASSERT(0);
+ array= new (thd->mem_root) in_datetime(date_arg, arg_count - 1);
break;
}
- if (array && !(thd->is_fatal_error)) // If not EOM
+ if (!array || thd->is_fatal_error) // OOM
+ return;
+ uint j=0;
+ for (uint i=1 ; i < arg_count ; i++)
{
- uint j=0;
- for (uint i=1 ; i < arg_count ; i++)
+ array->set(j,args[i]);
+ if (!args[i]->null_value)
+ j++; // include this cell in the array.
+ else
{
- array->set(j,args[i]);
- if (!args[i]->null_value) // Skip NULL values
- j++;
- else
- have_null= 1;
+ /*
+ We don't put NULL values in array, to avoid erronous matches in
+ bisection.
+ */
+ have_null= 1;
}
- if ((array->used_count= j))
- array->sort();
}
+ if ((array->used_count= j))
+ array->sort();
}
else
{
@@ -4192,16 +4345,6 @@ void Item_func_in::fix_length_and_dec()
}
}
}
- /*
- Set cmp_context of all arguments. This prevents
- Item_field::equal_fields_propagator() from transforming a zerofill integer
- argument into a string constant. Such a change would require rebuilding
- cmp_itmes.
- */
- for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
- {
- arg[0]->cmp_context= item_cmp_type(left_result_type, arg[0]->result_type());
- }
max_length= 1;
}
@@ -4250,7 +4393,14 @@ longlong Item_func_in::val_int()
uint value_added_map= 0;
if (array)
{
- int tmp=array->find(args[0]);
+ bool tmp=array->find(args[0]);
+ /*
+ NULL on left -> UNKNOWN.
+ Found no match, and NULL on right -> UNKNOWN.
+ NULL on right can never give a match, as it is not stored in
+ array.
+ See also the 'bisection_possible' variable in fix_length_and_dec().
+ */
null_value=args[0]->null_value || (!tmp && have_null);
return (longlong) (!null_value && tmp != negated);
}
@@ -4266,19 +4416,18 @@ longlong Item_func_in::val_int()
have_null= TRUE;
continue;
}
- Item_result cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
+ 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]);
- if ((null_value= args[0]->null_value))
- return 0;
value_added_map|= 1U << (uint)cmp_type;
}
- if (!in_item->cmp(args[i]) && !args[i]->null_value)
+ const int rc= in_item->cmp(args[i]);
+ if (rc == FALSE)
return (longlong) (!negated);
- have_null|= args[i]->null_value;
+ have_null|= (rc == UNKNOWN);
}
null_value= have_null;
@@ -4336,11 +4485,28 @@ Item_cond::Item_cond(THD *thd, Item_cond *item)
}
+Item_cond::Item_cond(THD *thd, Item *i1, Item *i2):
+ Item_bool_func(thd), abort_on_null(0)
+{
+ list.push_back(i1, thd->mem_root);
+ list.push_back(i2, thd->mem_root);
+}
+
+
+Item *Item_cond_and::copy_andor_structure(THD *thd)
+{
+ Item_cond_and *item;
+ if ((item= new (thd->mem_root) Item_cond_and(thd, this)))
+ item->copy_andor_arguments(thd, this);
+ return item;
+}
+
+
void Item_cond::copy_andor_arguments(THD *thd, Item_cond *item)
{
List_iterator_fast<Item> li(item->list);
while (Item *it= li++)
- list.push_back(it->copy_andor_structure(thd));
+ list.push_back(it->copy_andor_structure(thd), thd->mem_root);
}
@@ -4351,8 +4517,8 @@ Item_cond::fix_fields(THD *thd, Item **ref)
List_iterator<Item> li(list);
Item *item;
uchar buff[sizeof(char*)]; // Max local vars in function
- not_null_tables_cache= used_tables_cache= 0;
- const_item_cache= 1;
+ not_null_tables_cache= 0;
+ used_tables_and_const_cache_init();
/*
and_table_cache is the value that Item_cond_or() returns for
@@ -4395,12 +4561,13 @@ Item_cond::fix_fields(THD *thd, Item **ref)
was: <field>
become: <field> = 1
*/
- if (item->type() == FIELD_ITEM)
+ Item::Type type= item->type();
+ if (type == Item::FIELD_ITEM || type == Item::REF_ITEM)
{
Query_arena backup, *arena;
Item *new_item;
arena= thd->activate_stmt_arena_if_needed(&backup);
- if ((new_item= new Item_func_ne(item, new Item_int(0, 1))))
+ if ((new_item= new (thd->mem_root) Item_func_ne(thd, item, new (thd->mem_root) Item_int(thd, 0, 1))))
li.replace(item= new_item);
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -4448,7 +4615,6 @@ Item_cond::fix_fields(THD *thd, Item **ref)
if (item->maybe_null)
maybe_null=1;
}
- thd->lex->current_select->cond_count+= list.elements;
fix_length_and_dec();
fixed= 1;
return FALSE;
@@ -4502,8 +4668,7 @@ void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref)
List_iterator<Item> li(list);
Item *item;
- used_tables_cache=0;
- const_item_cache=1;
+ used_tables_and_const_cache_init();
and_tables_cache= ~(table_map) 0; // Here and below we do as fix_fields does
not_null_tables_cache= 0;
@@ -4513,8 +4678,7 @@ void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref)
table_map tmp_table_map;
item->fix_after_pullout(new_parent, li.ref());
item= *li.ref();
- used_tables_cache|= item->used_tables();
- const_item_cache&= item->const_item();
+ used_tables_and_const_cache_join(item);
if (item->const_item())
and_tables_cache= (table_map) 0;
@@ -4568,15 +4732,15 @@ bool Item_cond_and::walk_top_and(Item_processor processor, uchar *arg)
Item returned as the result of transformation of the root node
*/
-Item *Item_cond::transform(Item_transformer transformer, uchar *arg)
+Item *Item_cond::transform(THD *thd, Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
+ DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
List_iterator<Item> li(list);
Item *item;
while ((item= li++))
{
- Item *new_item= item->transform(transformer, arg);
+ Item *new_item= item->transform(thd, transformer, arg);
if (!new_item)
return 0;
@@ -4587,9 +4751,9 @@ Item *Item_cond::transform(Item_transformer transformer, uchar *arg)
change records at each execution.
*/
if (new_item != item)
- current_thd->change_item_tree(li.ref(), new_item);
+ thd->change_item_tree(li.ref(), new_item);
}
- return Item_func::transform(transformer, arg);
+ return Item_func::transform(thd, transformer, arg);
}
@@ -4617,7 +4781,7 @@ Item *Item_cond::transform(Item_transformer transformer, uchar *arg)
Item returned as the result of transformation of the root node
*/
-Item *Item_cond::compile(Item_analyzer analyzer, uchar **arg_p,
+Item *Item_cond::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t)
{
if (!(this->*analyzer)(arg_p))
@@ -4632,11 +4796,34 @@ Item *Item_cond::compile(Item_analyzer analyzer, uchar **arg_p,
to analyze any argument of the condition formula.
*/
uchar *arg_v= *arg_p;
- Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t);
+ Item *new_item= item->compile(thd, analyzer, &arg_v, transformer, arg_t);
+ if (new_item && new_item != item)
+ thd->change_item_tree(li.ref(), new_item);
+ }
+ return Item_func::transform(thd, transformer, arg_t);
+}
+
+
+Item *Item_cond::propagate_equal_fields(THD *thd,
+ const Context &ctx,
+ COND_EQUAL *cond)
+{
+ DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
+ DBUG_ASSERT(arg_count == 0);
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item= li++))
+ {
+ /*
+ The exact value of the second parameter to propagate_equal_fields()
+ 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)
- current_thd->change_item_tree(li.ref(), new_item);
+ thd->change_item_tree(li.ref(), new_item);
}
- return Item_func::transform(transformer, arg_t);
+ return this;
}
void Item_cond::traverse_cond(Cond_traverser traverser,
@@ -4681,12 +4868,13 @@ void Item_cond::traverse_cond(Cond_traverser traverser,
*/
void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array,
- List<Item> &fields)
+ List<Item> &fields, uint flags)
{
List_iterator<Item> li(list);
Item *item;
while ((item= li++))
- item->split_sum_func2(thd, ref_pointer_array, fields, li.ref(), TRUE);
+ item->split_sum_func2(thd, ref_pointer_array, fields, li.ref(),
+ flags | SPLIT_SUM_SKIP_REGISTERED);
}
@@ -4697,22 +4885,6 @@ Item_cond::used_tables() const
}
-void Item_cond::update_used_tables()
-{
- List_iterator_fast<Item> li(list);
- Item *item;
-
- used_tables_cache=0;
- const_item_cache=1;
- while ((item=li++))
- {
- item->update_used_tables();
- used_tables_cache|= item->used_tables();
- const_item_cache&= item->const_item();
- }
-}
-
-
void Item_cond::print(String *str, enum_query_type query_type)
{
str->append('(');
@@ -4740,7 +4912,7 @@ void Item_cond::neg_arguments(THD *thd)
Item *new_item= item->neg_transformer(thd);
if (!new_item)
{
- if (!(new_item= new Item_func_not(item)))
+ if (!(new_item= new (thd->mem_root) Item_func_not(thd, item)))
return; // Fatal OEM error
}
(void) li.replace(new_item);
@@ -4758,7 +4930,6 @@ void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding)
}
}
-
/**
Evaluation of AND(expr, expr, expr ...).
@@ -4816,6 +4987,15 @@ longlong Item_cond_or::val_int()
return 0;
}
+Item *Item_cond_or::copy_andor_structure(THD *thd)
+{
+ Item_cond_or *item;
+ if ((item= new (thd->mem_root) Item_cond_or(thd, this)))
+ item->copy_andor_arguments(thd, this);
+ return item;
+}
+
+
/**
Create an AND expression from two expressions.
@@ -4836,21 +5016,21 @@ longlong Item_cond_or::val_int()
Item
*/
-Item *and_expressions(Item *a, Item *b, Item **org_item)
+Item *and_expressions(THD *thd, Item *a, Item *b, Item **org_item)
{
if (!a)
return (*org_item= (Item*) b);
if (a == *org_item)
{
Item_cond *res;
- if ((res= new Item_cond_and(a, (Item*) b)))
+ if ((res= new (thd->mem_root) Item_cond_and(thd, a, (Item*) b)))
{
res->used_tables_cache= a->used_tables() | b->used_tables();
res->not_null_tables_cache= a->not_null_tables() | b->not_null_tables();
}
return res;
}
- if (((Item_cond_and*) a)->add((Item*) b))
+ if (((Item_cond_and*) a)->add((Item*) b, thd->mem_root))
return 0;
((Item_cond_and*) a)->used_tables_cache|= b->used_tables();
((Item_cond_and*) a)->not_null_tables_cache|= b->not_null_tables();
@@ -4858,6 +5038,13 @@ Item *and_expressions(Item *a, Item *b, Item **org_item)
}
+bool Item_func_null_predicate::count_sargable_conds(uchar *arg)
+{
+ ((SELECT_LEX*) arg)->cond_count++;
+ return 0;
+}
+
+
longlong Item_func_isnull::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -4910,16 +5097,23 @@ void Item_func_isnotnull::print(String *str, enum_query_type query_type)
}
+bool Item_bool_func2::count_sargable_conds(uchar *arg)
+{
+ ((SELECT_LEX*) arg)->cond_count++;
+ return 0;
+}
+
+
longlong Item_func_like::val_int()
{
DBUG_ASSERT(fixed == 1);
- String* res = args[0]->val_str(&cmp.value1);
+ String* res= args[0]->val_str(&cmp_value1);
if (args[0]->null_value)
{
null_value=1;
return 0;
}
- String* res2 = args[1]->val_str(&cmp.value2);
+ String* res2= args[1]->val_str(&cmp_value2);
if (args[1]->null_value)
{
null_value=1;
@@ -4928,7 +5122,7 @@ longlong Item_func_like::val_int()
null_value=0;
if (canDoTurboBM)
return turboBM_matches(res->ptr(), res->length()) ? 1 : 0;
- return my_wildcmp(cmp.cmp_collation.collation,
+ return my_wildcmp(cmp_collation.collation,
res->ptr(),res->ptr()+res->length(),
res2->ptr(),res2->ptr()+res2->length(),
escape,wild_one,wild_many) ? 0 : 1;
@@ -4939,22 +5133,33 @@ longlong Item_func_like::val_int()
We can optimize a where if first character isn't a wildcard
*/
-Item_func::optimize_type Item_func_like::select_optimize() const
+bool Item_func_like::with_sargable_pattern() const
{
if (!args[1]->const_item() || args[1]->is_expensive())
- return OPTIMIZE_NONE;
+ return false;
- String* res2= args[1]->val_str((String *)&cmp.value2);
+ String* res2= args[1]->val_str((String *) &cmp_value2);
if (!res2)
- return OPTIMIZE_NONE;
+ return false;
if (!res2->length()) // Can optimize empty wildcard: column LIKE ''
- return OPTIMIZE_OP;
+ return true;
DBUG_ASSERT(res2->ptr());
char first= res2->ptr()[0];
- return (first == wild_many || first == wild_one) ?
- OPTIMIZE_NONE : OPTIMIZE_OP;
+ return first != wild_many && first != wild_one;
+}
+
+
+SEL_TREE *Item_func_like::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
+{
+ MEM_ROOT *tmp_root= param->mem_root;
+ param->thd->mem_root= param->old_root;
+ bool sargable_pattern= with_sargable_pattern();
+ param->thd->mem_root= tmp_root;
+ return sargable_pattern ?
+ Item_bool_func2::get_mm_tree(param, cond_ptr) :
+ Item_func::get_mm_tree(param, cond_ptr);
}
@@ -4974,7 +5179,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (escape_item->const_item())
{
/* If we are on execution stage */
- String *escape_str= escape_item->val_str(&cmp.value1);
+ String *escape_str= escape_item->val_str(&cmp_value1);
if (escape_str)
{
const char *escape_str_ptr= escape_str->ptr();
@@ -4987,7 +5192,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
return TRUE;
}
- if (use_mb(cmp.cmp_collation.collation))
+ if (use_mb(cmp_collation.collation))
{
CHARSET_INFO *cs= escape_str->charset();
my_wc_t wc;
@@ -5004,7 +5209,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
code instead of Unicode code as "escape" argument.
Convert to "cs" if charset of escape differs.
*/
- CHARSET_INFO *cs= cmp.cmp_collation.collation;
+ CHARSET_INFO *cs= cmp_collation.collation;
uint32 unused;
if (escape_str->needs_conversion(escape_str->length(),
escape_str->charset(), cs, &unused))
@@ -5030,7 +5235,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
!args[1]->is_expensive())
{
- String* res2 = args[1]->val_str(&cmp.value2);
+ String* res2= args[1]->val_str(&cmp_value2);
if (!res2)
return FALSE; // Null argument
@@ -5084,16 +5289,18 @@ bool Item_func_like::find_selective_predicates_list_processor(uchar *arg)
(find_selective_predicates_list_processor_data *) arg;
if (use_sampling && used_tables() == data->table->map)
{
- COND_STATISTIC *stat= (COND_STATISTIC *)sql_alloc(sizeof(COND_STATISTIC));
- if (!stat)
+ THD *thd= data->table->in_use;
+ COND_STATISTIC *stat;
+ Item *arg0;
+ if (!(stat= (COND_STATISTIC *) thd->alloc(sizeof(COND_STATISTIC))))
return TRUE;
stat->cond= this;
- Item *arg0= args[0]->real_item();
+ arg0= args[0]->real_item();
if (args[1]->const_item() && arg0->type() == FIELD_ITEM)
stat->field_arg= ((Item_field *)arg0)->field;
else
stat->field_arg= NULL;
- data->list.push_back(stat);
+ data->list.push_back(stat, thd->mem_root);
}
return FALSE;
}
@@ -5187,6 +5394,8 @@ void Regexp_processor_pcre::pcre_exec_warn(int rc) const
{
char buf[64];
const char *errmsg= NULL;
+ THD *thd= current_thd;
+
/*
Make a descriptive message only for those pcre_exec() error codes
that can actually happen in MariaDB.
@@ -5211,8 +5420,8 @@ void Regexp_processor_pcre::pcre_exec_warn(int rc) const
my_snprintf(buf, sizeof(buf), "pcre_exec: Internal error (%d)", rc);
errmsg= buf;
}
- push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
- ER_REGEXP_ERROR, ER(ER_REGEXP_ERROR), errmsg);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REGEXP_ERROR, ER_THD(thd, ER_REGEXP_ERROR), errmsg);
}
@@ -5368,7 +5577,7 @@ void Item_func_like::turboBM_compute_suffixes(int *suff)
int f = 0;
int g = plm1;
int *const splm1 = suff + plm1;
- CHARSET_INFO *cs= cmp.cmp_collation.collation;
+ CHARSET_INFO *cs= cmp_collation.collation;
*splm1 = pattern_len;
@@ -5468,7 +5677,7 @@ void Item_func_like::turboBM_compute_bad_character_shifts()
int *end = bmBc + alphabet_size;
int j;
const int plm1 = pattern_len - 1;
- CHARSET_INFO *cs= cmp.cmp_collation.collation;
+ CHARSET_INFO *cs= cmp_collation.collation;
for (i = bmBc; i < end; i++)
*i = pattern_len;
@@ -5500,7 +5709,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
int shift = pattern_len;
int j = 0;
int u = 0;
- CHARSET_INFO *cs= cmp.cmp_collation.collation;
+ CHARSET_INFO *cs= cmp_collation.collation;
const int plm1= pattern_len - 1;
const int tlmpl= text_len - pattern_len;
@@ -5641,12 +5850,12 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref)
args[0]->under_not(this);
if (args[0]->type() == FIELD_ITEM)
{
- /* replace "NOT <field>" with "<filed> == 0" */
+ /* replace "NOT <field>" with "<field> == 0" */
Query_arena backup, *arena;
Item *new_item;
bool rc= TRUE;
arena= thd->activate_stmt_arena_if_needed(&backup);
- if ((new_item= new Item_func_eq(args[0], new Item_int(0, 1))))
+ if ((new_item= new (thd->mem_root) Item_func_eq(thd, args[0], new (thd->mem_root) Item_int(thd, 0, 1))))
{
new_item->name= name;
rc= (*ref= new_item)->fix_fields(thd, ref);
@@ -5661,7 +5870,7 @@ bool Item_func_not::fix_fields(THD *thd, Item **ref)
Item *Item_bool_rowready_func2::neg_transformer(THD *thd)
{
- Item *item= negated_item();
+ Item *item= negated_item(thd);
return item;
}
@@ -5680,14 +5889,14 @@ Item *Item_func_xor::neg_transformer(THD *thd)
Item_func_xor *new_item;
if ((neg_operand= args[0]->neg_transformer(thd)))
// args[0] has neg_tranformer
- new_item= new(thd->mem_root) Item_func_xor(neg_operand, args[1]);
+ new_item= new(thd->mem_root) Item_func_xor(thd, neg_operand, args[1]);
else if ((neg_operand= args[1]->neg_transformer(thd)))
// args[1] has neg_tranformer
- new_item= new(thd->mem_root) Item_func_xor(args[0], neg_operand);
+ new_item= new(thd->mem_root) Item_func_xor(thd, args[0], neg_operand);
else
{
- neg_operand= new(thd->mem_root) Item_func_not(args[0]);
- new_item= new(thd->mem_root) Item_func_xor(neg_operand, args[1]);
+ neg_operand= new(thd->mem_root) Item_func_not(thd, args[0]);
+ new_item= new(thd->mem_root) Item_func_xor(thd, neg_operand, args[1]);
}
return new_item;
}
@@ -5698,7 +5907,7 @@ Item *Item_func_xor::neg_transformer(THD *thd)
*/
Item *Item_func_isnull::neg_transformer(THD *thd)
{
- Item *item= new Item_func_isnotnull(args[0]);
+ Item *item= new (thd->mem_root) Item_func_isnotnull(thd, args[0]);
return item;
}
@@ -5708,7 +5917,7 @@ Item *Item_func_isnull::neg_transformer(THD *thd)
*/
Item *Item_func_isnotnull::neg_transformer(THD *thd)
{
- Item *item= new Item_func_isnull(args[0]);
+ Item *item= new (thd->mem_root) Item_func_isnull(thd, args[0]);
return item;
}
@@ -5717,7 +5926,7 @@ Item *Item_cond_and::neg_transformer(THD *thd) /* NOT(a AND b AND ...) -> */
/* NOT a OR NOT b OR ... */
{
neg_arguments(thd);
- Item *item= new Item_cond_or(list);
+ Item *item= new (thd->mem_root) Item_cond_or(thd, list);
return item;
}
@@ -5726,7 +5935,7 @@ Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */
/* NOT a AND NOT b AND ... */
{
neg_arguments(thd);
- Item *item= new Item_cond_and(list);
+ Item *item= new (thd->mem_root) Item_cond_and(thd, list);
return item;
}
@@ -5734,7 +5943,7 @@ Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */
Item *Item_func_nop_all::neg_transformer(THD *thd)
{
/* "NOT (e $cmp$ ANY (SELECT ...)) -> e $rev_cmp$" ALL (SELECT ...) */
- Item_func_not_all *new_item= new Item_func_not_all(args[0]);
+ Item_func_not_all *new_item= new (thd->mem_root) Item_func_not_all(thd, args[0]);
Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
allany->create_comp_func(FALSE);
allany->all= !allany->all;
@@ -5745,7 +5954,7 @@ Item *Item_func_nop_all::neg_transformer(THD *thd)
Item *Item_func_not_all::neg_transformer(THD *thd)
{
/* "NOT (e $cmp$ ALL (SELECT ...)) -> e $rev_cmp$" ANY (SELECT ...) */
- Item_func_nop_all *new_item= new Item_func_nop_all(args[0]);
+ Item_func_nop_all *new_item= new (thd->mem_root) Item_func_nop_all(thd, args[0]);
Item_allany_subselect *allany= (Item_allany_subselect*)args[0];
allany->all= !allany->all;
allany->create_comp_func(TRUE);
@@ -5753,45 +5962,45 @@ Item *Item_func_not_all::neg_transformer(THD *thd)
return new_item;
}
-Item *Item_func_eq::negated_item() /* a = b -> a != b */
+Item *Item_func_eq::negated_item(THD *thd) /* a = b -> a != b */
{
- return new Item_func_ne(args[0], args[1]);
+ return new (thd->mem_root) Item_func_ne(thd, args[0], args[1]);
}
-Item *Item_func_ne::negated_item() /* a != b -> a = b */
+Item *Item_func_ne::negated_item(THD *thd) /* a != b -> a = b */
{
- return new Item_func_eq(args[0], args[1]);
+ return new (thd->mem_root) Item_func_eq(thd, args[0], args[1]);
}
-Item *Item_func_lt::negated_item() /* a < b -> a >= b */
+Item *Item_func_lt::negated_item(THD *thd) /* a < b -> a >= b */
{
- return new Item_func_ge(args[0], args[1]);
+ return new (thd->mem_root) Item_func_ge(thd, args[0], args[1]);
}
-Item *Item_func_ge::negated_item() /* a >= b -> a < b */
+Item *Item_func_ge::negated_item(THD *thd) /* a >= b -> a < b */
{
- return new Item_func_lt(args[0], args[1]);
+ return new (thd->mem_root) Item_func_lt(thd, args[0], args[1]);
}
-Item *Item_func_gt::negated_item() /* a > b -> a <= b */
+Item *Item_func_gt::negated_item(THD *thd) /* a > b -> a <= b */
{
- return new Item_func_le(args[0], args[1]);
+ return new (thd->mem_root) Item_func_le(thd, args[0], args[1]);
}
-Item *Item_func_le::negated_item() /* a <= b -> a > b */
+Item *Item_func_le::negated_item(THD *thd) /* a <= b -> a > b */
{
- return new Item_func_gt(args[0], args[1]);
+ return new (thd->mem_root) Item_func_gt(thd, args[0], args[1]);
}
/**
just fake method, should never be called.
*/
-Item *Item_bool_rowready_func2::negated_item()
+Item *Item_bool_rowready_func2::negated_item(THD *thd)
{
DBUG_ASSERT(0);
return 0;
@@ -5814,17 +6023,17 @@ Item *Item_bool_rowready_func2::negated_item()
of the type Item_field or Item_direct_view_ref(Item_field).
*/
-Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
- : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
- context_field(NULL), link_equal_fields(FALSE)
+Item_equal::Item_equal(THD *thd, 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_collation(f2->collation.collation)
{
const_item_cache= 0;
with_const= with_const_item;
- equal_items.push_back(f1);
- equal_items.push_back(f2);
- compare_as_dates= with_const_item && f2->cmp_type() == TIME_RESULT;
+ equal_items.push_back(f1, thd->mem_root);
+ equal_items.push_back(f2, thd->mem_root);
upper_levels= NULL;
- sargable= TRUE;
}
@@ -5840,22 +6049,22 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
outer join).
*/
-Item_equal::Item_equal(Item_equal *item_equal)
- : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
- context_field(NULL), link_equal_fields(FALSE)
+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_collation(item_equal->m_compare_collation)
{
const_item_cache= 0;
List_iterator_fast<Item> li(item_equal->equal_items);
Item *item;
while ((item= li++))
{
- equal_items.push_back(item);
+ equal_items.push_back(item, thd->mem_root);
}
with_const= item_equal->with_const;
- compare_as_dates= item_equal->compare_as_dates;
cond_false= item_equal->cond_false;
upper_levels= item_equal->upper_levels;
- sargable= TRUE;
}
@@ -5873,41 +6082,87 @@ Item_equal::Item_equal(Item_equal *item_equal)
the list. Otherwise the value of c is compared with the value of the
constant item from equal_items. If they are not equal cond_false is set
to TRUE. This serves as an indicator that this Item_equal is always FALSE.
- The optional parameter f is used to adjust the flag compare_as_dates.
*/
-void Item_equal::add_const(Item *c, Item *f)
+void Item_equal::add_const(THD *thd, Item *c)
{
if (cond_false)
return;
if (!with_const)
{
with_const= TRUE;
- if (f)
- compare_as_dates= f->cmp_type() == TIME_RESULT;
- equal_items.push_front(c);
+ equal_items.push_front(c, thd->mem_root);
return;
}
Item *const_item= get_const();
- if (compare_as_dates)
- {
- cmp.set_datetime_cmp_func(this, &c, &const_item);
- cond_false= cmp.compare();
- }
- else
- {
- Item_func_eq *func= new Item_func_eq(c, const_item);
- if (func->set_cmp_func())
+ switch (Item_equal::compare_type()) {
+ case TIME_RESULT:
{
+ enum_field_types f_type= context_field->field_type();
+ longlong value0= c->val_temporal_packed(f_type);
+ longlong value1= const_item->val_temporal_packed(f_type);
+ cond_false= c->null_value || const_item->null_value || value0 != value1;
+ break;
+ }
+ case STRING_RESULT:
+ {
+ String *str1, *str2;
/*
- Setting a comparison function fails when trying to compare
- incompatible charsets. Charset compatibility is checked earlier,
- except for constant subqueries where we may do it here.
+ Suppose we have an expression (with a string type field) like this:
+ WHERE field=const1 AND field=const2 ...
+
+ For all pairs field=constXXX we know that:
+
+ - Item_func_eq::fix_length_and_dec() performed collation and character
+ set aggregation and added character set converters when needed.
+ Note, the case like:
+ WHERE field=const1 COLLATE latin1_bin AND field=const2
+ is not handled here, because the field would be replaced to
+ Item_func_set_collation, which cannot get into Item_equal.
+ So all constXXX that are handled by Item_equal
+ already have compatible character sets with "field".
+
+ - Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees
+ that the comparison collation of all equalities handled by Item_equal
+ match the the collation of the field.
+
+ Therefore, at Item_equal::add_const() time all constants constXXX
+ should be directly comparable to each other without an additional
+ character set conversion.
+ It's safe to do val_str() for "const_item" and "c" and compare
+ them according to the collation of the *field*.
+
+ So in a script like this:
+ CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx);
+ INSERT INTO t1 VALUES ('a'),('A');
+ SELECT * FROM t1 WHERE a='a' AND a='A';
+ Item_equal::add_const() effectively rewrites the condition to:
+ SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A';
+ and then to:
+ SELECT * FROM t1 WHERE a='a'; // if the two constants were equal
+ // e.g. in case of latin1_swedish_ci
+ or to:
+ SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal
+ // e.g. in case of latin1_bin
+
+ Note, both "const_item" and "c" can return NULL, e.g.:
+ SELECT * FROM t1 WHERE a=NULL AND a='const';
+ SELECT * FROM t1 WHERE a='const' AND a=NULL;
+ SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2)
*/
- return;
+ cond_false= !(str1= const_item->val_str(&cmp_value1)) ||
+ !(str2= c->val_str(&cmp_value2)) ||
+ !str1->eq(str2, compare_collation());
+ break;
+ }
+ default:
+ {
+ Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, c, const_item);
+ if (func->set_cmp_func())
+ return;
+ func->quick_fix_field();
+ cond_false= !func->val_int();
}
- func->quick_fix_field();
- cond_false= !func->val_int();
}
if (with_const && equal_items.elements == 1)
cond_true= TRUE;
@@ -5963,12 +6218,12 @@ bool Item_equal::contains(Field *field)
contains a reference to f2->field.
*/
-void Item_equal::merge(Item_equal *item)
+void Item_equal::merge(THD *thd, Item_equal *item)
{
Item *c= item->get_const();
if (c)
item->equal_items.pop();
- equal_items.concat(&item->equal_items);
+ equal_items.append(&item->equal_items);
if (c)
{
/*
@@ -5976,7 +6231,7 @@ void Item_equal::merge(Item_equal *item)
the multiple equality already contains a constant and its
value is not equal to the value of c.
*/
- add_const(c);
+ add_const(thd, c);
}
cond_false|= item->cond_false;
}
@@ -6010,7 +6265,7 @@ void Item_equal::merge(Item_equal *item)
they have common members.
*/
-bool Item_equal::merge_with_check(Item_equal *item, bool save_merged)
+bool Item_equal::merge_with_check(THD *thd, Item_equal *item, bool save_merged)
{
bool intersected= FALSE;
Item_equal_fields_iterator_slow fi(*item);
@@ -6027,12 +6282,12 @@ bool Item_equal::merge_with_check(Item_equal *item, bool save_merged)
if (intersected)
{
if (!save_merged)
- merge(item);
+ merge(thd, item);
else
{
Item *c= item->get_const();
if (c)
- add_const(c);
+ add_const(thd, c);
if (!cond_false)
{
Item *item;
@@ -6040,7 +6295,7 @@ bool Item_equal::merge_with_check(Item_equal *item, bool save_merged)
while ((item= fi++))
{
if (!contains(fi.get_curr_field()))
- add(item);
+ add(item, thd->mem_root);
}
}
}
@@ -6069,7 +6324,7 @@ bool Item_equal::merge_with_check(Item_equal *item, bool save_merged)
Item_equal is joined to the 'list'.
*/
-void Item_equal::merge_into_list(List<Item_equal> *list,
+void Item_equal::merge_into_list(THD *thd, List<Item_equal> *list,
bool save_merged,
bool only_intersected)
{
@@ -6080,17 +6335,17 @@ void Item_equal::merge_into_list(List<Item_equal> *list,
{
if (!merge_into)
{
- if (item->merge_with_check(this, save_merged))
+ if (item->merge_with_check(thd, this, save_merged))
merge_into= item;
}
else
{
- if (merge_into->merge_with_check(item, false))
+ if (merge_into->merge_with_check(thd, item, false))
it.remove();
}
}
if (!only_intersected && !merge_into)
- list->push_back(this);
+ list->push_back(this, thd->mem_root);
}
@@ -6135,7 +6390,7 @@ void Item_equal::sort(Item_field_cmpfunc compare, void *arg)
Currently this function is called only after substitution of constant tables.
*/
-void Item_equal::update_const()
+void Item_equal::update_const(THD *thd)
{
List_iterator<Item> it(equal_items);
if (with_const)
@@ -6164,7 +6419,7 @@ void Item_equal::update_const()
else
{
it.remove();
- add_const(item);
+ add_const(thd, item);
}
}
}
@@ -6304,10 +6559,11 @@ longlong Item_equal::val_int()
while ((item= it++))
{
Field *field= it.get_curr_field();
- /* Skip fields of non-const tables. They haven't been read yet */
- if (field->table->const_table)
+ /* Skip fields of tables that has not been read yet */
+ if (!field->table->status || (field->table->status & STATUS_NULL_ROW))
{
- if (eval_item->cmp(item) || (null_value= item->null_value))
+ const int rc= eval_item->cmp(item);
+ if ((rc == TRUE) || (null_value= (rc == UNKNOWN)))
return 0;
}
}
@@ -6336,15 +6592,15 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg)
}
-Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
+Item *Item_equal::transform(THD *thd, Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
+ DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
Item *item;
Item_equal_fields_iterator it(*this);
while ((item= it++))
{
- Item *new_item= item->transform(transformer, arg);
+ Item *new_item= item->transform(thd, transformer, arg);
if (!new_item)
return 0;
@@ -6355,9 +6611,9 @@ Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
change records at each execution.
*/
if (new_item != item)
- current_thd->change_item_tree((Item **) it.ref(), new_item);
+ thd->change_item_tree((Item **) it.ref(), new_item);
}
- return Item_func::transform(transformer, arg);
+ return Item_func::transform(thd, transformer, arg);
}
@@ -6384,14 +6640,6 @@ void Item_equal::print(String *str, enum_query_type query_type)
}
-CHARSET_INFO *Item_equal::compare_collation()
-{
- Item_equal_fields_iterator it(*this);
- Item *item= it++;
- return item->collation.collation;
-}
-
-
/*
@brief Get the first equal field of multiple equality.
@param[in] field the field to get equal field to
@@ -6597,3 +6845,75 @@ null:
null_value= TRUE;
return 0;
}
+
+
+Item_bool_rowready_func2 *Eq_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_eq(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Eq_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_eq(thd, b, a);
+}
+
+
+Item_bool_rowready_func2* Ne_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_ne(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Ne_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_ne(thd, b, a);
+}
+
+
+Item_bool_rowready_func2* Gt_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_gt(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Gt_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_lt(thd, b, a);
+}
+
+
+Item_bool_rowready_func2* Lt_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_lt(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Lt_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_gt(thd, b, a);
+}
+
+
+Item_bool_rowready_func2* Ge_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_ge(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Ge_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_le(thd, b, a);
+}
+
+
+Item_bool_rowready_func2* Le_creator::create(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_le(thd, a, b);
+}
+
+
+Item_bool_rowready_func2* Le_creator::create_swap(THD *thd, Item *a, Item *b) const
+{
+ return new(thd->mem_root) Item_func_ge(thd, b, a);
+}