Commit 7f161a5c authored by Alexander Barkov's avatar Alexander Barkov

MDEV-34088 The TIMESTAMP value of '1970-01-01 00:00:00' can be indirectly inserted in strict mode

In strict mode a timestamp(0) column could be directly assigned from
another timestamp(N>0) column with the value '1970-01-01 00:00:00.1'
(at time zone '+00:00'), or with any other value '1970-01-01 00:00:00.XXXXXX'
with non-zero microsecond value XXXXXX.
This assignment happened silently without warnings or errors.

It worked as follows:

- The value {tv_sec=0, tv_usec=100000}, which is '1970-01-01 00:00:00.1'
  was rounded to {tv_sec=0, tv_usec=0}, which is '1970-01-01 00:00:00.0'

- Then {tv_sec=0, tv_usec=0} was silently re-interpreted as zero datetime.

After the fix this assignment always raises a warning,
which in case of the strict mode is escalated to an error.

The problem in this scenario is that '1970-01-01 00:00:00' cannot be stored,
because its timeval value {tv_sec=0, tv_usec=0} is reserved for zero datetimes.
Thus the warning should be raised no matter if sql_mode allows or disallows
zero dates.
parent 2c198770
......@@ -1409,5 +1409,80 @@ c1 c2
DROP TABLE t1;
SET time_zone=DEFAULT;
#
# MDEV-34088 The TIMESTAMP value of '1970-01-01 00:00:00' can be indirectly inserted in strict mode
#
SET sql_mode='STRICT_TRANS_TABLES,STRICT_ALL_TABLES';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00' for column `test`.`t1`.`a` at row 1
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00.1' for column `test`.`t1`.`a` at row 1
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00 +00:00' for column `test`.`t1`.`a` at row 1
DROP TABLE t2;
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00' for column `test`.`t1`.`a` at row 1
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00.1' for column `test`.`t1`.`a` at row 1
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
ERROR 22007: Incorrect datetime value: '1970-01-01 00:00:00 +00:00' for column `test`.`t1`.`a` at row 1
DROP TABLE t2;
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
DROP TABLE t2;
SELECT * FROM t1;
a
0000-00-00 00:00:00
0000-00-00 00:00:00
0000-00-00 00:00:00
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
DROP TABLE t2;
SELECT * FROM t1;
a
0000-00-00 00:00:00
0000-00-00 00:00:00
0000-00-00 00:00:00
DROP TABLE t1;
SET sql_mode=DEFAULT;
#
# End of 10.5 tests
#
......@@ -951,6 +951,67 @@ SELECT unix_timestamp(a) AS c1, unix_timestamp(coalesce(a)) AS c2 FROM t1;
DROP TABLE t1;
SET time_zone=DEFAULT;
--echo #
--echo # MDEV-34088 The TIMESTAMP value of '1970-01-01 00:00:00' can be indirectly inserted in strict mode
--echo #
SET sql_mode='STRICT_TRANS_TABLES,STRICT_ALL_TABLES';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 SELECT a FROM t2;
DROP TABLE t2;
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
--error ER_TRUNCATED_WRONG_VALUE
INSERT INTO t1 SELECT a FROM t2;
DROP TABLE t2;
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
DROP TABLE t2;
SELECT * FROM t1;
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET sql_mode='NO_ZERO_DATE,NO_ZERO_IN_DATE';
SET time_zone='+00:00';
CREATE TABLE t1 (a TIMESTAMP);
INSERT INTO t1 VALUES ('1970-01-01 00:00:00');
INSERT INTO t1 VALUES ('1970-01-01 00:00:00.1');
CREATE TABLE t2 (a TIMESTAMP(1)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('1970-01-01 00:00:00.1');
INSERT INTO t1 SELECT a FROM t2;
DROP TABLE t2;
SELECT * FROM t1;
DROP TABLE t1;
SET sql_mode=DEFAULT;
--echo #
--echo # End of 10.5 tests
--echo #
......@@ -5307,11 +5307,33 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
}
int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec)
int Field_timestamp::store_timestamp_dec(const timeval &tv, uint dec)
{
int warn= 0;
time_round_mode_t mode= Datetime::default_round_mode(get_thd());
store_TIMESTAMP(Timestamp(ts).round(decimals(), mode, &warn));
const Timestamp ts= Timestamp(tv).round(decimals(), mode, &warn);
store_TIMESTAMP(ts);
if (ts.tv().tv_sec == 0 && ts.tv().tv_usec == 0)
{
/*
The value {tv_sec==0, tv_usec==0} here means '1970-01-01 00:00:00 +00'.
It does not mean zero datetime! because store_timestamp_dec() knows
nothing about zero dates. It inserts only real timeval values.
Zero ts={0,0} here is possible in two scenarios:
- the passed tv was already {0,0} meaning '1970-01-01 00:00:00 +00'
- the passed tv had some microseconds but they were rounded/truncated
to zero: '1970-01-01 00:00:00.1 +00' -> '1970-01-01 00:00:00 +00'.
It does not matter whether rounding/truncation really happened.
In both cases the call for store_TIMESTAMP(ts) above re-interpreted
'1970-01-01 00:00:00 +00:00' to zero date. Return 1 no matter what
sql_mode is. Even if sql_mode allows zero dates, there is still a problem
here: '1970-01-01 00:00:00 +00' could not be stored as-is!
*/
ErrConvString str(STRING_WITH_LEN("1970-01-01 00:00:00 +00:00"),
system_charset_info);
set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE, &str, "datetime", 1);
return 1; // '1970-01-01 00:00:00 +00' was converted to a zero date
}
if (warn)
{
/*
......@@ -5325,9 +5347,6 @@ int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec)
*/
set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
}
if (ts.tv_sec == 0 && ts.tv_usec == 0 &&
get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE)
return zero_time_stored_return_code_with_warning();
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