Commit b50c6070 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-4848 Wrong metadata or column type for LEAST(1.0,'10')

MDEV-8873 Wrong field type or metadata for LEAST(int_column,string_column)
parent 3266216f
This diff is collapsed.
......@@ -57,6 +57,50 @@ INSERT INTO t1 VALUES (0x32,0x32);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a FLOAT, b SMALLINT);
INSERT INTO t1 VALUES (1,-32678);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a VARCHAR(10), b ENUM('b'));
INSERT INTO t1 VALUES ('a','b');
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a INT, b YEAR);
INSERT INTO t1 VALUES (-2147483648,2015);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
CREATE TABLE t1 (a INT UNSIGNED, b YEAR);
INSERT INTO t1 VALUES (4294967295,2015);
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
--echo # MDEV-8873 Wrong field type or metadata for LEAST(int_column,string_column)
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (-2147483648,'100x');
--source include/func_hybrid_type.inc
DROP TABLE t1;
--echo #
--echo # MDEV-4848 Wrong metadata or column type for LEAST(1.0,'10')
--echo #
--disable_ps_protocol
--enable_metadata
SELECT LEAST(1.0,'10');
--disable_metadata
--enable_ps_protocol
CREATE TABLE t1 AS SELECT LEAST(1.0,'10');
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # End of 10.1 tests
......
......@@ -2833,7 +2833,8 @@ void Item_func_min_max::fix_length_and_dec()
maybe_null=0;
thd= current_thd;
compare_as_dates= find_date_time_item(args, arg_count, 0);
cmp_type=args[0]->result_type();
Item_result tmp_cmp_type= args[0]->cmp_type();
uint string_type_count= 0;
for (uint i=0 ; i < arg_count ; i++)
{
......@@ -2843,14 +2844,39 @@ void Item_func_min_max::fix_length_and_dec()
unsigned_count+= args[i]->unsigned_flag;
if (args[i]->maybe_null)
maybe_null= 1;
cmp_type= item_cmp_type(cmp_type,args[i]->result_type());
tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type());
string_type_count+= args[i]->cmp_type() == STRING_RESULT;
}
unsigned_flag= unsigned_count == arg_count; // if all args are unsigned
if (cmp_type == STRING_RESULT)
switch (tmp_cmp_type) {
case TIME_RESULT:
// At least one temporal argument was found.
collation.set_numeric();
set_handler_by_field_type(compare_as_dates->field_type());
if (mysql_type_to_time_type(Item_func_min_max::field_type()) ==
MYSQL_TIMESTAMP_DATE)
decimals= 0;
else
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
break;
case STRING_RESULT:
/*
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);
else if (cmp_type == INT_RESULT)
{
set_handler_by_field_type(agg_field_type(args, arg_count, false));
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,
......@@ -2862,32 +2888,45 @@ void Item_func_min_max::fix_length_and_dec()
If all args are of INT-alike type, but have different unsigned_flag,
then change type to DECIMAL.
*/
cmp_type= DECIMAL_RESULT;
cached_field_type= MYSQL_TYPE_NEWDECIMAL;
return;
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
}
}
else if (cmp_type == DECIMAL_RESULT)
{
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.
*/
set_handler_by_field_type(agg_field_type(args, arg_count, true));
}
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));
}
else if (cmp_type == REAL_RESULT)
fix_char_length(float_length(decimals));
set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL);
break;
if (compare_as_dates)
{
cached_field_type= compare_as_dates->field_type();
if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_DATE)
decimals= 0;
else
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
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::tmp_table_field() does not
distinguish between DOUBLE and FLOAT and always creates Field_double.
Perhaps we should eventually change this to use agg_field_type() here,
and fix Item_func::tmp_table_field() to create Field_float when possible.
*/
set_handler_by_field_type(MYSQL_TYPE_DOUBLE);
break;
}
else
cached_field_type= agg_field_type(args, arg_count, false);
}
......@@ -2964,7 +3003,7 @@ String *Item_func_min_max::val_str(String *str)
DBUG_ASSERT(fixed == 1);
if (compare_as_dates)
return val_string_from_date(str);
switch (cmp_type) {
switch (Item_func_min_max::result_type()) {
case INT_RESULT:
return val_string_from_int(str);
case DECIMAL_RESULT:
......
......@@ -986,19 +986,28 @@ public:
};
class Item_func_min_max :public Item_func
/**
Item_func_min_max does not derive from Item_func_hybrid_field_type
because the way how its methods val_xxx() and get_date() work depend
not only by its arguments, but also on the context in which
LEAST() and GREATEST() appear.
For example, using Item_func_min_max in a CAST like this:
CAST(LEAST('11','2') AS SIGNED)
forces Item_func_min_max to compare the arguments as numbers rather
than strings.
Perhaps this should be changed eventually (see MDEV-5893).
*/
class Item_func_min_max :public Item_func,
public Type_handler_hybrid_field_type
{
Item_result cmp_type;
String tmp_value;
int cmp_sign;
/* An item used for issuing warnings while string to DATETIME conversion. */
Item *compare_as_dates;
THD *thd;
protected:
enum_field_types cached_field_type;
public:
Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
Item_func(thd, list), cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg),
Item_func(thd, list), cmp_sign(cmp_sign_arg),
compare_as_dates(0) {}
double val_real();
longlong val_int();
......@@ -1006,8 +1015,12 @@ public:
my_decimal *val_decimal(my_decimal *);
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void fix_length_and_dec();
enum Item_result result_type () const { return cmp_type; }
enum_field_types field_type() const { return cached_field_type; }
enum Item_result cmp_type() const
{ return Type_handler_hybrid_field_type::cmp_type(); }
enum Item_result result_type() const
{ return Type_handler_hybrid_field_type::result_type(); }
enum_field_types field_type() const
{ return Type_handler_hybrid_field_type::field_type(); }
};
class Item_func_min :public Item_func_min_max
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment