Commit f79bab3a authored by Alexander Barkov's avatar Alexander Barkov

MDEV-17318 CAST(LEAST(zero_date,non_zero_date) AS numeric_data_type) returns a wrong result

Also fixes:
MDEV-17330 Wrong result for 0 + LEAST(TIME'-10:00:00',TIME'10:00:00')

Problems:

1. These methods did not take into account the current session date flags
and passed date_mode_t(0) to func->get_date():

Type_handler_temporal_result::Item_func_min_max_val_real
Type_handler_temporal_result::Item_func_min_max_val_int
Type_handler_temporal_result::Item_func_min_max_val_decimal

Fixing to pass sql_mode_for_dates(thd) instead of date_mode_t(0).
Note, sql_mode_for_dates(thd) is only needed for DATE/DATETIME
data types. It is not needed for TIME.
So splitting value methods Type_handler_temporal_result::Item_func_min_max_xxx
into individual implementations for
Type_handler_{time|date|datetime|timestamp}_common
and, instead of calling get_date(), reusing inside classes
Time(), Date(), Datetime() and their methods to_longlong().
sql_mode_for_dates(thd) is automatically passed to get_date()
inside Date() and Datetime() constructors.

The switch to classes also fixed the problem reported in MDEV-17330.
Type_handler_temporal_result::Item_func_min_max_val_int() used to
call TIME_to_ulonglong(), which was not correct for TIME.
Changing the code to use Time().to_longlong() solved this.

