Skip to content

Commit

Permalink
MDEV-15293 CAST(AS TIME) returns bad results for LAST_VALUE(),NAME_CO…
Browse files Browse the repository at this point in the history
…NST(),SP variable
  • Loading branch information
Alexander Barkov committed Feb 13, 2018
1 parent 0c4aeef commit 95d075a
Show file tree
Hide file tree
Showing 16 changed files with 418 additions and 60 deletions.
42 changes: 42 additions & 0 deletions mysql-test/r/type_time.result
Original file line number Diff line number Diff line change
Expand Up @@ -1487,3 +1487,45 @@ lt_minus200_implicit 200
lt_minus200_explictit 200
lt_plus200_implicit 10
lt_plus200_explicit 10
#
# MDEV-15293 CAST(AS TIME) returns bad results for LAST_VALUE(),NAME_CONST(),SP variable
#
SELECT CAST(DATE'2001-01-01' AS TIME);
CAST(DATE'2001-01-01' AS TIME)
00:00:00
SELECT CAST(LAST_VALUE(DATE'2001-01-01') AS TIME);
CAST(LAST_VALUE(DATE'2001-01-01') AS TIME)
00:00:00
SELECT CAST(NAME_CONST('name',DATE'2001-01-01') AS TIME);
CAST(NAME_CONST('name',DATE'2001-01-01') AS TIME)
00:00:00
BEGIN NOT ATOMIC
DECLARE a DATE DEFAULT '2001-01-01';
SELECT CAST(a AS TIME);
END;
$$
CAST(a AS TIME)
00:00:00
CREATE OR REPLACE TABLE t1 (dt DATE,country VARCHAR(10), amount INT);
INSERT INTO t1 VALUES ('2000-01-01','DE',102);
SELECT
dt, country, amount,
FIRST_VALUE(dt) OVER () AS first,
MINUTE(FIRST_VALUE(dt) OVER ()) AS m_first,
LAST_VALUE(dt) OVER () AS last,
MINUTE(LAST_VALUE(dt) OVER ()) AS m_last
FROM t1
ORDER BY country, dt;
dt country amount first m_first last m_last
2000-01-01 DE 102 2000-01-01 0 2000-01-01 0
SELECT
dt, country, amount,
FIRST_VALUE(dt) OVER () AS first,
CAST(FIRST_VALUE(dt) OVER () AS TIME) AS t_first,
LAST_VALUE(dt) OVER () AS last,
CAST(LAST_VALUE(dt) OVER () AS TIME) AS t_last
FROM t1
ORDER BY country, dt;
dt country amount first t_first last t_last
2000-01-01 DE 102 2000-01-01 00:00:00 2000-01-01 00:00:00
DROP TABLE t1;
36 changes: 36 additions & 0 deletions mysql-test/t/type_time.test
Original file line number Diff line number Diff line change
Expand Up @@ -866,3 +866,39 @@ SELECT
HOUR(LEAST(CAST('2010-01-01 10:10:10' AS TIME(6)),TIME('200:20:20'))) AS lt_plus200_explicit;

--horizontal_results


--echo #
--echo # MDEV-15293 CAST(AS TIME) returns bad results for LAST_VALUE(),NAME_CONST(),SP variable
--echo #

SELECT CAST(DATE'2001-01-01' AS TIME);
SELECT CAST(LAST_VALUE(DATE'2001-01-01') AS TIME);
SELECT CAST(NAME_CONST('name',DATE'2001-01-01') AS TIME);
DELIMITER $$;
BEGIN NOT ATOMIC
DECLARE a DATE DEFAULT '2001-01-01';
SELECT CAST(a AS TIME);
END;
$$
DELIMITER ;$$

CREATE OR REPLACE TABLE t1 (dt DATE,country VARCHAR(10), amount INT);
INSERT INTO t1 VALUES ('2000-01-01','DE',102);
SELECT
dt, country, amount,
FIRST_VALUE(dt) OVER () AS first,
MINUTE(FIRST_VALUE(dt) OVER ()) AS m_first,
LAST_VALUE(dt) OVER () AS last,
MINUTE(LAST_VALUE(dt) OVER ()) AS m_last
FROM t1
ORDER BY country, dt;
SELECT
dt, country, amount,
FIRST_VALUE(dt) OVER () AS first,
CAST(FIRST_VALUE(dt) OVER () AS TIME) AS t_first,
LAST_VALUE(dt) OVER () AS last,
CAST(LAST_VALUE(dt) OVER () AS TIME) AS t_last
FROM t1
ORDER BY country, dt;
DROP TABLE t1;
152 changes: 95 additions & 57 deletions sql/item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1454,67 +1454,73 @@ Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
As a extra convenience the time structure is reset on error or NULL values!
*/

bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
if (field_type() == MYSQL_TYPE_TIME)
fuzzydate|= TIME_TIME_ONLY;
longlong value= val_int();
bool neg= !unsigned_flag && value < 0;
if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
ltime, fuzzydate,
field_name_or_null()))
return null_value|= make_zero_date(ltime, fuzzydate);
return null_value= false;
}

switch (result_type()) {
case INT_RESULT:
{
longlong value= val_int();
bool neg= !unsigned_flag && value < 0;
if (field_type() == MYSQL_TYPE_YEAR)
{
if (max_length == 2)
{
if (value < 70)
value+= 2000;
else if (value <= 1900)
value+= 1900;
}
value*= 10000; /* make it YYYYMMHH */
}
if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
ltime, fuzzydate,
field_name_or_null()))
goto err;
break;
}
case REAL_RESULT:
{
double value= val_real();
if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
field_name_or_null()))
goto err;
break;
}
case DECIMAL_RESULT:
{
my_decimal value, *res;
if (!(res= val_decimal(&value)) ||
decimal_to_datetime_with_warn(res, ltime, fuzzydate,
field_name_or_null()))
goto err;
break;
}
case STRING_RESULT:

bool Item::get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
longlong value= val_int();
DBUG_ASSERT(unsigned_flag || value >= 0);
if (max_length == 2)
{
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate))
goto err;
break;
}
default:
DBUG_ASSERT(0);
if (value < 70)
value+= 2000;
else if (value <= 1900)
value+= 1900;
}
value*= 10000; /* make it YYYYMMHH */
if (null_value || int_to_datetime_with_warn(false, value,
ltime, fuzzydate,
field_name_or_null()))
return null_value|= make_zero_date(ltime, fuzzydate);
return null_value= false;
}

return null_value= 0;

err:
bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
double value= val_real();
if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
field_name_or_null()))
return null_value|= make_zero_date(ltime, fuzzydate);
return null_value= false;
}


bool Item::get_date_from_decimal(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
my_decimal value, *res;
if (!(res= val_decimal(&value)) ||
decimal_to_datetime_with_warn(res, ltime, fuzzydate,
field_name_or_null()))
return null_value|= make_zero_date(ltime, fuzzydate);
return null_value= false;
}


bool Item::get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate))
return null_value|= make_zero_date(ltime, fuzzydate);
return null_value= false;
}


bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
/*
if the item was not null and convertion failed, we return a zero date
if allowed, otherwise - null.
Expand All @@ -1536,7 +1542,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
*/
ltime->time_type= MYSQL_TIMESTAMP_TIME;
}
return null_value|= !(fuzzydate & TIME_FUZZY_DATES);
return !(fuzzydate & TIME_FUZZY_DATES);
}

bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
Expand Down Expand Up @@ -1780,6 +1786,16 @@ my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value)
}


bool Item_sp_variable::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed);
Item *it= this_item();
bool val= it->get_date(ltime, fuzzydate);
null_value= it->null_value;
return val;
}


bool Item_sp_variable::is_null()
{
return this_item()->is_null();
Expand Down Expand Up @@ -2125,6 +2141,13 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value)
return val;
}

bool Item_name_const::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(fixed);
bool rc= value_item->get_date(ltime, fuzzydate);
null_value= value_item->null_value;
return rc;
}

bool Item_name_const::is_null()
{
Expand Down Expand Up @@ -3731,6 +3754,15 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value)
}


bool Item_null::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
make_zero_date(ltime, fuzzydate);
return (null_value= true);
}


Item *Item_null::safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
return this;
Expand Down Expand Up @@ -4201,7 +4233,7 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
*res= value.time;
return 0;
}
return Item::get_date(res, fuzzydate);
return type_handler()->Item_get_date(this, res, fuzzydate);
}


Expand Down Expand Up @@ -10191,6 +10223,12 @@ String *Item_type_holder::val_str(String*)
return 0;
}

bool Item_type_holder::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
DBUG_ASSERT(0); // should never be called
return true;
}

void Item_result_field::cleanup()
{
DBUG_ENTER("Item_result_field::cleanup()");
Expand Down
Loading

0 comments on commit 95d075a

Please sign in to comment.