From 1a0e3a285818f0a71cdc7f387a63f7cc72c72972 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 4 May 2007 00:53:37 +0400 Subject: Bug#23656: Wrong conversion result of a DATETIME to integer using CAST function. The generic string to int conversion was used by the Item_func_signed and the Item_func_unsigned classes to convert DATE/DATETIME values to the SIGNED/UNSIGNED type. But this conversion produces wrong results for such values. Now if the item which result has to be converted can return its result as longlong then the item->val_int() method is used to allow the item to carry out the conversion itself and return the correct result. This condition is checked in the Item_func_signed::val_int() and the Item_func_unsigned::val_int() functions. mysql-test/t/cast.test: Added a test case for the bug#23656: Wrong conversion result of a DATETIME to integer using CAST function. mysql-test/r/cast.result: Added a test case for the bug#23656: Wrong conversion result of a DATETIME to integer using CAST function. sql/item_func.cc: Bug#23656: Wrong conversion result of a DATETIME to integer using CAST function. Now if the item which result has to be converted can return its result as longlong then the item->val_int() method is used to allow the item to carry out the conversion itself and return the correct result. This condition is checked in the Item_func_signed::val_int() and the Item_func_unsigned::val_int() functions. --- sql/item_func.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sql/item_func.cc') diff --git a/sql/item_func.cc b/sql/item_func.cc index c6b2fa5cc3e..12bb6571369 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -477,7 +477,8 @@ longlong Item_func_signed::val_int() longlong value; int error; - if (args[0]->cast_to_int_type() != STRING_RESULT) + if (args[0]->cast_to_int_type() != STRING_RESULT || + args[0]->result_as_longlong()) { value= args[0]->val_int(); null_value= args[0]->null_value; @@ -529,7 +530,8 @@ longlong Item_func_unsigned::val_int() return (longlong) (dvalue + (dvalue > 0 ? 0.5 : -0.5)); } - if (args[0]->cast_to_int_type() != STRING_RESULT) + if (args[0]->cast_to_int_type() != STRING_RESULT || + args[0]->result_as_longlong()) { value= args[0]->val_int(); null_value= args[0]->null_value; -- cgit v1.2.1 From 99bde6d9ecf76fabfa1460b9780134f40663a11f Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 4 May 2007 18:57:10 +0400 Subject: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. The LEAST/GREATEST functions compared DATE/DATETIME values as strings which in some cases could lead to a wrong result. A new member function called cmp_datetimes() is added to the Item_func_min_max class. It compares arguments in DATETIME context and returns index of the least/greatest argument. The Item_func_min_max::fix_length_and_dec() function now detects when arguments should be compared in DATETIME context and sets the newly added flag compare_as_dates. It indicates that the cmp_datetimes() function should be called to get a correct result. Item_func_min_max::val_xxx() methods are corrected to call the cmp_datetimes() function when needed. Objects of the Item_splocal class now stores and reports correct original field type. mysql-test/t/type_datetime.test: Added a test case for the bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. mysql-test/r/type_datetime.result: Added a test case for the bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. mysql-test/r/sp-vars.result: A test case result corrected after the fix for the bug#27759. sql/mysql_priv.h: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. Added the prototype of the get_datetime_value() function. sql/item_func.h: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. A new member function called cmp_datetimes() is added to the Item_func_min_max class. sql/item_func.cc: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. A new member function called cmp_datetimes() is added to the Item_func_min_max class. It compares arguments in DATETIME context and returns index of the least/greatest argument. The Item_func_min_max::fix_length_and_dec() function now detects when arguments should be compared in DATETIME context and sets the newly added flag compare_as_dates. It indicates that the cmp_datetimes() function should be called to get a correct result. Item_func_min_max::val_xxx() methods are corrected to call the cmp_datetimes() function when needed. sql/item_cmpfunc.cc: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. The get_datetime_value() function is no longer static. sql/item.h: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. Objects of the Item_splocal class now stores and reports correct original field type. sql/item.cc: Bug#27759: Wrong DATE/DATETIME comparison in LEAST()/GREATEST() functions. Objects of the Item_splocal class now stores and reports correct original field type. --- sql/item_func.cc | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'sql/item_func.cc') diff --git a/sql/item_func.cc b/sql/item_func.cc index 503b4362e7a..4ee6af37dad 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2120,6 +2120,7 @@ double Item_func_units::val_real() void Item_func_min_max::fix_length_and_dec() { int max_int_part=0; + bool datetime_found= FALSE; decimals=0; max_length=0; maybe_null=0; @@ -2133,18 +2134,88 @@ void Item_func_min_max::fix_length_and_dec() if (args[i]->maybe_null) maybe_null=1; cmp_type=item_cmp_type(cmp_type,args[i]->result_type()); + if (args[i]->result_type() != ROW_RESULT && args[i]->is_datetime()) + { + datetime_found= TRUE; + if (!datetime_item || args[i]->field_type() == MYSQL_TYPE_DATETIME) + datetime_item= args[i]; + } } if (cmp_type == STRING_RESULT) + { agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1); + if (datetime_found) + { + thd= current_thd; + compare_as_dates= TRUE; + } + } else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT)) max_length= my_decimal_precision_to_length(max_int_part+decimals, decimals, unsigned_flag); } +/* + Compare item arguments in the DATETIME context. + + SYNOPSIS + cmp_datetimes() + value [out] found least/greatest DATE/DATETIME value + + DESCRIPTION + Compare item arguments as DATETIME values and return the index of the + least/greatest argument in the arguments array. + The correct integer DATE/DATETIME value of the found argument is + stored to the value pointer, if latter is provided. + + RETURN + 0 If one of arguments is NULL + # index of the least/greatest argument +*/ + +uint Item_func_min_max::cmp_datetimes(ulonglong *value) +{ + ulonglong min_max; + uint min_max_idx= 0; + LINT_INIT(min_max); + + for (uint i=0; i < arg_count ; i++) + { + Item **arg= args + i; + bool is_null; + ulonglong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); + if ((null_value= args[i]->null_value)) + return 0; + if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) + { + min_max= res; + min_max_idx= i; + } + } + if (value) + { + *value= min_max; + if (datetime_item->field_type() == MYSQL_TYPE_DATE) + *value/= 1000000L; + } + return min_max_idx; +} + + String *Item_func_min_max::val_str(String *str) { DBUG_ASSERT(fixed == 1); + if (compare_as_dates) + { + String *str_res; + uint min_max_idx= cmp_datetimes(NULL); + if (null_value) + return 0; + str_res= args[min_max_idx]->val_str(str); + str_res->set_charset(collation.collation); + return str_res; + } switch (cmp_type) { case INT_RESULT: { @@ -2212,6 +2283,12 @@ double Item_func_min_max::val_real() { DBUG_ASSERT(fixed == 1); double value=0.0; + if (compare_as_dates) + { + ulonglong result; + (void)cmp_datetimes(&result); + return (double)result; + } for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2233,6 +2310,12 @@ longlong Item_func_min_max::val_int() { DBUG_ASSERT(fixed == 1); longlong value=0; + if (compare_as_dates) + { + ulonglong result; + (void)cmp_datetimes(&result); + return (longlong)result; + } for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2256,6 +2339,13 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) my_decimal tmp_buf, *tmp, *res; LINT_INIT(res); + if (compare_as_dates) + { + ulonglong value; + (void)cmp_datetimes(&value); + ulonglong2decimal(value, dec); + return dec; + } for (uint i=0; i < arg_count ; i++) { if (i == 0) -- cgit v1.2.1