MDEV-5969 Crash in prepared statement with NO_ZERO_IN_DATE and ROLLUP
MDEV-5971 Asymmetry between CAST(DATE'2001-00-00') to INT and TO CHAR in prepared statements Consistently set maybe_null flag, even not-NULL temporal literal may become NULL in the restrictive sql_mode.
This commit is contained in:
parent
b37157e49e
commit
73f4861575
@ -472,3 +472,144 @@ a
|
|||||||
10:20:30.123456
|
10:20:30.123456
|
||||||
10:20:30.123456
|
10:20:30.123456
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# MDEV-5969 Crash in prepared statement with NO_ZERO_IN_DATE and ROLLUP
|
||||||
|
#
|
||||||
|
CREATE TABLE t1
|
||||||
|
(
|
||||||
|
year INT NOT NULL,
|
||||||
|
product VARCHAR(32) NOT NULL,
|
||||||
|
profit INT
|
||||||
|
);
|
||||||
|
INSERT INTO t1 VALUES ('2001','car',101);
|
||||||
|
INSERT INTO t1 VALUES ('2001','gas',102);
|
||||||
|
INSERT INTO t1 VALUES ('2001','toy',103);
|
||||||
|
INSERT INTO t1 VALUES ('2002','car',201);
|
||||||
|
INSERT INTO t1 VALUES ('2002','gas',202);
|
||||||
|
INSERT INTO t1 VALUES ('2002','toy',203);
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT DATE'2001-00-00' AS c,year, SUM(profit) FROM t1 GROUP BY c,year WITH ROLLUP";
|
||||||
|
EXECUTE stmt;
|
||||||
|
c year SUM(profit)
|
||||||
|
2001-00-00 2001 306
|
||||||
|
2001-00-00 2002 606
|
||||||
|
2001-00-00 NULL 912
|
||||||
|
NULL NULL 912
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
c year SUM(profit)
|
||||||
|
NULL 2001 306
|
||||||
|
NULL 2002 606
|
||||||
|
NULL NULL 912
|
||||||
|
NULL NULL 912
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# MDEV-5971 Asymmetry between CAST(DATE'2001-00-00') to INT and TO CHAR in prepared statements
|
||||||
|
#
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS SIGNED) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
c
|
||||||
|
NULL
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS CHAR) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
c
|
||||||
|
NULL
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS DECIMAL(30,0)) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
c
|
||||||
|
NULL
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS DOUBLE) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
c
|
||||||
|
NULL
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
#
|
||||||
|
# Zero month or zero day automatically mean NULL flag, no matter SQL_MODE is.
|
||||||
|
# Only zero year is OK for NOT NULL.
|
||||||
|
#
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "CREATE TABLE t1 AS SELECT CAST(DATE'2001-00-00' AS CHAR) AS c";
|
||||||
|
EXECUTE stmt;
|
||||||
|
SHOW COLUMNS FROM t1;
|
||||||
|
Field Type Null Key Default Extra
|
||||||
|
c varchar(10) YES NULL
|
||||||
|
SELECT * FROM t1;
|
||||||
|
c
|
||||||
|
2001-00-00
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Incorrect datetime value: '2001-00-00'
|
||||||
|
SHOW COLUMNS FROM t1;
|
||||||
|
Field Type Null Key Default Extra
|
||||||
|
c varchar(10) YES NULL
|
||||||
|
SELECT * FROM t1;
|
||||||
|
c
|
||||||
|
NULL
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
CREATE TABLE t1 AS SELECT
|
||||||
|
DATE'2001-01-01',
|
||||||
|
DATE'0000-01-01',
|
||||||
|
DATE'2001-00-00',
|
||||||
|
DATE'2001-00-01',
|
||||||
|
DATE'2001-01-00';
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`DATE'2001-01-01'` date NOT NULL DEFAULT '0000-00-00',
|
||||||
|
`DATE'0000-01-01'` date NOT NULL DEFAULT '0000-00-00',
|
||||||
|
`DATE'2001-00-00'` date DEFAULT NULL,
|
||||||
|
`DATE'2001-00-01'` date DEFAULT NULL,
|
||||||
|
`DATE'2001-01-00'` date DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 AS SELECT
|
||||||
|
TIMESTAMP'2001-01-01 00:00:00',
|
||||||
|
TIMESTAMP'0000-01-01 00:00:00',
|
||||||
|
TIMESTAMP'2001-00-00 00:00:00',
|
||||||
|
TIMESTAMP'2001-00-01 00:00:00',
|
||||||
|
TIMESTAMP'2001-01-00 00:00:00';
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`TIMESTAMP'2001-01-01 00:00:00'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||||
|
`TIMESTAMP'0000-01-01 00:00:00'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||||
|
`TIMESTAMP'2001-00-00 00:00:00'` datetime DEFAULT NULL,
|
||||||
|
`TIMESTAMP'2001-00-01 00:00:00'` datetime DEFAULT NULL,
|
||||||
|
`TIMESTAMP'2001-01-00 00:00:00'` datetime DEFAULT NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -247,3 +247,82 @@ INSERT INTO t1 VALUES (TIME'10:20:30.1234567');
|
|||||||
INSERT INTO t1 VALUES (TIME('10:20:30.1234567'));
|
INSERT INTO t1 VALUES (TIME('10:20:30.1234567'));
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-5969 Crash in prepared statement with NO_ZERO_IN_DATE and ROLLUP
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t1
|
||||||
|
(
|
||||||
|
year INT NOT NULL,
|
||||||
|
product VARCHAR(32) NOT NULL,
|
||||||
|
profit INT
|
||||||
|
);
|
||||||
|
INSERT INTO t1 VALUES ('2001','car',101);
|
||||||
|
INSERT INTO t1 VALUES ('2001','gas',102);
|
||||||
|
INSERT INTO t1 VALUES ('2001','toy',103);
|
||||||
|
INSERT INTO t1 VALUES ('2002','car',201);
|
||||||
|
INSERT INTO t1 VALUES ('2002','gas',202);
|
||||||
|
INSERT INTO t1 VALUES ('2002','toy',203);
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT DATE'2001-00-00' AS c,year, SUM(profit) FROM t1 GROUP BY c,year WITH ROLLUP";
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-5971 Asymmetry between CAST(DATE'2001-00-00') to INT and TO CHAR in prepared statements
|
||||||
|
--echo #
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS SIGNED) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS CHAR) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS DECIMAL(30,0)) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "SELECT CAST(DATE'2001-00-00' AS DOUBLE) AS c";
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Zero month or zero day automatically mean NULL flag, no matter SQL_MODE is.
|
||||||
|
--echo # Only zero year is OK for NOT NULL.
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
PREPARE stmt FROM "CREATE TABLE t1 AS SELECT CAST(DATE'2001-00-00' AS CHAR) AS c";
|
||||||
|
EXECUTE stmt;
|
||||||
|
SHOW COLUMNS FROM t1;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET sql_mode='no_zero_in_date';
|
||||||
|
EXECUTE stmt;
|
||||||
|
SHOW COLUMNS FROM t1;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET sql_mode=DEFAULT;
|
||||||
|
|
||||||
|
CREATE TABLE t1 AS SELECT
|
||||||
|
DATE'2001-01-01',
|
||||||
|
DATE'0000-01-01',
|
||||||
|
DATE'2001-00-00',
|
||||||
|
DATE'2001-00-01',
|
||||||
|
DATE'2001-01-00';
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
CREATE TABLE t1 AS SELECT
|
||||||
|
TIMESTAMP'2001-01-01 00:00:00',
|
||||||
|
TIMESTAMP'0000-01-01 00:00:00',
|
||||||
|
TIMESTAMP'2001-00-00 00:00:00',
|
||||||
|
TIMESTAMP'2001-00-01 00:00:00',
|
||||||
|
TIMESTAMP'2001-01-00 00:00:00';
|
||||||
|
SHOW CREATE TABLE t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
29
sql/item.cc
29
sql/item.cc
@ -341,12 +341,29 @@ String *Item::val_string_from_decimal(String *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
All val_xxx_from_date() must call this method, to expose consistent behaviour
|
||||||
|
regarding SQL_MODE when converting DATE/DATETIME to other data types.
|
||||||
|
*/
|
||||||
|
bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime)
|
||||||
|
{
|
||||||
|
return get_date(ltime, field_type() == MYSQL_TYPE_TIME
|
||||||
|
? TIME_TIME_ONLY
|
||||||
|
: sql_mode_for_dates(current_thd));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Item::is_null_from_temporal()
|
||||||
|
{
|
||||||
|
MYSQL_TIME ltime;
|
||||||
|
return get_temporal_with_sql_mode(<ime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String *Item::val_string_from_date(String *str)
|
String *Item::val_string_from_date(String *str)
|
||||||
{
|
{
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (get_date(<ime, field_type() == MYSQL_TYPE_TIME
|
if (get_temporal_with_sql_mode(<ime) ||
|
||||||
? TIME_TIME_ONLY
|
|
||||||
: sql_mode_for_dates(current_thd)) ||
|
|
||||||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
str->alloc(MAX_DATE_STRING_REP_LENGTH))
|
||||||
{
|
{
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
@ -403,7 +420,7 @@ my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value)
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (get_date(<ime, sql_mode_for_dates(current_thd)))
|
if (get_temporal_with_sql_mode(<ime))
|
||||||
{
|
{
|
||||||
my_decimal_set_zero(decimal_value);
|
my_decimal_set_zero(decimal_value);
|
||||||
null_value= 1; // set NULL, stop processing
|
null_value= 1; // set NULL, stop processing
|
||||||
@ -430,7 +447,7 @@ longlong Item::val_int_from_date()
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (get_date(<ime, 0))
|
if (get_temporal_with_sql_mode(<ime))
|
||||||
return 0;
|
return 0;
|
||||||
longlong v= TIME_to_ulonglong(<ime);
|
longlong v= TIME_to_ulonglong(<ime);
|
||||||
return ltime.neg ? -v : v;
|
return ltime.neg ? -v : v;
|
||||||
@ -441,7 +458,7 @@ double Item::val_real_from_date()
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
MYSQL_TIME ltime;
|
MYSQL_TIME ltime;
|
||||||
if (get_date(<ime, 0))
|
if (get_temporal_with_sql_mode(<ime))
|
||||||
return 0;
|
return 0;
|
||||||
return TIME_to_double(<ime);
|
return TIME_to_double(<ime);
|
||||||
}
|
}
|
||||||
|
18
sql/item.h
18
sql/item.h
@ -971,6 +971,11 @@ public:
|
|||||||
double val_real_from_decimal();
|
double val_real_from_decimal();
|
||||||
double val_real_from_date();
|
double val_real_from_date();
|
||||||
|
|
||||||
|
// Get TIME, DATE or DATETIME using proper sql_mode flags for the field type
|
||||||
|
bool get_temporal_with_sql_mode(MYSQL_TIME *ltime);
|
||||||
|
// Check NULL value for a TIME, DATE or DATETIME expression
|
||||||
|
bool is_null_from_temporal();
|
||||||
|
|
||||||
int save_time_in_field(Field *field);
|
int save_time_in_field(Field *field);
|
||||||
int save_date_in_field(Field *field);
|
int save_date_in_field(Field *field);
|
||||||
int save_str_value_in_field(Field *field, String *result);
|
int save_str_value_in_field(Field *field, String *result);
|
||||||
@ -2933,6 +2938,9 @@ public:
|
|||||||
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
|
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
|
||||||
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
|
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
|
||||||
|
|
||||||
|
bool is_null()
|
||||||
|
{ return is_null_from_temporal(); }
|
||||||
|
bool get_date_with_sql_mode(MYSQL_TIME *to);
|
||||||
String *val_str(String *str)
|
String *val_str(String *str)
|
||||||
{ return val_string_from_date(str); }
|
{ return val_string_from_date(str); }
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
@ -2959,6 +2967,14 @@ public:
|
|||||||
{
|
{
|
||||||
max_length= MAX_DATE_WIDTH;
|
max_length= MAX_DATE_WIDTH;
|
||||||
fixed= 1;
|
fixed= 1;
|
||||||
|
/*
|
||||||
|
If date has zero month or day, it can return NULL in case of
|
||||||
|
NO_ZERO_DATE or NO_ZERO_IN_DATE.
|
||||||
|
We can't just check the current sql_mode here in constructor,
|
||||||
|
because sql_mode can change in case of prepared statements
|
||||||
|
between PREPARE and EXECUTE.
|
||||||
|
*/
|
||||||
|
maybe_null= !ltime->month || !ltime->day;
|
||||||
}
|
}
|
||||||
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
|
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
|
||||||
void print(String *str, enum_query_type query_type);
|
void print(String *str, enum_query_type query_type);
|
||||||
@ -2995,6 +3011,8 @@ public:
|
|||||||
{
|
{
|
||||||
max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
|
max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
|
||||||
fixed= 1;
|
fixed= 1;
|
||||||
|
// See the comment on maybe_null in Item_date_literal
|
||||||
|
maybe_null= !ltime->month || !ltime->day;
|
||||||
}
|
}
|
||||||
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
|
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
|
||||||
void print(String *str, enum_query_type query_type);
|
void print(String *str, enum_query_type query_type);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user