summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2015-09-30 12:37:34 +0400
committerAlexander Barkov <bar@mariadb.org>2015-09-30 12:37:34 +0400
commitcc9cfecab78bde9376e4406bf24506e92fdaaeac (patch)
tree1ca3c873ef881d5e25be4aa390253f7eeb17c4ec /sql/item_cmpfunc.cc
parent09b87d6293b4b41321ba98366d5d7ade9ad681d3 (diff)
downloadmariadb-git-cc9cfecab78bde9376e4406bf24506e92fdaaeac.tar.gz
MDEV-8865 Wrong field type or metadata for COALESCE(signed_int_column, unsigned_int_column)
Item_func_hybrid_field_type did not return correct field_type(), cmp_type() and result_type() in some cases, because cached_result_type and cached_field_type were set in independent pieces of the code and did not properly match to each other. Fix: - Removing Item_func_hybrid_result_type - Deriving Item_func_hybrid_field_type directly from Item_func - Introducing a new class Type_handler which guarantees that field_type(), cmp_type() and result_type() are always properly synchronized and using the new class in Item_func_hybrid_field_type.
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc135
1 files changed, 72 insertions, 63 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 0e2802fe794..bad24dc1ec2 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
@@ -185,6 +144,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
@@ -198,14 +173,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;
}
@@ -2261,13 +2272,13 @@ void
Item_func_case_abbreviation2::fix_length_and_dec2(Item **args)
{
uint32 char_length;
- agg_result_type(&cached_result_type, args, 2);
- cached_field_type= agg_field_type(args, 2);
+ set_handler_by_field_type(agg_field_type(args, 2, true));
maybe_null=args[0]->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)
+ if (Item_func_case_abbreviation2::result_type() == DECIMAL_RESULT ||
+ Item_func_case_abbreviation2::result_type() == INT_RESULT)
{
int len0= args[0]->max_char_length() - args[0]->decimals
- (args[0]->unsigned_flag ? 0 : 1);
@@ -2280,9 +2291,10 @@ Item_func_case_abbreviation2::fix_length_and_dec2(Item **args)
else
char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
- switch (cached_result_type) {
+ switch (Item_func_case_abbreviation2::result_type()) {
case STRING_RESULT:
- if (count_string_result_length(cached_field_type, args, 2))
+ if (count_string_result_length(Item_func_case_abbreviation2::field_type(),
+ args, 2))
return;
break;
case DECIMAL_RESULT:
@@ -2459,8 +2471,7 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
void Item_func_if::cache_type_info(Item *source)
{
Type_std_attributes::set(source);
- cached_field_type= source->field_type();
- cached_result_type= source->result_type();
+ set_handler_by_field_type(source->field_type());
maybe_null= source->maybe_null;
}
@@ -2475,7 +2486,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)
@@ -2546,8 +2557,7 @@ Item_func_nullif::fix_length_and_dec()
if (!args[2]) // Only false if EOM
return;
- cached_result_type= args[2]->result_type();
- cached_field_type= args[2]->field_type();
+ set_handler_by_field_type(args[2]->field_type());
collation.set(args[2]->collation);
decimals= args[2]->decimals;
unsigned_flag= args[2]->unsigned_flag;
@@ -2961,12 +2971,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.
@@ -3295,11 +3304,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);
- switch (cached_result_type) {
+ set_handler_by_field_type(agg_field_type(args, arg_count, true));
+ switch (Item_func_coalesce::result_type()) {
case STRING_RESULT:
- if (count_string_result_length(cached_field_type, args, arg_count))
+ if (count_string_result_length(Item_func_coalesce::field_type(),
+ args, arg_count))
return;
break;
case DECIMAL_RESULT: