diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-04-22 23:47:27 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-22 23:47:27 +0400 |
commit | 658082551ffd49a89d5473775bef5197e572d17e (patch) | |
tree | ef6e2a34d6ede0d4a0d7cae20aa2e2355e1c5195 /sql | |
parent | ba670edfa30a5317c4b0eebd005b83dce63d0dfc (diff) | |
download | mariadb-git-658082551ffd49a89d5473775bef5197e572d17e.tar.gz |
MDEV-12506 Split Item_func_min_max::fix_length_and_dec() into methods in Type_handler + MDEV-12497 + MDEV-12504
This patch does the following:
1. Adds a new method Type_handler_hybrid_field_type::aggregate_for_min_max()
- For non-traditional data types it uses
type_handler_data->m_type_aggregator_for_result.find_handler()
This allows pluggable data types to define in the future their
own behavior of the result data type detection for LEAST/GREATEST.
Also, this disallows expressions of the GEOMETRY data type
(and its variants such as POINT) to be mixed in with
numeric and temporal data types in LEAST/GREATEST.
- For traditional data types it reproduces the old behavior of
the result data type detection (but not attributes, see below).
2. Adds a new virtual method Type_handler::Item_func_min_max_fix_attributes()
and reuses as much as possible the code that calculates data type attributes
for CASE-alike functions (e.g. CASE..THEN, COALESCE, IF).
As the old code responsible for attributes calculation in the old
implementation of Item_func_min_max::fix_length_and_dec()
was not fully correct, this automatically fixes the following bugs:
- MDEV-12497 Wrong data type for LEAST(latin1_expr, utf8_expr)
The old fix_length_and_dec() calculated max_length before
character set aggregation. Now max_length is calculated after, in
Item_func::count_string_length() called from
Item_func::aggregate_attributes_string() called from
Type_handler_string_result::Item_hybrid_func_fix_attributes() called from
Type_handler::Item_func_min_max_fix_attributes() called from
Item_func_min_max::fix_length_and_dec().
- MDEV-12504 Wrong data type for LEAST(date_expr,time_expr)
The old fix_length_and_dec() simply used the maximum of max_length
among all arguments to set its own max_length and did not take
into account that a mixture of DATE and TIME becomes DATETIME.
Now this is correctly handled by:
Type_handler_datetime_common::Item_hybrid_func_fix_attributes() called from
Type_handler::Item_func_min_max_fix_attributes() called from
Item_func_min_max::fix_length_and_dec().
3. Removes the old implementation of Item_func_min_max::fix_length_and_dec()
and replaces it to calls of the new methods.
4. Cleanup: moves the code related to unsigned_flag processing
from Type_handler_hybrid_field_type::aggregate_for_result()
to Type_handler_int_result::Item_hybrid_func_fix_attributes().
This is done:
- to avoid code duplication in
Type_handler_hybrid_field_type::aggregate_for_min_max()
- to get rid of one more call for field_type(), which is unfriendly
to the conceipt of pluggable data types.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 3 | ||||
-rw-r--r-- | sql/item_func.cc | 117 | ||||
-rw-r--r-- | sql/item_func.h | 29 | ||||
-rw-r--r-- | sql/sql_type.cc | 167 | ||||
-rw-r--r-- | sql/sql_type.h | 9 |
5 files changed, 190 insertions, 135 deletions
diff --git a/sql/item.cc b/sql/item.cc index 0456adc3fb1..f5ef8f30dff 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10187,7 +10187,8 @@ void Item_cache_row::set_null() Item_type_holder::Item_type_holder(THD *thd, Item *item) :Item(thd, item), Type_handler_hybrid_field_type(item->real_type_handler()), - enum_set_typelib(0) + enum_set_typelib(0), + geometry_type(Field::GEOM_GEOMETRY) { DBUG_ASSERT(item->fixed); maybe_null= item->maybe_null; diff --git a/sql/item_func.cc b/sql/item_func.cc index a52c2609412..96057ec31f5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2697,119 +2697,12 @@ double Item_func_units::val_real() } -void Item_func_min_max::fix_length_and_dec() +bool Item_func_min_max::fix_attributes(Item **items, uint nitems) { - uint unsigned_count= 0; - int max_int_part=0; - decimals=0; - max_length=0; - maybe_null=0; - Item_result tmp_cmp_type= args[0]->cmp_type(); - uint string_type_count= 0; - uint temporal_type_count= 0; - enum_field_types temporal_field_type= MYSQL_TYPE_DATETIME; - - for (uint i=0 ; i < arg_count ; i++) - { - set_if_bigger(max_length, args[i]->max_length); - set_if_bigger(decimals, args[i]->decimals); - set_if_bigger(max_int_part, args[i]->decimal_int_part()); - unsigned_count+= args[i]->unsigned_flag; - if (args[i]->maybe_null) - maybe_null= 1; - tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type()); - string_type_count+= args[i]->cmp_type() == STRING_RESULT; - if (args[i]->cmp_type() == TIME_RESULT) - { - if (!temporal_type_count) - temporal_field_type= args[i]->field_type(); - else - temporal_field_type= Field::field_type_merge(temporal_field_type, - args[i]->field_type()); - temporal_type_count++; - } - } - unsigned_flag= unsigned_count == arg_count; // if all args are unsigned - - switch (tmp_cmp_type) { - case TIME_RESULT: - // At least one temporal argument was found. - collation.set_numeric(); - set_handler_by_field_type(temporal_field_type); - if (is_temporal_type_with_time(temporal_field_type)) - set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); - else - decimals= 0; - break; - - case STRING_RESULT: - if (aggregate_for_result(func_name(), args, arg_count, false)) - return; - /* - All arguments are of string-alike types: - CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB, SET, ENUM - No numeric and no temporal types were found. - */ - agg_arg_charsets_for_string_result_with_comparison(collation, - args, arg_count); - break; - - case INT_RESULT: - /* - All arguments have INT-alike types: - TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT. - */ - collation.set_numeric(); - fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part + - decimals, - decimals, - unsigned_flag)); - if (unsigned_count != 0 && unsigned_count != arg_count) - { - /* - If all args are of INT-alike type, but have different unsigned_flag, - then change type to DECIMAL. - */ - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - } - else - { - /* - There are only INT-alike arguments with equal unsigned_flag. - Aggregate types to get the best covering type. - Treat BIT as LONGLONG when aggregating to non-BIT types. - Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT. - */ - if (aggregate_for_result(func_name(), args, arg_count, true)) - return; - } - break; - - case DECIMAL_RESULT: - // All arguments are of DECIMAL type - collation.set_numeric(); - fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part + - decimals, - decimals, - unsigned_flag)); - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - break; - - case ROW_RESULT: - DBUG_ASSERT(0); - // Pass through - case REAL_RESULT: - collation.set_numeric(); - fix_char_length(float_length(decimals)); - /* - Set type to DOUBLE, as Item_func::create_tmp_field() does not - distinguish between DOUBLE and FLOAT and always creates Field_double. - Perhaps we should eventually change this to use aggregate_for_result() - and fix Item_func::create_tmp_field() to create Field_float when possible. - */ - set_handler_by_field_type(MYSQL_TYPE_DOUBLE); - break; - } + bool rc= Item_func_min_max::type_handler()-> + Item_func_min_max_fix_attributes(current_thd, this, items, nitems); + DBUG_ASSERT(!rc || current_thd->is_error()); + return rc; } diff --git a/sql/item_func.h b/sql/item_func.h index a367a0d74c4..3fc2586729d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1434,6 +1434,8 @@ class Item_func_min_max :public Item_hybrid_func { String tmp_value; int cmp_sign; +protected: + bool fix_attributes(Item **item, uint nitems); public: Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg): Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg) @@ -1474,7 +1476,32 @@ public: return Item_func_min_max::type_handler()-> Item_func_min_max_get_date(this, res, fuzzy_date); } - void fix_length_and_dec(); + void aggregate_attributes_real(Item **items, uint nitems) + { + /* + Aggregating attributes for the double data type for LEAST/GREATEST + is almost the same with aggregating for CASE-alike hybrid functions, + (CASE..THEN, COALESCE, IF, etc). + There is one notable difference though, when a numeric argument is mixed + with a string argument: + - CASE-alike functions return a string data type in such cases + COALESCE(10,'x') -> VARCHAR(2) = '10' + - LEAST/GREATEST returns double: + GREATEST(10,'10e4') -> DOUBLE = 100000 + As the string argument can represent a number in the scientific notation, + like in the example above, max_length of the result can be longer than + max_length of the arguments. To handle this properly, max_length is + additionally assigned to the result of float_length(decimals). + */ + Item_func::aggregate_attributes_real(items, nitems); + max_length= float_length(decimals); + } + void fix_length_and_dec() + { + if (aggregate_for_min_max(func_name(), args, arg_count)) + return; + fix_attributes(args, arg_count); + } }; class Item_func_min :public Item_func_min_max diff --git a/sql/sql_type.cc b/sql/sql_type.cc index a2994c74250..6a150703831 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -370,7 +370,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname, return true; } set_handler(items[0]->type_handler()); - uint unsigned_count= items[0]->unsigned_flag; for (uint i= 1 ; i < nitems ; i++) { const Type_handler *cur= items[i]->type_handler(); @@ -388,26 +387,6 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname, type_handler()->name().ptr(), cur->name().ptr(), funcname); return true; } - unsigned_count+= items[i]->unsigned_flag; - } - switch (field_type()) { - 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. - */ - set_handler(&type_handler_newdecimal); - } - default: - break; } return false; } @@ -479,6 +458,114 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h) } +/** + Aggregate data type handler for LEAST/GRATEST. + aggregate_for_min_max() is close to aggregate_for_comparison(), + but tries to preserve the exact type handler for string, int and temporal + data types (instead of converting to super-types). + FLOAT is not preserved and is converted to its super-type (DOUBLE). + This should probably fixed eventually, for symmetry. +*/ + +bool +Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h) +{ + if (!m_type_handler->is_traditional_type() || + !h->is_traditional_type()) + { + /* + If at least one data type is non-traditional, + do aggregation for result immediately. + For now we suppose that these two expressions: + - LEAST(type1, type2) + - COALESCE(type1, type2) + return the same data type (or both expressions return error) + if type1 and/or type2 are non-traditional. + This may change in the future. + */ + h= type_handler_data-> + m_type_aggregator_for_result.find_handler(m_type_handler, h); + if (!h) + return true; + m_type_handler= h; + return false; + } + + Item_result a= cmp_type(); + Item_result b= h->cmp_type(); + DBUG_ASSERT(a != ROW_RESULT); // Disallowed by check_cols() in fix_fields() + DBUG_ASSERT(b != ROW_RESULT); // Disallowed by check_cols() in fix_fields() + + if (a == STRING_RESULT && b == STRING_RESULT) + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + else if (a == INT_RESULT && b == INT_RESULT) + { + // BIT aggregates with non-BIT as BIGINT + if (m_type_handler != h) + { + if (m_type_handler == &type_handler_bit) + m_type_handler= &type_handler_longlong; + else if (h == &type_handler_bit) + h= &type_handler_longlong; + } + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + } + else if (a == TIME_RESULT || b == TIME_RESULT) + { + if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1) + { + /* + We're here if there's only one temporal data type: + either m_type_handler or h. + */ + if (b == TIME_RESULT) + m_type_handler= h; // Temporal types bit non-temporal types + } + else + { + /* + We're here if both m_type_handler and h are temporal data types. + */ + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + } + } + else if ((a == INT_RESULT || a == DECIMAL_RESULT) && + (b == INT_RESULT || b == DECIMAL_RESULT)) + { + m_type_handler= &type_handler_newdecimal; + } + else + { + m_type_handler= &type_handler_double; + } + return false; +} + + +bool +Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname, + Item **items, uint nitems) +{ + // LEAST/GREATEST require at least two arguments + DBUG_ASSERT(nitems > 1); + set_handler(items[0]->type_handler()); + for (uint i= 1; i < nitems; i++) + { + const Type_handler *cur= items[i]->type_handler(); + if (aggregate_for_min_max(cur)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + type_handler()->name().ptr(), cur->name().ptr(), funcname); + return true; + } + } + return false; +} + + const Type_handler * Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0, const Type_handler *h1) @@ -1313,6 +1400,17 @@ bool Type_handler_int_result:: Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, Item **items, uint nitems) const { + uint unsigned_flag= items[0]->unsigned_flag; + for (uint i= 1; i < nitems; i++) + { + if (unsigned_flag != items[i]->unsigned_flag) + { + // Convert a mixture of signed and unsigned int to decimal + func->set_handler(&type_handler_newdecimal); + func->aggregate_attributes_decimal(items, nitems); + return false; + } + } func->aggregate_attributes_int(items, nitems); return false; } @@ -1379,6 +1477,33 @@ bool Type_handler_timestamp_common:: return false; } +/*************************************************************************/ + +bool Type_handler:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + /* + Aggregating attributes for LEAST/GREATES is exactly the same + with aggregating for CASE-alike functions (e.g. COALESCE) + for the majority of data type handlers. + */ + return Item_hybrid_func_fix_attributes(thd, func, items, nitems); +} + + +bool Type_handler_real_result:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + /* + DOUBLE is an exception and aggregates attributes differently + for LEAST/GREATEST vs CASE-alike functions. See the comment in + Item_func_min_max::aggregate_attributes_real(). + */ + func->aggregate_attributes_real(items, nitems); + return false; +} /*************************************************************************/ diff --git a/sql/sql_type.h b/sql/sql_type.h index 2c8d5df050f..21b8e75db11 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -485,6 +485,10 @@ public: virtual bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, Item **items, uint nitems) const= 0; + virtual bool Item_func_min_max_fix_attributes(THD *thd, + Item_func_min_max *func, + Item **items, + uint nitems) const; virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0; virtual bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const= 0; virtual bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const= 0; @@ -840,6 +844,8 @@ public: bool set_comparator_func(Arg_comparator *cmp) const; bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, Item **items, uint nitems) const; + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const; bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; @@ -1602,6 +1608,7 @@ public: class Type_handler_hybrid_field_type { const Type_handler *m_type_handler; + bool aggregate_for_min_max(const Type_handler *other); public: Type_handler_hybrid_field_type(); Type_handler_hybrid_field_type(const Type_handler *handler) @@ -1657,6 +1664,8 @@ public: bool aggregate_for_result(const Type_handler *other); bool aggregate_for_result(const char *funcname, Item **item, uint nitems, bool treat_bit_as_number); + bool aggregate_for_min_max(const char *funcname, Item **item, uint nitems); + bool aggregate_for_num_op(const class Type_aggregator *aggregator, const Type_handler *h0, const Type_handler *h1); }; |