2. Type_handler_temporal_result::Item_func_min_max_get_date
also did not take into account the current session
date flags in case of conversion from DATE/DATETIME to time
and passed date_mode_t(0) to get_date_native().
Fixing to pass sql_mode_for_dates(thd) in case of conversion
from DATE/DATETIME to TIME.
parent 23740441
......@@ -3873,5 +3873,75 @@ t1 CREATE TABLE `t1` (
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
# MDEV-17318 CAST(LEAST(zero_date,non_zero_date) AS numeric_data_type) returns a wrong result
#
SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE';
SELECT
LEAST('0000-00-00',DATE'2001-01-01') AS c0,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS CHAR) AS string,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATE) AS date,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATETIME) AS datetime,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS TIME) AS time,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DECIMAL) AS dc,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DOUBLE) AS dbl,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS SIGNED) AS sint,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS UNSIGNED) AS uint;
c0 string date datetime time dc dbl sint uint
NULL NULL NULL NULL NULL NULL NULL NULL NULL
Warnings:
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
CREATE TABLE t1 AS SELECT
LEAST('0000-00-00',DATE'2001-01-01') AS c0,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS CHAR) AS string,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATE) AS date,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATETIME) AS datetime,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS TIME) AS time,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DECIMAL) AS dc,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DOUBLE) AS dbl,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS SIGNED) AS sint,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS UNSIGNED) AS uint;
Warnings:
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
Warning 1292 Incorrect datetime value: '0000-00-00'
SELECT * FROM t1;
c0 string date datetime time dc dbl sint uint
NULL NULL NULL NULL NULL NULL NULL NULL NULL
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c0` date DEFAULT NULL,
`string` varchar(10) DEFAULT NULL,
`date` date DEFAULT NULL,
`datetime` datetime DEFAULT NULL,
`time` time DEFAULT NULL,
`dc` decimal(10,0) DEFAULT NULL,
`dbl` double DEFAULT NULL,
`sint` bigint(10) DEFAULT NULL,
`uint` bigint(20) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
# MDEV-17330 Wrong result for 0 + LEAST(TIME'-10:00:00',TIME'10:00:00')
#
SELECT 0 + LEAST(TIME'-10:00:00',TIME'10:00:00') AS c;
c
-100000
#
# End of 10.4 tests
#
......@@ -682,6 +682,40 @@ SHOW CREATE TABLE t1;
DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
--echo # MDEV-17318 CAST(LEAST(zero_date,non_zero_date) AS numeric_data_type) returns a wrong result
--echo #
SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE';
SELECT
LEAST('0000-00-00',DATE'2001-01-01') AS c0,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS CHAR) AS string,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATE) AS date,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATETIME) AS datetime,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS TIME) AS time,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DECIMAL) AS dc,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DOUBLE) AS dbl,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS SIGNED) AS sint,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS UNSIGNED) AS uint;
CREATE TABLE t1 AS SELECT
LEAST('0000-00-00',DATE'2001-01-01') AS c0,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS CHAR) AS string,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATE) AS date,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DATETIME) AS datetime,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS TIME) AS time,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DECIMAL) AS dc,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS DOUBLE) AS dbl,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS SIGNED) AS sint,
CAST(LEAST('0000-00-00',DATE'2001-01-01') AS UNSIGNED) AS uint;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
--echo # MDEV-17330 Wrong result for 0 + LEAST(TIME'-10:00:00',TIME'10:00:00')
--echo #
SELECT 0 + LEAST(TIME'-10:00:00',TIME'10:00:00') AS c;
--echo #
--echo # End of 10.4 tests
......
......@@ -4639,13 +4639,30 @@ double Type_handler_string_result::
}
double Type_handler_temporal_result::
double Type_handler_time_common::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
MYSQL_TIME ltime;
if (func->get_date(current_thd, &ltime, date_mode_t(0)))
return 0;
return TIME_to_double(&ltime);
return Time(current_thd, func).to_double();
}
double Type_handler_date_common::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
return Date(current_thd, func).to_double();
}
double Type_handler_datetime_common::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
return Datetime(current_thd, func).to_double();
}
double Type_handler_timestamp_common::
Item_func_min_max_val_real(Item_func_min_max *func) const
{
return Datetime(current_thd, func).to_double();
}
......@@ -4663,13 +4680,31 @@ longlong Type_handler_string_result::
}
longlong Type_handler_temporal_result::
longlong Type_handler_time_common::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
MYSQL_TIME ltime;
if (func->get_date(current_thd, &ltime, date_mode_t(0)))
return 0;
return TIME_to_ulonglong(&ltime);
return Time(current_thd, func).to_longlong();
}
longlong Type_handler_date_common::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
return Date(current_thd, func).to_longlong();
}
longlong Type_handler_datetime_common::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
return Datetime(current_thd, func).to_longlong();
}
longlong Type_handler_timestamp_common::
Item_func_min_max_val_int(Item_func_min_max *func) const
{
return Datetime(current_thd, func).to_longlong();
}
......@@ -4696,14 +4731,35 @@ my_decimal *Type_handler_numeric::
}
my_decimal *Type_handler_temporal_result::
my_decimal *Type_handler_time_common::
Item_func_min_max_val_decimal(Item_func_min_max *func,
my_decimal *dec) const
{
MYSQL_TIME ltime;
if (func->get_date(current_thd, &ltime, date_mode_t(0)))
return 0;
return date2my_decimal(&ltime, dec);
return Time(current_thd, func).to_decimal(dec);
}
my_decimal *Type_handler_date_common::
Item_func_min_max_val_decimal(Item_func_min_max *func,
my_decimal *dec) const
{
return Date(current_thd, func).to_decimal(dec);
}
my_decimal *Type_handler_datetime_common::
Item_func_min_max_val_decimal(Item_func_min_max *func,
my_decimal *dec) const
{
return Datetime(current_thd, func).to_decimal(dec);
}
my_decimal *Type_handler_timestamp_common::
Item_func_min_max_val_decimal(Item_func_min_max *func,
my_decimal *dec) const
{
return Datetime(current_thd, func).to_decimal(dec);
}
......@@ -4733,7 +4789,19 @@ bool Type_handler_temporal_result::
Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
MYSQL_TIME *ltime, date_mode_t fuzzydate) const
{
return func->get_date_native(thd, ltime, fuzzydate);
/*
- If the caller specified TIME_TIME_ONLY, then it's going to convert
a DATETIME or DATE to TIME. So we pass the default flags for date. This is
exactly the same with what Item_func_min_max_val_{int|real|decimal|str} or
Item_send_datetime() do. We return the value in accordance with the
current session date flags and let the caller further convert it to TIME.
- If the caller did not specify TIME_TIME_ONLY, then return the value
according to the flags supplied by the caller.
*/
return func->get_date_native(thd, ltime,
fuzzydate & TIME_TIME_ONLY ?
sql_mode_for_dates(thd) :
fuzzydate);
}
bool Type_handler_time_common::
......
......@@ -3308,10 +3308,6 @@ class Type_handler_temporal_result: public Type_handler
Item_func_hybrid_field_type *,
MYSQL_TIME *,
date_mode_t fuzzydate) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
MYSQL_TIME *, date_mode_t fuzzydate) const;
bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
......@@ -3939,6 +3935,10 @@ class Type_handler_time_common: public Type_handler_temporal_result
MYSQL_TIME *,
date_mode_t fuzzydate) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
MYSQL_TIME *, date_mode_t fuzzydate) const;
longlong Item_func_between_val_int(Item_func_between *func) const;
......@@ -4056,6 +4056,10 @@ class Type_handler_date_common: public Type_handler_temporal_with_date
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
......@@ -4152,6 +4156,10 @@ class Type_handler_datetime_common: public Type_handler_temporal_with_date
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
......@@ -4252,6 +4260,10 @@ class Type_handler_timestamp_common: public Type_handler_temporal_with_date
String *print_item_value(THD *thd, Item *item, String *str) const;
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
double Item_func_min_max_val_real(Item_func_min_max *) const;
longlong Item_func_min_max_val_int(Item_func_min_max *) const;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *) const;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
......
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