Commit 13053fbe authored by Tor Didriksen's avatar Tor Didriksen

Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY

The table contains one time value: '00:00:32'
This value is converted to timestamp by a subquery.

In convert_constant_item we call (*item)->is_null()
which triggers execution of the Item_singlerow_subselect subquery,
and the string "0000-00-00 00:00:32" is cached
by Item_cache_datetime.
We continue execution and call update_null_value, which calls val_int()
on the cached item, which converts the time value to ((longlong) 32)
Then we continue to do (*item)->save_in_field()
which ends up in Item_cache_datetime::val_str() which fails,
since (32 < 101) in number_to_datetime, and val_str() returns NULL.

Item_singlerow_subselect::val_str isnt prepared for this:
if exec() succeeds, and return !null_value, then val_str()
*must* succeed.

Solution: refuse to cache strings like "0000-00-00 00:00:32"
in Item_cache_datetime::cache_value, and return NULL instead.

This is similar to the solution for 
Bug#11766860 - 60085: CRASH IN ITEM::SAVE_IN_FIELD() WITH TIME DATA TYPE

This patch is for 5.5 only.
The issue is not present after WL#946, since a time value
will be converted to a proper timestamp, with the current date
rather than "0000-00-00"


mysql-test/r/subselect.result:
  New test case.
mysql-test/t/subselect.test:
  New test case.
sql/item.cc:
  Verify proper date format before caching timestamps.
sql/item_timefunc.cc:
  Use named constant for readability.
parent 1a2e4afe
......@@ -5113,3 +5113,12 @@ SELECT 1 FROM
1) FROM t1) AS e;
ERROR 21000: Operand should contain 1 column(s)
DROP TABLE t1;
#
# Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY
#
CREATE TABLE t1(a TIME NOT NULL);
INSERT INTO t1 VALUES ('00:00:32');
SELECT 1 FROM t1 WHERE a >
(SELECT timestamp(a) AS a FROM t1);
1
DROP TABLE t1;
......@@ -4067,4 +4067,14 @@ SELECT 1 FROM
DROP TABLE t1;
--echo #
--echo # Bug#13721076 CRASH WITH TIME TYPE/TIMESTAMP() AND WARNINGS IN SUBQUERY
--echo #
CREATE TABLE t1(a TIME NOT NULL);
INSERT INTO t1 VALUES ('00:00:32');
SELECT 1 FROM t1 WHERE a >
(SELECT timestamp(a) AS a FROM t1);
DROP TABLE t1;
......@@ -7647,6 +7647,33 @@ bool Item_cache_datetime::cache_value()
str_value.copy(*res);
null_value= example->null_value;
unsigned_flag= example->unsigned_flag;
if (!null_value)
{
switch(field_type())
{
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME ltime;
int was_cut;
const timestamp_type tt=
str_to_datetime(str_value.charset(),
str_value.ptr(),
str_value.length(),
&ltime,
TIME_DATETIME_ONLY,
&was_cut);
if (tt != MYSQL_TIMESTAMP_DATETIME || was_cut)
null_value= true;
else
my_datetime_to_str(&ltime, const_cast<char*>(str_value.ptr()));
}
default:
{}
}
}
return TRUE;
}
......
......@@ -2694,7 +2694,7 @@ longlong Item_datetime_typecast::val_int()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
if (get_arg0_date(&ltime,1))
if (get_arg0_date(&ltime, TIME_FUZZY_DATE))
{
null_value= 1;
return 0;
......
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