diff options
59 files changed, 4771 insertions, 395 deletions
diff --git a/include/my_time.h b/include/my_time.h index 94632bbbf38..27011be8b47 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -108,12 +108,14 @@ typedef struct st_mysql_time_status { int warnings; uint precision; + uint nanoseconds; } MYSQL_TIME_STATUS; static inline void my_time_status_init(MYSQL_TIME_STATUS *status) { status->warnings= 0; status->precision= 0; + status->nanoseconds= 0; } my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, diff --git a/mysql-test/main/func_time_round.result b/mysql-test/main/func_time_round.result new file mode 100644 index 00000000000..208baae8cf9 --- /dev/null +++ b/mysql-test/main/func_time_round.result @@ -0,0 +1,1374 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; +CREATE TABLE t1_datetime_in_varchar (id SERIAL, a VARCHAR(64)); +INSERT INTO t1_datetime_in_varchar (a) VALUES +('2000-12-31 23:59:59'), +('2000-12-31 23:59:59.9'), +('2000-12-31 23:59:59.99'), +('2000-12-31 23:59:59.999'), +('2000-12-31 23:59:59.9999'), +('2000-12-31 23:59:59.99999'), +('2000-12-31 23:59:59.999999'), +('2000-12-31 23:59:59.9999999'); +CREATE TABLE t1_datetime_in_decimal (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_datetime_in_decimal (a) VALUES +(20001231235959), +(20001231235959.9), +(20001231235959.99), +(20001231235959.999), +(20001231235959.9999), +(20001231235959.99999), +(20001231235959.999999), +(20001231235959.9999999); +CREATE TABLE t1_time_in_varchar (id SERIAL, a VARCHAR(64)); +INSERT INTO t1_time_in_varchar (a) VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.99'), +('00:00:00.999'), +('00:00:00.9999'), +('00:00:00.99999'), +('00:00:00.999999'), +('00:00:00.9999999'); +INSERT INTO t1_time_in_varchar (a) VALUES +('837:59:59.9999999'), +('838:59:59'), +('838:59:59.9'), +('838:59:59.99'), +('838:59:59.999'), +('838:59:59.9999'), +('838:59:59.99999'), +('838:59:59.999999'), +('838:59:59.9999999'), +('839:59:59.9999999'), +('87649414:59:59.999999'), +('87649414:59:59.9999999'), +('87649415:59:59.999999'), +('87649415:59:59.9999999'); +CREATE TABLE t1_time_in_decimal (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_time_in_decimal (a) VALUES +(0), +(0.9), +(0.99), +(0.999), +(0.9999), +(0.99999), +(0.999999), +(0.9999999); +INSERT INTO t1_time_in_decimal (a) VALUES +(8375959.9999999), +(8385959), +(8385959.9), +(8385959.99), +(8385959.999), +(8385959.9999), +(8385959.99999), +(8385959.999999), +(8385959.9999999), +(8395959.9999999), +(876494145959.999999), +(876494145959.9999999), +(876494155959.999999), +(876494155959.9999999); +# +# TIME: LEAST/GREATEST +# +SELECT GREATEST(TIME'00:00:00', a) FROM t1_time_in_varchar; +GREATEST(TIME'00:00:00', a) +00:00:00.000000 +00:00:00.900000 +00:00:00.990000 +00:00:00.999000 +00:00:00.999900 +00:00:00.999990 +00:00:00.999999 +00:00:01.000000 +838:00:00.000000 +838:59:59.000000 +838:59:59.900000 +838:59:59.990000 +838:59:59.999000 +838:59:59.999900 +838:59:59.999990 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Warning 1292 Truncated incorrect time value: '838:59:59.9999999' +Warning 1292 Truncated incorrect time value: '839:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +SELECT GREATEST(TIME'00:00:00', a) FROM t1_time_in_decimal; +GREATEST(TIME'00:00:00', a) +00:00:00.000000 +00:00:00.900000 +00:00:00.990000 +00:00:00.999000 +00:00:00.999900 +00:00:00.999990 +00:00:00.999999 +00:00:01.000000 +838:00:00.000000 +838:59:59.000000 +838:59:59.900000 +838:59:59.990000 +838:59:59.999000 +838:59:59.999900 +838:59:59.999990 +838:59:59.999999 +838:59:59.999999 +838:59:59.999999 +NULL +NULL +NULL +NULL +Warnings: +Warning 1292 Incorrect time value: '8385959.9999999000' for column 'a' at row 17 +Warning 1292 Incorrect time value: '8395959.9999999000' for column 'a' at row 18 +Warning 1292 Incorrect time value: '876494145959.9999990000' for column 'a' at row 19 +Warning 1292 Incorrect time value: '876494145959.9999999000' for column 'a' at row 20 +Warning 1292 Incorrect time value: '876494155959.9999990000' for column 'a' at row 21 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +SELECT GREATEST(TIME'00:00:00', '00:00:00.0000004'); +GREATEST(TIME'00:00:00', '00:00:00.0000004') +00:00:00.000000 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.0000004' +SELECT GREATEST(TIME'00:00:00', 0.0000004); +GREATEST(TIME'00:00:00', 0.0000004) +00:00:00.000000 +SELECT GREATEST(TIME'00:00:00', '00:00:00.0000005'); +GREATEST(TIME'00:00:00', '00:00:00.0000005') +00:00:00.000001 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.0000005' +SELECT GREATEST(TIME'00:00:00', 0.0000005); +GREATEST(TIME'00:00:00', 0.0000005) +00:00:00.000001 +# +# Functions with a single TIME input, conversion from DATETIME-in-VARCHAR +# +SELECT SECOND(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SECOND(a) CAST(a AS TIME(6)) a +59 23:59:59.000000 2000-12-31 23:59:59 +59 23:59:59.900000 2000-12-31 23:59:59.9 +59 23:59:59.990000 2000-12-31 23:59:59.99 +59 23:59:59.999000 2000-12-31 23:59:59.999 +59 23:59:59.999900 2000-12-31 23:59:59.9999 +59 23:59:59.999990 2000-12-31 23:59:59.99999 +59 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT MINUTE(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +MINUTE(a) CAST(a AS TIME(6)) a +59 23:59:59.000000 2000-12-31 23:59:59 +59 23:59:59.900000 2000-12-31 23:59:59.9 +59 23:59:59.990000 2000-12-31 23:59:59.99 +59 23:59:59.999000 2000-12-31 23:59:59.999 +59 23:59:59.999900 2000-12-31 23:59:59.9999 +59 23:59:59.999990 2000-12-31 23:59:59.99999 +59 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT HOUR(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +HOUR(a) CAST(a AS TIME(6)) a +23 23:59:59.000000 2000-12-31 23:59:59 +23 23:59:59.900000 2000-12-31 23:59:59.9 +23 23:59:59.990000 2000-12-31 23:59:59.99 +23 23:59:59.999000 2000-12-31 23:59:59.999 +23 23:59:59.999900 2000-12-31 23:59:59.9999 +23 23:59:59.999990 2000-12-31 23:59:59.99999 +23 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT EXTRACT(SECOND FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +EXTRACT(SECOND FROM a) CAST(a AS TIME(6)) a +59 23:59:59.000000 2000-12-31 23:59:59 +59 23:59:59.900000 2000-12-31 23:59:59.9 +59 23:59:59.990000 2000-12-31 23:59:59.99 +59 23:59:59.999000 2000-12-31 23:59:59.999 +59 23:59:59.999900 2000-12-31 23:59:59.9999 +59 23:59:59.999990 2000-12-31 23:59:59.99999 +59 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT EXTRACT(MINUTE FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +EXTRACT(MINUTE FROM a) CAST(a AS TIME(6)) a +59 23:59:59.000000 2000-12-31 23:59:59 +59 23:59:59.900000 2000-12-31 23:59:59.9 +59 23:59:59.990000 2000-12-31 23:59:59.99 +59 23:59:59.999000 2000-12-31 23:59:59.999 +59 23:59:59.999900 2000-12-31 23:59:59.9999 +59 23:59:59.999990 2000-12-31 23:59:59.99999 +59 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT EXTRACT(HOUR FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +EXTRACT(HOUR FROM a) CAST(a AS TIME(6)) a +23 23:59:59.000000 2000-12-31 23:59:59 +23 23:59:59.900000 2000-12-31 23:59:59.9 +23 23:59:59.990000 2000-12-31 23:59:59.99 +23 23:59:59.999000 2000-12-31 23:59:59.999 +23 23:59:59.999900 2000-12-31 23:59:59.9999 +23 23:59:59.999990 2000-12-31 23:59:59.99999 +23 23:59:59.999999 2000-12-31 23:59:59.999999 +0 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT TIME_TO_SEC(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +TIME_TO_SEC(a) CAST(a AS TIME(6)) a +86399.000000 23:59:59.000000 2000-12-31 23:59:59 +86399.900000 23:59:59.900000 2000-12-31 23:59:59.9 +86399.990000 23:59:59.990000 2000-12-31 23:59:59.99 +86399.999000 23:59:59.999000 2000-12-31 23:59:59.999 +86399.999900 23:59:59.999900 2000-12-31 23:59:59.9999 +86399.999990 23:59:59.999990 2000-12-31 23:59:59.99999 +86399.999999 23:59:59.999999 2000-12-31 23:59:59.999999 +0.000000 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +# +# Functions with a single TIME input, conversion from DATETIME-in-DECIMAL +# +SELECT SECOND(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SECOND(a) CAST(a AS TIME(6)) a +59 23:59:59.000000 20001231235959.0000000000 +59 23:59:59.900000 20001231235959.9000000000 +59 23:59:59.990000 20001231235959.9900000000 +59 23:59:59.999000 20001231235959.9990000000 +59 23:59:59.999900 20001231235959.9999000000 +59 23:59:59.999990 20001231235959.9999900000 +59 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT MINUTE(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +MINUTE(a) CAST(a AS TIME(6)) a +59 23:59:59.000000 20001231235959.0000000000 +59 23:59:59.900000 20001231235959.9000000000 +59 23:59:59.990000 20001231235959.9900000000 +59 23:59:59.999000 20001231235959.9990000000 +59 23:59:59.999900 20001231235959.9999000000 +59 23:59:59.999990 20001231235959.9999900000 +59 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT HOUR(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +HOUR(a) CAST(a AS TIME(6)) a +23 23:59:59.000000 20001231235959.0000000000 +23 23:59:59.900000 20001231235959.9000000000 +23 23:59:59.990000 20001231235959.9900000000 +23 23:59:59.999000 20001231235959.9990000000 +23 23:59:59.999900 20001231235959.9999000000 +23 23:59:59.999990 20001231235959.9999900000 +23 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT EXTRACT(SECOND FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +EXTRACT(SECOND FROM a) CAST(a AS TIME(6)) a +59 23:59:59.000000 20001231235959.0000000000 +59 23:59:59.900000 20001231235959.9000000000 +59 23:59:59.990000 20001231235959.9900000000 +59 23:59:59.999000 20001231235959.9990000000 +59 23:59:59.999900 20001231235959.9999000000 +59 23:59:59.999990 20001231235959.9999900000 +59 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT EXTRACT(MINUTE FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +EXTRACT(MINUTE FROM a) CAST(a AS TIME(6)) a +59 23:59:59.000000 20001231235959.0000000000 +59 23:59:59.900000 20001231235959.9000000000 +59 23:59:59.990000 20001231235959.9900000000 +59 23:59:59.999000 20001231235959.9990000000 +59 23:59:59.999900 20001231235959.9999000000 +59 23:59:59.999990 20001231235959.9999900000 +59 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT EXTRACT(HOUR FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +EXTRACT(HOUR FROM a) CAST(a AS TIME(6)) a +23 23:59:59.000000 20001231235959.0000000000 +23 23:59:59.900000 20001231235959.9000000000 +23 23:59:59.990000 20001231235959.9900000000 +23 23:59:59.999000 20001231235959.9990000000 +23 23:59:59.999900 20001231235959.9999000000 +23 23:59:59.999990 20001231235959.9999900000 +23 23:59:59.999999 20001231235959.9999990000 +0 00:00:00.000000 20001231235959.9999999000 +SELECT TIME_TO_SEC(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +TIME_TO_SEC(a) CAST(a AS TIME(6)) a +86399.000000 23:59:59.000000 20001231235959.0000000000 +86399.900000 23:59:59.900000 20001231235959.9000000000 +86399.990000 23:59:59.990000 20001231235959.9900000000 +86399.999000 23:59:59.999000 20001231235959.9990000000 +86399.999900 23:59:59.999900 20001231235959.9999000000 +86399.999990 23:59:59.999990 20001231235959.9999900000 +86399.999999 23:59:59.999999 20001231235959.9999990000 +0.000000 00:00:00.000000 20001231235959.9999999000 +# +# Functions with a single TIME interval input, conversion from TIME-interval-in-VARCHAR +# +SELECT +EXTRACT(DAY FROM a), +EXTRACT(HOUR FROM a), +EXTRACT(MINUTE FROM a), +EXTRACT(SECOND FROM a), +EXTRACT(MICROSECOND FROM a), +CAST(a AS INTERVAL DAY_SECOND(6)), +a +FROM t1_time_in_varchar ORDER BY id; +EXTRACT(DAY FROM a) EXTRACT(HOUR FROM a) EXTRACT(MINUTE FROM a) EXTRACT(SECOND FROM a) EXTRACT(MICROSECOND FROM a) CAST(a AS INTERVAL DAY_SECOND(6)) a +0 0 0 0 0 00:00:00.000000 00:00:00 +0 0 0 0 900000 00:00:00.900000 00:00:00.9 +0 0 0 0 990000 00:00:00.990000 00:00:00.99 +0 0 0 0 999000 00:00:00.999000 00:00:00.999 +0 0 0 0 999900 00:00:00.999900 00:00:00.9999 +0 0 0 0 999990 00:00:00.999990 00:00:00.99999 +0 0 0 0 999999 00:00:00.999999 00:00:00.999999 +0 0 0 1 0 00:00:01.000000 00:00:00.9999999 +34 22 0 0 0 34 22:00:00.000000 837:59:59.9999999 +34 22 59 59 0 34 22:59:59.000000 838:59:59 +34 22 59 59 900000 34 22:59:59.900000 838:59:59.9 +34 22 59 59 990000 34 22:59:59.990000 838:59:59.99 +34 22 59 59 999000 34 22:59:59.999000 838:59:59.999 +34 22 59 59 999900 34 22:59:59.999900 838:59:59.9999 +34 22 59 59 999990 34 22:59:59.999990 838:59:59.99999 +34 22 59 59 999999 34 22:59:59.999999 838:59:59.999999 +34 23 0 0 0 34 23:00:00.000000 838:59:59.9999999 +35 0 0 0 0 35 00:00:00.000000 839:59:59.9999999 +3652058 22 59 59 999999 3652058 22:59:59.999999 87649414:59:59.999999 +3652058 23 0 0 0 3652058 23:00:00.000000 87649414:59:59.9999999 +3652058 23 59 59 999999 3652058 23:59:59.999999 87649415:59:59.999999 +3652058 23 59 59 999999 3652058 23:59:59.999999 87649415:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '838:59:59.9999999' +Note 1292 Truncated incorrect time value: '838:59:59.9999999' +Note 1292 Truncated incorrect time value: '838:59:59.9999999' +Note 1292 Truncated incorrect time value: '838:59:59.9999999' +Note 1292 Truncated incorrect time value: '838:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '838:59:59.9999999' +Note 1292 Truncated incorrect time value: '839:59:59.9999999' +Note 1292 Truncated incorrect time value: '839:59:59.9999999' +Note 1292 Truncated incorrect time value: '839:59:59.9999999' +Note 1292 Truncated incorrect time value: '839:59:59.9999999' +Note 1292 Truncated incorrect time value: '839:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '839:59:59.9999999' +Note 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Note 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Note 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Note 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Note 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '87649414:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Incorrect INTERVAL DAY TO SECOND value: '87649415:59:59.9999999' +SELECT +TIME_TO_SEC(a), +CAST(a AS TIME(6)), +a +FROM t1_time_in_varchar ORDER BY id; +TIME_TO_SEC(a) CAST(a AS TIME(6)) a +0.000000 00:00:00.000000 00:00:00 +0.900000 00:00:00.900000 00:00:00.9 +0.990000 00:00:00.990000 00:00:00.99 +0.999000 00:00:00.999000 00:00:00.999 +0.999900 00:00:00.999900 00:00:00.9999 +0.999990 00:00:00.999990 00:00:00.99999 +0.999999 00:00:00.999999 00:00:00.999999 +1.000000 00:00:01.000000 00:00:00.9999999 +3016800.000000 838:00:00.000000 837:59:59.9999999 +3020399.000000 838:59:59.000000 838:59:59 +3020399.900000 838:59:59.900000 838:59:59.9 +3020399.990000 838:59:59.990000 838:59:59.99 +3020399.999000 838:59:59.999000 838:59:59.999 +3020399.999900 838:59:59.999900 838:59:59.9999 +3020399.999990 838:59:59.999990 838:59:59.99999 +3020399.999999 838:59:59.999999 838:59:59.999999 +3020399.999999 838:59:59.999999 838:59:59.9999999 +3020399.999999 838:59:59.999999 839:59:59.9999999 +3020399.999999 838:59:59.999999 87649414:59:59.999999 +3020399.999999 838:59:59.999999 87649414:59:59.9999999 +3020399.999999 838:59:59.999999 87649415:59:59.999999 +3020399.999999 838:59:59.999999 87649415:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Note 1292 Truncated incorrect time value: '837:59:59.9999999' +Warning 1292 Truncated incorrect time value: '838:59:59.9999999' +Warning 1292 Truncated incorrect time value: '838:59:59.9999999' +Warning 1292 Truncated incorrect time value: '839:59:59.9999999' +Warning 1292 Truncated incorrect time value: '839:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649414:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +Warning 1292 Truncated incorrect time value: '87649415:59:59.9999999' +# +# Functions with a single TIME interval input, conversion from TIME-interval-in-DECIMAL +# +SELECT +EXTRACT(DAY FROM a), +EXTRACT(HOUR FROM a), +EXTRACT(MINUTE FROM a), +EXTRACT(SECOND FROM a), +EXTRACT(MICROSECOND FROM a), +CAST(a AS INTERVAL DAY_SECOND(6)), +a +FROM t1_time_in_decimal ORDER BY id; +EXTRACT(DAY FROM a) EXTRACT(HOUR FROM a) EXTRACT(MINUTE FROM a) EXTRACT(SECOND FROM a) EXTRACT(MICROSECOND FROM a) CAST(a AS INTERVAL DAY_SECOND(6)) a +0 0 0 0 0 00:00:00.000000 0.0000000000 +0 0 0 0 900000 00:00:00.900000 0.9000000000 +0 0 0 0 990000 00:00:00.990000 0.9900000000 +0 0 0 0 999000 00:00:00.999000 0.9990000000 +0 0 0 0 999900 00:00:00.999900 0.9999000000 +0 0 0 0 999990 00:00:00.999990 0.9999900000 +0 0 0 0 999999 00:00:00.999999 0.9999990000 +0 0 0 1 0 00:00:01.000000 0.9999999000 +34 22 0 0 0 34 22:00:00.000000 8375959.9999999000 +34 22 59 59 0 34 22:59:59.000000 8385959.0000000000 +34 22 59 59 900000 34 22:59:59.900000 8385959.9000000000 +34 22 59 59 990000 34 22:59:59.990000 8385959.9900000000 +34 22 59 59 999000 34 22:59:59.999000 8385959.9990000000 +34 22 59 59 999900 34 22:59:59.999900 8385959.9999000000 +34 22 59 59 999990 34 22:59:59.999990 8385959.9999900000 +34 22 59 59 999999 34 22:59:59.999999 8385959.9999990000 +34 23 0 0 0 34 23:00:00.000000 8385959.9999999000 +35 0 0 0 0 35 00:00:00.000000 8395959.9999999000 +3652058 22 59 59 999999 3652058 22:59:59.999999 876494145959.9999990000 +3652058 23 0 0 0 3652058 23:00:00.000000 876494145959.9999999000 +3652058 23 59 59 999999 3652058 23:59:59.999999 876494155959.9999990000 +3652058 23 59 59 999999 3652058 23:59:59.999999 876494155959.9999999000 +Warnings: +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.0000000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9000000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9900000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9990000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9999000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9999900000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9999990000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '0.9999999000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8375959.9999999000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.0000000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9000000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9900000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9990000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9999000000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9999900000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9999990000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8385959.9999999000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '8395959.9999999000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '876494145959.9999990000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '876494145959.9999999000' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '876494155959.9999990000' +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect INTERVAL DAY TO SECOND value: '876494155959.9999999000' +SELECT +TIME_TO_SEC(a), +CAST(a AS TIME(6)), +a +FROM t1_time_in_decimal ORDER BY id; +TIME_TO_SEC(a) CAST(a AS TIME(6)) a +0.000000 00:00:00.000000 0.0000000000 +0.900000 00:00:00.900000 0.9000000000 +0.990000 00:00:00.990000 0.9900000000 +0.999000 00:00:00.999000 0.9990000000 +0.999900 00:00:00.999900 0.9999000000 +0.999990 00:00:00.999990 0.9999900000 +0.999999 00:00:00.999999 0.9999990000 +1.000000 00:00:01.000000 0.9999999000 +3016800.000000 838:00:00.000000 8375959.9999999000 +3020399.000000 838:59:59.000000 8385959.0000000000 +3020399.900000 838:59:59.900000 8385959.9000000000 +3020399.990000 838:59:59.990000 8385959.9900000000 +3020399.999000 838:59:59.999000 8385959.9990000000 +3020399.999900 838:59:59.999900 8385959.9999000000 +3020399.999990 838:59:59.999990 8385959.9999900000 +3020399.999999 838:59:59.999999 8385959.9999990000 +3020399.999999 838:59:59.999999 8385959.9999999000 +3020399.999999 838:59:59.999999 8395959.9999999000 +NULL NULL 876494145959.9999990000 +NULL NULL 876494145959.9999999000 +NULL NULL 876494155959.9999990000 +NULL NULL 876494155959.9999999000 +Warnings: +Warning 1292 Incorrect time value: '8385959.9999999000' for column 'a' at row 17 +Warning 1292 Incorrect time value: '8385959.9999999000' for column 'a' at row 17 +Warning 1292 Incorrect time value: '8395959.9999999000' for column 'a' at row 18 +Warning 1292 Incorrect time value: '8395959.9999999000' for column 'a' at row 18 +Warning 1292 Incorrect time value: '876494145959.9999990000' for column 'a' at row 19 +Warning 1292 Incorrect time value: '876494145959.9999990000' for column 'a' at row 19 +Warning 1292 Incorrect time value: '876494145959.9999999000' for column 'a' at row 20 +Warning 1292 Incorrect time value: '876494145959.9999999000' for column 'a' at row 20 +Warning 1292 Incorrect time value: '876494155959.9999990000' for column 'a' at row 21 +Warning 1292 Incorrect time value: '876494155959.9999990000' for column 'a' at row 21 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +Warning 1292 Incorrect time value: '876494155959.9999999000' for column 'a' at row 22 +# +# Functions with a single DATE input, conversion from DATETIME-in-VARCHAR +# +SELECT QUARTER(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +QUARTER(a) CAST(a AS DATE) a +4 2000-12-31 2000-12-31 23:59:59 +4 2000-12-31 2000-12-31 23:59:59.9 +4 2000-12-31 2000-12-31 23:59:59.99 +4 2000-12-31 2000-12-31 23:59:59.999 +4 2000-12-31 2000-12-31 23:59:59.9999 +4 2000-12-31 2000-12-31 23:59:59.99999 +4 2000-12-31 2000-12-31 23:59:59.999999 +1 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +DAY(a) CAST(a AS DATE) a +31 2000-12-31 2000-12-31 23:59:59 +31 2000-12-31 2000-12-31 23:59:59.9 +31 2000-12-31 2000-12-31 23:59:59.99 +31 2000-12-31 2000-12-31 23:59:59.999 +31 2000-12-31 2000-12-31 23:59:59.9999 +31 2000-12-31 2000-12-31 23:59:59.99999 +31 2000-12-31 2000-12-31 23:59:59.999999 +1 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT MONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +MONTH(a) CAST(a AS DATE) a +12 2000-12-31 2000-12-31 23:59:59 +12 2000-12-31 2000-12-31 23:59:59.9 +12 2000-12-31 2000-12-31 23:59:59.99 +12 2000-12-31 2000-12-31 23:59:59.999 +12 2000-12-31 2000-12-31 23:59:59.9999 +12 2000-12-31 2000-12-31 23:59:59.99999 +12 2000-12-31 2000-12-31 23:59:59.999999 +1 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT YEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +YEAR(a) CAST(a AS DATE) a +2000 2000-12-31 2000-12-31 23:59:59 +2000 2000-12-31 2000-12-31 23:59:59.9 +2000 2000-12-31 2000-12-31 23:59:59.99 +2000 2000-12-31 2000-12-31 23:59:59.999 +2000 2000-12-31 2000-12-31 23:59:59.9999 +2000 2000-12-31 2000-12-31 23:59:59.99999 +2000 2000-12-31 2000-12-31 23:59:59.999999 +2001 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT DAYNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +DAYNAME(a) CAST(a AS DATE) a +Sunday 2000-12-31 2000-12-31 23:59:59 +Sunday 2000-12-31 2000-12-31 23:59:59.9 +Sunday 2000-12-31 2000-12-31 23:59:59.99 +Sunday 2000-12-31 2000-12-31 23:59:59.999 +Sunday 2000-12-31 2000-12-31 23:59:59.9999 +Sunday 2000-12-31 2000-12-31 23:59:59.99999 +Sunday 2000-12-31 2000-12-31 23:59:59.999999 +Monday 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT MONTHNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +MONTHNAME(a) CAST(a AS DATE) a +December 2000-12-31 2000-12-31 23:59:59 +December 2000-12-31 2000-12-31 23:59:59.9 +December 2000-12-31 2000-12-31 23:59:59.99 +December 2000-12-31 2000-12-31 23:59:59.999 +December 2000-12-31 2000-12-31 23:59:59.9999 +December 2000-12-31 2000-12-31 23:59:59.99999 +December 2000-12-31 2000-12-31 23:59:59.999999 +January 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT LAST_DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +LAST_DAY(a) CAST(a AS DATE) a +2000-12-31 2000-12-31 2000-12-31 23:59:59 +2000-12-31 2000-12-31 2000-12-31 23:59:59.9 +2000-12-31 2000-12-31 2000-12-31 23:59:59.99 +2000-12-31 2000-12-31 2000-12-31 23:59:59.999 +2000-12-31 2000-12-31 2000-12-31 23:59:59.9999 +2000-12-31 2000-12-31 2000-12-31 23:59:59.99999 +2000-12-31 2000-12-31 2000-12-31 23:59:59.999999 +2000-12-31 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT TO_DAYS(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +TO_DAYS(a) CAST(a AS DATE) a +730850 2000-12-31 2000-12-31 23:59:59 +730850 2000-12-31 2000-12-31 23:59:59.9 +730850 2000-12-31 2000-12-31 23:59:59.99 +730850 2000-12-31 2000-12-31 23:59:59.999 +730850 2000-12-31 2000-12-31 23:59:59.9999 +730850 2000-12-31 2000-12-31 23:59:59.99999 +730850 2000-12-31 2000-12-31 23:59:59.999999 +730851 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT DAYOFYEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +DAYOFYEAR(a) CAST(a AS DATE) a +366 2000-12-31 2000-12-31 23:59:59 +366 2000-12-31 2000-12-31 23:59:59.9 +366 2000-12-31 2000-12-31 23:59:59.99 +366 2000-12-31 2000-12-31 23:59:59.999 +366 2000-12-31 2000-12-31 23:59:59.9999 +366 2000-12-31 2000-12-31 23:59:59.99999 +366 2000-12-31 2000-12-31 23:59:59.999999 +1 2000-12-31 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +CREATE TABLE t1 (a VARCHAR(32)); +INSERT INTO t1 VALUES +('2002-01-05 23:59:59'), +('2002-01-05 23:59:59.999999'), +('2002-01-05 23:59:59.9999999'); +SELECT YEARWEEK(a), a FROM t1; +YEARWEEK(a) a +200152 2002-01-05 23:59:59 +200152 2002-01-05 23:59:59.999999 +200201 2002-01-05 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2002-01-05 23:59:59.9999999' +SELECT WEEK(a), a FROM t1; +WEEK(a) a +0 2002-01-05 23:59:59 +0 2002-01-05 23:59:59.999999 +1 2002-01-05 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2002-01-05 23:59:59.9999999' +SELECT WEEKDAY(a), a FROM t1; +WEEKDAY(a) a +5 2002-01-05 23:59:59 +5 2002-01-05 23:59:59.999999 +6 2002-01-05 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2002-01-05 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(32,9)); +INSERT INTO t1 VALUES +(20020105235959), +(20020105235959.999999), +(20020105235959.9999999); +SELECT YEARWEEK(a), a FROM t1; +YEARWEEK(a) a +200152 20020105235959.000000000 +200152 20020105235959.999999000 +200201 20020105235959.999999900 +SELECT WEEK(a), a FROM t1; +WEEK(a) a +0 20020105235959.000000000 +0 20020105235959.999999000 +1 20020105235959.999999900 +SELECT WEEKDAY(a), a FROM t1; +WEEKDAY(a) a +5 20020105235959.000000000 +5 20020105235959.999999000 +6 20020105235959.999999900 +DROP TABLE t1; +# +# Functions with a single DATE input, conversion from DATETIME-in-DECIMAL +# +SELECT QUARTER(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +QUARTER(a) CAST(a AS DATE) a +4 2000-12-31 20001231235959.0000000000 +4 2000-12-31 20001231235959.9000000000 +4 2000-12-31 20001231235959.9900000000 +4 2000-12-31 20001231235959.9990000000 +4 2000-12-31 20001231235959.9999000000 +4 2000-12-31 20001231235959.9999900000 +4 2000-12-31 20001231235959.9999990000 +1 2000-12-31 20001231235959.9999999000 +SELECT DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +DAY(a) CAST(a AS DATE) a +31 2000-12-31 20001231235959.0000000000 +31 2000-12-31 20001231235959.9000000000 +31 2000-12-31 20001231235959.9900000000 +31 2000-12-31 20001231235959.9990000000 +31 2000-12-31 20001231235959.9999000000 +31 2000-12-31 20001231235959.9999900000 +31 2000-12-31 20001231235959.9999990000 +1 2000-12-31 20001231235959.9999999000 +SELECT MONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +MONTH(a) CAST(a AS DATE) a +12 2000-12-31 20001231235959.0000000000 +12 2000-12-31 20001231235959.9000000000 +12 2000-12-31 20001231235959.9900000000 +12 2000-12-31 20001231235959.9990000000 +12 2000-12-31 20001231235959.9999000000 +12 2000-12-31 20001231235959.9999900000 +12 2000-12-31 20001231235959.9999990000 +1 2000-12-31 20001231235959.9999999000 +SELECT YEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +YEAR(a) CAST(a AS DATE) a +2000 2000-12-31 20001231235959.0000000000 +2000 2000-12-31 20001231235959.9000000000 +2000 2000-12-31 20001231235959.9900000000 +2000 2000-12-31 20001231235959.9990000000 +2000 2000-12-31 20001231235959.9999000000 +2000 2000-12-31 20001231235959.9999900000 +2000 2000-12-31 20001231235959.9999990000 +2001 2000-12-31 20001231235959.9999999000 +SELECT DAYNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +DAYNAME(a) CAST(a AS DATE) a +Sunday 2000-12-31 20001231235959.0000000000 +Sunday 2000-12-31 20001231235959.9000000000 +Sunday 2000-12-31 20001231235959.9900000000 +Sunday 2000-12-31 20001231235959.9990000000 +Sunday 2000-12-31 20001231235959.9999000000 +Sunday 2000-12-31 20001231235959.9999900000 +Sunday 2000-12-31 20001231235959.9999990000 +Monday 2000-12-31 20001231235959.9999999000 +SELECT MONTHNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +MONTHNAME(a) CAST(a AS DATE) a +December 2000-12-31 20001231235959.0000000000 +December 2000-12-31 20001231235959.9000000000 +December 2000-12-31 20001231235959.9900000000 +December 2000-12-31 20001231235959.9990000000 +December 2000-12-31 20001231235959.9999000000 +December 2000-12-31 20001231235959.9999900000 +December 2000-12-31 20001231235959.9999990000 +January 2000-12-31 20001231235959.9999999000 +SELECT YEARWEEK(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +YEARWEEK(a) CAST(a AS DATE) a +200053 2000-12-31 20001231235959.0000000000 +200053 2000-12-31 20001231235959.9000000000 +200053 2000-12-31 20001231235959.9900000000 +200053 2000-12-31 20001231235959.9990000000 +200053 2000-12-31 20001231235959.9999000000 +200053 2000-12-31 20001231235959.9999900000 +200053 2000-12-31 20001231235959.9999990000 +200053 2000-12-31 20001231235959.9999999000 +SELECT LAST_DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +LAST_DAY(a) CAST(a AS DATE) a +2000-12-31 2000-12-31 20001231235959.0000000000 +2000-12-31 2000-12-31 20001231235959.9000000000 +2000-12-31 2000-12-31 20001231235959.9900000000 +2000-12-31 2000-12-31 20001231235959.9990000000 +2000-12-31 2000-12-31 20001231235959.9999000000 +2000-12-31 2000-12-31 20001231235959.9999900000 +2000-12-31 2000-12-31 20001231235959.9999990000 +2000-12-31 2000-12-31 20001231235959.9999999000 +SELECT TO_DAYS(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +TO_DAYS(a) CAST(a AS DATE) a +730850 2000-12-31 20001231235959.0000000000 +730850 2000-12-31 20001231235959.9000000000 +730850 2000-12-31 20001231235959.9900000000 +730850 2000-12-31 20001231235959.9990000000 +730850 2000-12-31 20001231235959.9999000000 +730850 2000-12-31 20001231235959.9999900000 +730850 2000-12-31 20001231235959.9999990000 +730851 2000-12-31 20001231235959.9999999000 +SELECT DAYOFYEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +DAYOFYEAR(a) CAST(a AS DATE) a +366 2000-12-31 20001231235959.0000000000 +366 2000-12-31 20001231235959.9000000000 +366 2000-12-31 20001231235959.9900000000 +366 2000-12-31 20001231235959.9990000000 +366 2000-12-31 20001231235959.9999000000 +366 2000-12-31 20001231235959.9999900000 +366 2000-12-31 20001231235959.9999990000 +1 2000-12-31 20001231235959.9999999000 +SELECT DAYOFMONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +DAYOFMONTH(a) CAST(a AS DATE) a +31 2000-12-31 20001231235959.0000000000 +31 2000-12-31 20001231235959.9000000000 +31 2000-12-31 20001231235959.9900000000 +31 2000-12-31 20001231235959.9990000000 +31 2000-12-31 20001231235959.9999000000 +31 2000-12-31 20001231235959.9999900000 +31 2000-12-31 20001231235959.9999990000 +1 2000-12-31 20001231235959.9999999000 +# +# Functions with a single DATETIME input, conversion from DATETIME-in-VARCHAR +# +SELECT TO_SECONDS(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +TO_SECONDS(a) CAST(a AS DATETIME(6)) a +63145526399 2000-12-31 23:59:59.000000 2000-12-31 23:59:59 +63145526399 2000-12-31 23:59:59.900000 2000-12-31 23:59:59.9 +63145526399 2000-12-31 23:59:59.990000 2000-12-31 23:59:59.99 +63145526399 2000-12-31 23:59:59.999000 2000-12-31 23:59:59.999 +63145526399 2000-12-31 23:59:59.999900 2000-12-31 23:59:59.9999 +63145526399 2000-12-31 23:59:59.999990 2000-12-31 23:59:59.99999 +63145526399 2000-12-31 23:59:59.999999 2000-12-31 23:59:59.999999 +63145526400 2001-01-01 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SET time_zone='+00:00'; +SELECT UNIX_TIMESTAMP(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +UNIX_TIMESTAMP(a) CAST(a AS DATETIME(6)) a +978307199.000000 2000-12-31 23:59:59.000000 2000-12-31 23:59:59 +978307199.900000 2000-12-31 23:59:59.900000 2000-12-31 23:59:59.9 +978307199.990000 2000-12-31 23:59:59.990000 2000-12-31 23:59:59.99 +978307199.999000 2000-12-31 23:59:59.999000 2000-12-31 23:59:59.999 +978307199.999900 2000-12-31 23:59:59.999900 2000-12-31 23:59:59.9999 +978307199.999990 2000-12-31 23:59:59.999990 2000-12-31 23:59:59.99999 +978307199.999999 2000-12-31 23:59:59.999999 2000-12-31 23:59:59.999999 +978307200.000000 2001-01-01 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SET time_zone=DEFAULT; +SELECT CONVERT_TZ(a, '+00:00','+00:00'), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +CONVERT_TZ(a, '+00:00','+00:00') CAST(a AS DATETIME(6)) a +2000-12-31 23:59:59.000000 2000-12-31 23:59:59.000000 2000-12-31 23:59:59 +2000-12-31 23:59:59.900000 2000-12-31 23:59:59.900000 2000-12-31 23:59:59.9 +2000-12-31 23:59:59.990000 2000-12-31 23:59:59.990000 2000-12-31 23:59:59.99 +2000-12-31 23:59:59.999000 2000-12-31 23:59:59.999000 2000-12-31 23:59:59.999 +2000-12-31 23:59:59.999900 2000-12-31 23:59:59.999900 2000-12-31 23:59:59.9999 +2000-12-31 23:59:59.999990 2000-12-31 23:59:59.999990 2000-12-31 23:59:59.99999 +2000-12-31 23:59:59.999999 2000-12-31 23:59:59.999999 2000-12-31 23:59:59.999999 +2001-01-01 00:00:00.000000 2001-01-01 00:00:00.000000 2000-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +# +# Functions with a single DATETIME input, conversion from DATETIME-in-DECIMAL +# +SELECT TO_SECONDS(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +TO_SECONDS(a) CAST(a AS DATETIME(6)) a +63145526399 2000-12-31 23:59:59.000000 20001231235959.0000000000 +63145526399 2000-12-31 23:59:59.900000 20001231235959.9000000000 +63145526399 2000-12-31 23:59:59.990000 20001231235959.9900000000 +63145526399 2000-12-31 23:59:59.999000 20001231235959.9990000000 +63145526399 2000-12-31 23:59:59.999900 20001231235959.9999000000 +63145526399 2000-12-31 23:59:59.999990 20001231235959.9999900000 +63145526399 2000-12-31 23:59:59.999999 20001231235959.9999990000 +63145526400 2001-01-01 00:00:00.000000 20001231235959.9999999000 +SET time_zone='+00:00'; +SELECT UNIX_TIMESTAMP(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +UNIX_TIMESTAMP(a) CAST(a AS DATETIME(6)) a +978307199.000000 2000-12-31 23:59:59.000000 20001231235959.0000000000 +978307199.900000 2000-12-31 23:59:59.900000 20001231235959.9000000000 +978307199.990000 2000-12-31 23:59:59.990000 20001231235959.9900000000 +978307199.999000 2000-12-31 23:59:59.999000 20001231235959.9990000000 +978307199.999900 2000-12-31 23:59:59.999900 20001231235959.9999000000 +978307199.999990 2000-12-31 23:59:59.999990 20001231235959.9999900000 +978307199.999999 2000-12-31 23:59:59.999999 20001231235959.9999990000 +978307200.000000 2001-01-01 00:00:00.000000 20001231235959.9999999000 +SET time_zone=DEFAULT; +SELECT CONVERT_TZ(a, '+00:00','+00:00'), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +CONVERT_TZ(a, '+00:00','+00:00') CAST(a AS DATETIME(6)) a +2000-12-31 23:59:59.000000 2000-12-31 23:59:59.000000 20001231235959.0000000000 +2000-12-31 23:59:59.900000 2000-12-31 23:59:59.900000 20001231235959.9000000000 +2000-12-31 23:59:59.990000 2000-12-31 23:59:59.990000 20001231235959.9900000000 +2000-12-31 23:59:59.999000 2000-12-31 23:59:59.999000 20001231235959.9990000000 +2000-12-31 23:59:59.999900 2000-12-31 23:59:59.999900 20001231235959.9999000000 +2000-12-31 23:59:59.999990 2000-12-31 23:59:59.999990 20001231235959.9999900000 +2000-12-31 23:59:59.999999 2000-12-31 23:59:59.999999 20001231235959.9999990000 +2001-01-01 00:00:00.000000 2001-01-01 00:00:00.000000 20001231235959.9999999000 +DROP TABLE t1_datetime_in_varchar; +DROP TABLE t1_datetime_in_decimal; +DROP TABLE t1_time_in_varchar; +DROP TABLE t1_time_in_decimal; +# +# Functions that construct DATETIME +# +SET time_zone='+00:00'; +CREATE TABLE t1_unix_timestamp (id SERIAL, a DECIMAL(30,10)); +INSERT INTO t1_unix_timestamp (a) VALUES +(980639999), +(980639999.9), +(980639999.999999), +(980639999.9999999), +(2147483647), +(2147483647.9), +(2147483647.999999), +(2147483647.9999999); +SELECT a, FROM_UNIXTIME(a) FROM t1_unix_timestamp ORDER BY id; +a FROM_UNIXTIME(a) +980639999.0000000000 2001-01-27 23:59:59.000000 +980639999.9000000000 2001-01-27 23:59:59.900000 +980639999.9999990000 2001-01-27 23:59:59.999999 +980639999.9999999000 2001-01-28 00:00:00.000000 +2147483647.0000000000 2038-01-19 03:14:07.000000 +2147483647.9000000000 2038-01-19 03:14:07.900000 +2147483647.9999990000 2038-01-19 03:14:07.999999 +2147483647.9999999000 NULL +DROP TABLE t1_unix_timestamp; +SET time_zone=DEFAULT; +# +# Functions that construct TIME +# +CREATE TABLE t1_sec (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_sec (a) VALUES +(59), +(59.9), +(59.999999), +(59.9999999), +(3020398), +(3020398.999999), +(3020398.9999999), +(3020399), +(3020399.999999), +(3020399.9999999), +(9223372036854775807), +(9223372036854775807.9), +(9223372036854775807.999999), +(9223372036854775807.9999999), +(18446744073709551615), +(18446744073709551615.9), +(18446744073709551615.999999), +(18446744073709551615.9999999); +SELECT a, SEC_TO_TIME(a) FROM t1_sec ORDER BY id; +a SEC_TO_TIME(a) +59.0000000000 00:00:59.000000 +59.9000000000 00:00:59.900000 +59.9999990000 00:00:59.999999 +59.9999999000 00:01:00.000000 +3020398.0000000000 838:59:58.000000 +3020398.9999990000 838:59:58.999999 +3020398.9999999000 838:59:59.000000 +3020399.0000000000 838:59:59.000000 +3020399.9999990000 838:59:59.999999 +3020399.9999999000 838:59:59.999999 +9223372036854775807.0000000000 838:59:59.999999 +9223372036854775807.9000000000 838:59:59.999999 +9223372036854775807.9999990000 838:59:59.999999 +9223372036854775807.9999999000 838:59:59.999999 +18446744073709551615.0000000000 838:59:59.999999 +18446744073709551615.9000000000 838:59:59.999999 +18446744073709551615.9999990000 838:59:59.999999 +18446744073709551615.9999999000 838:59:59.999999 +Warnings: +Warning 1292 Truncated incorrect seconds value: '3020400' +Warning 1292 Truncated incorrect seconds value: '9223372036854775807.0000000000' +Warning 1292 Truncated incorrect seconds value: '9223372036854775807.9000000000' +Warning 1292 Truncated incorrect seconds value: '9223372036854775807.9999990000' +Warning 1292 Truncated incorrect seconds value: '9223372036854775807.9999999000' +Warning 1292 Truncated incorrect seconds value: '18446744073709551615.0000000000' +Warning 1292 Truncated incorrect seconds value: '18446744073709551615.9000000000' +Warning 1292 Truncated incorrect seconds value: '18446744073709551615.9999990000' +Warning 1292 Truncated incorrect seconds value: '18446744073709551615.9999999000' +DROP TABLE t1_sec; +CREATE TABLE t1_sec (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_sec (a) VALUES +(0), +(0.9), +(0.999999), +(0.9999999); +SELECT a, MAKETIME(0, 0, a) FROM t1_sec ORDER BY id; +a MAKETIME(0, 0, a) +0.0000000000 00:00:00.000000 +0.9000000000 00:00:00.900000 +0.9999990000 00:00:00.999999 +0.9999999000 00:00:01.000000 +DROP TABLE t1_sec; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +SELECT +'----', +a, +DATE_FORMAT(a, '%Y') AS yyyy, +DATE_FORMAT(a, '%Y-%m-%d') AS d, +DATE_FORMAT(a, '%H:%i:%s') AS t0, +DATE_FORMAT(a, '%H:%i:%s.%f') AS t6, +DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s') AS dt0, +DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s.%f') AS dt6 +FROM t1; +---- ---- +a 2017-12-31 23:59:59 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.000000 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.000000 +---- ---- +a 2017-12-31 23:59:59.9 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.900000 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.900000 +---- ---- +a 2017-12-31 23:59:59.999999 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.999999 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.999999 +---- ---- +a 2017-12-31 23:59:59.9999999 +yyyy 2018 +d 2018-01-01 +t0 00:00:00 +t6 00:00:00.000000 +dt0 2018-01-01 00:00:00 +dt6 2018-01-01 00:00:00.000000 +Warnings: +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Level Note +Code 1292 +Message Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(32,9)); +INSERT INTO t1 VALUES +(20171231235959), +(20171231235959.9), +(20171231235959.999999), +(20171231235959.9999999); +SELECT +'----', +a, +DATE_FORMAT(a, '%Y') AS yyyy, +DATE_FORMAT(a, '%Y-%m-%d') AS d, +DATE_FORMAT(a, '%H:%i:%s') AS t0, +DATE_FORMAT(a, '%H:%i:%s.%f') AS t6, +DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s') AS dt0, +DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s.%f') AS dt6 +FROM t1; +---- ---- +a 20171231235959.000000000 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.000000 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.000000 +---- ---- +a 20171231235959.900000000 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.900000 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.900000 +---- ---- +a 20171231235959.999999000 +yyyy 2017 +d 2017-12-31 +t0 23:59:59 +t6 23:59:59.999999 +dt0 2017-12-31 23:59:59 +dt6 2017-12-31 23:59:59.999999 +---- ---- +a 20171231235959.999999900 +yyyy 2018 +d 2018-01-01 +t0 00:00:00 +t6 00:00:00.000000 +dt0 2018-01-01 00:00:00 +dt6 2018-01-01 00:00:00.000000 +DROP TABLE t1; +# +# Functions with two temporal parameters that round nanoseconds in both parameters in MySQL +# +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); +SELECT TIMESTAMP(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +TIMESTAMP(t1.a, t2.a) a a +2017-12-31 23:59:59.000000 2017-12-31 23:59:59 00:00:00 +2017-12-31 23:59:59.900000 2017-12-31 23:59:59 00:00:00.9 +2017-12-31 23:59:59.999999 2017-12-31 23:59:59 00:00:00.999999 +2018-01-01 00:00:00.000000 2017-12-31 23:59:59 00:00:00.9999999 +2017-12-31 23:59:59.900000 2017-12-31 23:59:59.9 00:00:00 +2018-01-01 00:00:00.800000 2017-12-31 23:59:59.9 00:00:00.9 +2018-01-01 00:00:00.899999 2017-12-31 23:59:59.9 00:00:00.999999 +2018-01-01 00:00:00.900000 2017-12-31 23:59:59.9 00:00:00.9999999 +2017-12-31 23:59:59.999999 2017-12-31 23:59:59.999999 00:00:00 +2018-01-01 00:00:00.899999 2017-12-31 23:59:59.999999 00:00:00.9 +2018-01-01 00:00:00.999998 2017-12-31 23:59:59.999999 00:00:00.999999 +2018-01-01 00:00:00.999999 2017-12-31 23:59:59.999999 00:00:00.9999999 +2018-01-01 00:00:00.000000 2017-12-31 23:59:59.9999999 00:00:00 +2018-01-01 00:00:00.900000 2017-12-31 23:59:59.9999999 00:00:00.9 +2018-01-01 00:00:00.999999 2017-12-31 23:59:59.9999999 00:00:00.999999 +2018-01-01 00:00:01.000000 2017-12-31 23:59:59.9999999 00:00:00.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +SELECT ADDTIME(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +ADDTIME(t1.a, t2.a) a a +2017-12-31 23:59:59 2017-12-31 23:59:59 00:00:00 +2017-12-31 23:59:59.900000 2017-12-31 23:59:59 00:00:00.9 +2017-12-31 23:59:59.999999 2017-12-31 23:59:59 00:00:00.999999 +2018-01-01 00:00:00 2017-12-31 23:59:59 00:00:00.9999999 +2017-12-31 23:59:59.900000 2017-12-31 23:59:59.9 00:00:00 +2018-01-01 00:00:00.800000 2017-12-31 23:59:59.9 00:00:00.9 +2018-01-01 00:00:00.899999 2017-12-31 23:59:59.9 00:00:00.999999 +2018-01-01 00:00:00.900000 2017-12-31 23:59:59.9 00:00:00.9999999 +2017-12-31 23:59:59.999999 2017-12-31 23:59:59.999999 00:00:00 +2018-01-01 00:00:00.899999 2017-12-31 23:59:59.999999 00:00:00.9 +2018-01-01 00:00:00.999998 2017-12-31 23:59:59.999999 00:00:00.999999 +2018-01-01 00:00:00.999999 2017-12-31 23:59:59.999999 00:00:00.9999999 +2018-01-01 00:00:00 2017-12-31 23:59:59.9999999 00:00:00 +2018-01-01 00:00:00.900000 2017-12-31 23:59:59.9999999 00:00:00.9 +2018-01-01 00:00:00.999999 2017-12-31 23:59:59.9999999 00:00:00.999999 +2018-01-01 00:00:01 2017-12-31 23:59:59.9999999 00:00:00.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect INTERVAL DAY TO SECOND value: '00:00:00.9999999' +DROP TABLE t1, t2; +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('23:59:59'), +('23:59:59.9'), +('23:59:59.999999'), +('23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); +SELECT TIMEDIFF(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +TIMEDIFF(t1.a, t2.a) a a +23:59:59.000000 23:59:59 00:00:00 +23:59:58.100000 23:59:59 00:00:00.9 +23:59:58.000001 23:59:59 00:00:00.999999 +23:59:58.000000 23:59:59 00:00:00.9999999 +23:59:59.900000 23:59:59.9 00:00:00 +23:59:59.000000 23:59:59.9 00:00:00.9 +23:59:58.900001 23:59:59.9 00:00:00.999999 +23:59:58.900000 23:59:59.9 00:00:00.9999999 +23:59:59.999999 23:59:59.999999 00:00:00 +23:59:59.099999 23:59:59.999999 00:00:00.9 +23:59:59.000000 23:59:59.999999 00:00:00.999999 +23:59:58.999999 23:59:59.999999 00:00:00.9999999 +24:00:00.000000 23:59:59.9999999 00:00:00 +23:59:59.100000 23:59:59.9999999 00:00:00.9 +23:59:59.000001 23:59:59.9999999 00:00:00.999999 +23:59:59.000000 23:59:59.9999999 00:00:00.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +DROP TABLE t1, t2; +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2001-12-31 23:59:59'), +('2001-12-31 23:59:59.9'), +('2001-12-31 23:59:59.999999'), +('2001-12-31 23:59:59.9999999'); +INSERT INTO t2 VALUES +('2001-12-31 23:59:59'), +('2001-12-31 23:59:59.9'), +('2001-12-31 23:59:59.999999'), +('2001-12-31 23:59:59.9999999'); +SELECT TIMESTAMPDIFF(MICROSECOND,t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +TIMESTAMPDIFF(MICROSECOND,t1.a, t2.a) a a +0 2001-12-31 23:59:59 2001-12-31 23:59:59 +900000 2001-12-31 23:59:59 2001-12-31 23:59:59.9 +999999 2001-12-31 23:59:59 2001-12-31 23:59:59.999999 +1000000 2001-12-31 23:59:59 2001-12-31 23:59:59.9999999 +-900000 2001-12-31 23:59:59.9 2001-12-31 23:59:59 +0 2001-12-31 23:59:59.9 2001-12-31 23:59:59.9 +99999 2001-12-31 23:59:59.9 2001-12-31 23:59:59.999999 +100000 2001-12-31 23:59:59.9 2001-12-31 23:59:59.9999999 +-999999 2001-12-31 23:59:59.999999 2001-12-31 23:59:59 +-99999 2001-12-31 23:59:59.999999 2001-12-31 23:59:59.9 +0 2001-12-31 23:59:59.999999 2001-12-31 23:59:59.999999 +1 2001-12-31 23:59:59.999999 2001-12-31 23:59:59.9999999 +-1000000 2001-12-31 23:59:59.9999999 2001-12-31 23:59:59 +-100000 2001-12-31 23:59:59.9999999 2001-12-31 23:59:59.9 +-1 2001-12-31 23:59:59.9999999 2001-12-31 23:59:59.999999 +0 2001-12-31 23:59:59.9999999 2001-12-31 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +DROP TABLE t1, t2; +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('23:59:59'), +('23:59:59.9'), +('23:59:59.999999'), +('23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); +SELECT TIMEDIFF(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +TIMEDIFF(t1.a, t2.a) a a +23:59:59.000000 23:59:59 00:00:00 +23:59:58.100000 23:59:59 00:00:00.9 +23:59:58.000001 23:59:59 00:00:00.999999 +23:59:58.000000 23:59:59 00:00:00.9999999 +23:59:59.900000 23:59:59.9 00:00:00 +23:59:59.000000 23:59:59.9 00:00:00.9 +23:59:58.900001 23:59:59.9 00:00:00.999999 +23:59:58.900000 23:59:59.9 00:00:00.9999999 +23:59:59.999999 23:59:59.999999 00:00:00 +23:59:59.099999 23:59:59.999999 00:00:00.9 +23:59:59.000000 23:59:59.999999 00:00:00.999999 +23:59:58.999999 23:59:59.999999 00:00:00.9999999 +24:00:00.000000 23:59:59.9999999 00:00:00 +23:59:59.100000 23:59:59.9999999 00:00:00.9 +23:59:59.000001 23:59:59.9999999 00:00:00.999999 +23:59:59.000000 23:59:59.9999999 00:00:00.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +Note 1292 Truncated incorrect time value: '23:59:59.9999999' +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +DROP TABLE t1, t2; +# +# STR_TO_DATE behaviour is questionable in MySQL 5.6 (MySQL Bug #92474) +# +# It truncates nanoseconds, but this may change in the future. +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +SELECT +a, +STR_TO_DATE(a, '%Y-%m-%d %H:%i:%s') AS c0, +STR_TO_DATE(a, '%Y-%m-%d %H:%i:%s.%f') AS c6 +FROM t1; +a c0 c6 +2017-12-31 23:59:59 2017-12-31 23:59:59 2017-12-31 23:59:59.000000 +2017-12-31 23:59:59.9 2017-12-31 23:59:59 2017-12-31 23:59:59.900000 +2017-12-31 23:59:59.999999 2017-12-31 23:59:59 2017-12-31 23:59:59.999999 +2017-12-31 23:59:59.9999999 2017-12-31 23:59:59 2017-12-31 23:59:59.999999 +Warnings: +Warning 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9' +Warning 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.999999' +Warning 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Warning 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +DROP TABLE t1; +# +# DATE_ADD behaviour is questionable in MySQL 5.6 (MySQL Bug#92473) +# It rounds nanoseconds in the first argument, but truncates nanoseconds in the second argument. +# This may change in the future, to round both arguments. +# +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +CREATE TABLE t2 (b DECIMAL(32,9)); +INSERT INTO t2 VALUES +(0), +(0.9), +(0.999999), +(0.9999999); +SELECT a, b, DATE_ADD(a, INTERVAL b SECOND) FROM t1,t2 ORDER BY a,b; +a b DATE_ADD(a, INTERVAL b SECOND) +2017-12-31 23:59:59 0.000000000 2017-12-31 23:59:59.000000 +2017-12-31 23:59:59 0.900000000 2017-12-31 23:59:59.900000 +2017-12-31 23:59:59 0.999999000 2017-12-31 23:59:59.999999 +2017-12-31 23:59:59 0.999999900 2017-12-31 23:59:59.999999 +2017-12-31 23:59:59.9 0.000000000 2017-12-31 23:59:59.900000 +2017-12-31 23:59:59.9 0.900000000 2018-01-01 00:00:00.800000 +2017-12-31 23:59:59.9 0.999999000 2018-01-01 00:00:00.899999 +2017-12-31 23:59:59.9 0.999999900 2018-01-01 00:00:00.899999 +2017-12-31 23:59:59.999999 0.000000000 2017-12-31 23:59:59.999999 +2017-12-31 23:59:59.999999 0.900000000 2018-01-01 00:00:00.899999 +2017-12-31 23:59:59.999999 0.999999000 2018-01-01 00:00:00.999998 +2017-12-31 23:59:59.999999 0.999999900 2018-01-01 00:00:00.999998 +2017-12-31 23:59:59.9999999 0.000000000 2018-01-01 00:00:00.000000 +2017-12-31 23:59:59.9999999 0.900000000 2018-01-01 00:00:00.900000 +2017-12-31 23:59:59.9999999 0.999999000 2018-01-01 00:00:00.999999 +2017-12-31 23:59:59.9999999 0.999999900 2018-01-01 00:00:00.999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2017-12-31 23:59:59.9999999' +DROP TABLE t1, t2; diff --git a/mysql-test/main/func_time_round.test b/mysql-test/main/func_time_round.test new file mode 100644 index 00000000000..12d3a50a10f --- /dev/null +++ b/mysql-test/main/func_time_round.test @@ -0,0 +1,461 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; + +CREATE TABLE t1_datetime_in_varchar (id SERIAL, a VARCHAR(64)); +INSERT INTO t1_datetime_in_varchar (a) VALUES +('2000-12-31 23:59:59'), +('2000-12-31 23:59:59.9'), +('2000-12-31 23:59:59.99'), +('2000-12-31 23:59:59.999'), +('2000-12-31 23:59:59.9999'), +('2000-12-31 23:59:59.99999'), +('2000-12-31 23:59:59.999999'), +('2000-12-31 23:59:59.9999999'); + +CREATE TABLE t1_datetime_in_decimal (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_datetime_in_decimal (a) VALUES +(20001231235959), +(20001231235959.9), +(20001231235959.99), +(20001231235959.999), +(20001231235959.9999), +(20001231235959.99999), +(20001231235959.999999), +(20001231235959.9999999); + + +CREATE TABLE t1_time_in_varchar (id SERIAL, a VARCHAR(64)); +INSERT INTO t1_time_in_varchar (a) VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.99'), +('00:00:00.999'), +('00:00:00.9999'), +('00:00:00.99999'), +('00:00:00.999999'), +('00:00:00.9999999'); +INSERT INTO t1_time_in_varchar (a) VALUES +('837:59:59.9999999'), +('838:59:59'), +('838:59:59.9'), +('838:59:59.99'), +('838:59:59.999'), +('838:59:59.9999'), +('838:59:59.99999'), +('838:59:59.999999'), +('838:59:59.9999999'), +('839:59:59.9999999'), +('87649414:59:59.999999'), +('87649414:59:59.9999999'), +('87649415:59:59.999999'), +('87649415:59:59.9999999'); + + +CREATE TABLE t1_time_in_decimal (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_time_in_decimal (a) VALUES +(0), +(0.9), +(0.99), +(0.999), +(0.9999), +(0.99999), +(0.999999), +(0.9999999); +INSERT INTO t1_time_in_decimal (a) VALUES +(8375959.9999999), +(8385959), +(8385959.9), +(8385959.99), +(8385959.999), +(8385959.9999), +(8385959.99999), +(8385959.999999), +(8385959.9999999), +(8395959.9999999), +(876494145959.999999), +(876494145959.9999999), +(876494155959.999999), +(876494155959.9999999); + +--echo # +--echo # TIME: LEAST/GREATEST +--echo # + +SELECT GREATEST(TIME'00:00:00', a) FROM t1_time_in_varchar; +SELECT GREATEST(TIME'00:00:00', a) FROM t1_time_in_decimal; + +SELECT GREATEST(TIME'00:00:00', '00:00:00.0000004'); +SELECT GREATEST(TIME'00:00:00', 0.0000004); + +SELECT GREATEST(TIME'00:00:00', '00:00:00.0000005'); +SELECT GREATEST(TIME'00:00:00', 0.0000005); + + +--echo # +--echo # Functions with a single TIME input, conversion from DATETIME-in-VARCHAR +--echo # + +SELECT SECOND(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT MINUTE(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT HOUR(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; + +SELECT EXTRACT(SECOND FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT EXTRACT(MINUTE FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT EXTRACT(HOUR FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; + +SELECT TIME_TO_SEC(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; + + +--echo # +--echo # Functions with a single TIME input, conversion from DATETIME-in-DECIMAL +--echo # + + +SELECT SECOND(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT MINUTE(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT HOUR(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; + +SELECT EXTRACT(SECOND FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT EXTRACT(MINUTE FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT EXTRACT(HOUR FROM a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; + +SELECT TIME_TO_SEC(a), CAST(a AS TIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; + + +--echo # +--echo # Functions with a single TIME interval input, conversion from TIME-interval-in-VARCHAR +--echo # + +SELECT + EXTRACT(DAY FROM a), + EXTRACT(HOUR FROM a), + EXTRACT(MINUTE FROM a), + EXTRACT(SECOND FROM a), + EXTRACT(MICROSECOND FROM a), + CAST(a AS INTERVAL DAY_SECOND(6)), + a +FROM t1_time_in_varchar ORDER BY id; + +SELECT + TIME_TO_SEC(a), + CAST(a AS TIME(6)), + a +FROM t1_time_in_varchar ORDER BY id; + +--echo # +--echo # Functions with a single TIME interval input, conversion from TIME-interval-in-DECIMAL +--echo # + +SELECT + EXTRACT(DAY FROM a), + EXTRACT(HOUR FROM a), + EXTRACT(MINUTE FROM a), + EXTRACT(SECOND FROM a), + EXTRACT(MICROSECOND FROM a), + CAST(a AS INTERVAL DAY_SECOND(6)), + a +FROM t1_time_in_decimal ORDER BY id; + +SELECT + TIME_TO_SEC(a), + CAST(a AS TIME(6)), + a +FROM t1_time_in_decimal ORDER BY id; + + +--echo # +--echo # Functions with a single DATE input, conversion from DATETIME-in-VARCHAR +--echo # + +SELECT QUARTER(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT MONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT YEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; + +SELECT DAYNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT MONTHNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; + +SELECT LAST_DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT TO_DAYS(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; +SELECT DAYOFYEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_varchar ORDER BY id; + + +CREATE TABLE t1 (a VARCHAR(32)); +INSERT INTO t1 VALUES +('2002-01-05 23:59:59'), +('2002-01-05 23:59:59.999999'), +('2002-01-05 23:59:59.9999999'); +SELECT YEARWEEK(a), a FROM t1; +SELECT WEEK(a), a FROM t1; +SELECT WEEKDAY(a), a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(32,9)); +INSERT INTO t1 VALUES +(20020105235959), +(20020105235959.999999), +(20020105235959.9999999); +SELECT YEARWEEK(a), a FROM t1; +SELECT WEEK(a), a FROM t1; +SELECT WEEKDAY(a), a FROM t1; +DROP TABLE t1; + +--echo # +--echo # Functions with a single DATE input, conversion from DATETIME-in-DECIMAL +--echo # + +SELECT QUARTER(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT MONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT YEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; + +SELECT DAYNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT MONTHNAME(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT YEARWEEK(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; + +SELECT LAST_DAY(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT TO_DAYS(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT DAYOFYEAR(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; +SELECT DAYOFMONTH(a), CAST(a AS DATE), a FROM t1_datetime_in_decimal ORDER BY id; + + +--echo # +--echo # Functions with a single DATETIME input, conversion from DATETIME-in-VARCHAR +--echo # + +SELECT TO_SECONDS(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; + +SET time_zone='+00:00'; +SELECT UNIX_TIMESTAMP(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; +SET time_zone=DEFAULT; + +SELECT CONVERT_TZ(a, '+00:00','+00:00'), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_varchar ORDER BY id; + + +--echo # +--echo # Functions with a single DATETIME input, conversion from DATETIME-in-DECIMAL +--echo # + +SELECT TO_SECONDS(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; + +SET time_zone='+00:00'; +SELECT UNIX_TIMESTAMP(a), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; +SET time_zone=DEFAULT; + +SELECT CONVERT_TZ(a, '+00:00','+00:00'), CAST(a AS DATETIME(6)), a FROM t1_datetime_in_decimal ORDER BY id; + +DROP TABLE t1_datetime_in_varchar; +DROP TABLE t1_datetime_in_decimal; +DROP TABLE t1_time_in_varchar; +DROP TABLE t1_time_in_decimal; + + +--echo # +--echo # Functions that construct DATETIME +--echo # + +SET time_zone='+00:00'; +CREATE TABLE t1_unix_timestamp (id SERIAL, a DECIMAL(30,10)); +INSERT INTO t1_unix_timestamp (a) VALUES +(980639999), +(980639999.9), +(980639999.999999), +(980639999.9999999), +(2147483647), +(2147483647.9), +(2147483647.999999), +(2147483647.9999999); +SELECT a, FROM_UNIXTIME(a) FROM t1_unix_timestamp ORDER BY id; +DROP TABLE t1_unix_timestamp; +SET time_zone=DEFAULT; + + +--echo # +--echo # Functions that construct TIME +--echo # + +CREATE TABLE t1_sec (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_sec (a) VALUES +(59), +(59.9), +(59.999999), +(59.9999999), +(3020398), +(3020398.999999), +(3020398.9999999), +(3020399), +(3020399.999999), +(3020399.9999999), +(9223372036854775807), +(9223372036854775807.9), +(9223372036854775807.999999), +(9223372036854775807.9999999), +(18446744073709551615), +(18446744073709551615.9), +(18446744073709551615.999999), +(18446744073709551615.9999999); +SELECT a, SEC_TO_TIME(a) FROM t1_sec ORDER BY id; +DROP TABLE t1_sec; + + +CREATE TABLE t1_sec (id SERIAL, a DECIMAL(38,10)); +INSERT INTO t1_sec (a) VALUES +(0), +(0.9), +(0.999999), +(0.9999999); +SELECT a, MAKETIME(0, 0, a) FROM t1_sec ORDER BY id; +DROP TABLE t1_sec; + + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +--vertical_results +SELECT + '----', + a, + DATE_FORMAT(a, '%Y') AS yyyy, + DATE_FORMAT(a, '%Y-%m-%d') AS d, + DATE_FORMAT(a, '%H:%i:%s') AS t0, + DATE_FORMAT(a, '%H:%i:%s.%f') AS t6, + DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s') AS dt0, + DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s.%f') AS dt6 +FROM t1; +--horizontal_results +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(32,9)); +INSERT INTO t1 VALUES +(20171231235959), +(20171231235959.9), +(20171231235959.999999), +(20171231235959.9999999); +--vertical_results +SELECT + '----', + a, + DATE_FORMAT(a, '%Y') AS yyyy, + DATE_FORMAT(a, '%Y-%m-%d') AS d, + DATE_FORMAT(a, '%H:%i:%s') AS t0, + DATE_FORMAT(a, '%H:%i:%s.%f') AS t6, + DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s') AS dt0, + DATE_FORMAT(a, '%Y-%m-%d %H:%i:%s.%f') AS dt6 +FROM t1; +--horizontal_results +DROP TABLE t1; + + +--echo # +--echo # Functions with two temporal parameters that round nanoseconds in both parameters in MySQL +--echo # + +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); + +SELECT TIMESTAMP(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +SELECT ADDTIME(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; + +DROP TABLE t1, t2; + + +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('23:59:59'), +('23:59:59.9'), +('23:59:59.999999'), +('23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); +SELECT TIMEDIFF(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +DROP TABLE t1, t2; + + +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2001-12-31 23:59:59'), +('2001-12-31 23:59:59.9'), +('2001-12-31 23:59:59.999999'), +('2001-12-31 23:59:59.9999999'); +INSERT INTO t2 VALUES +('2001-12-31 23:59:59'), +('2001-12-31 23:59:59.9'), +('2001-12-31 23:59:59.999999'), +('2001-12-31 23:59:59.9999999'); +SELECT TIMESTAMPDIFF(MICROSECOND,t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +DROP TABLE t1, t2; + +CREATE TABLE t1 (a VARCHAR(64)); +CREATE TABLE t2 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('23:59:59'), +('23:59:59.9'), +('23:59:59.999999'), +('23:59:59.9999999'); +INSERT INTO t2 VALUES +('00:00:00'), +('00:00:00.9'), +('00:00:00.999999'), +('00:00:00.9999999'); +SELECT TIMEDIFF(t1.a, t2.a), t1.a, t2.a FROM t1,t2 ORDER BY t1.a, t2.a; +DROP TABLE t1, t2; + + +--echo # +--echo # STR_TO_DATE behaviour is questionable in MySQL 5.6 (MySQL Bug #92474) +--echo # + +--echo # It truncates nanoseconds, but this may change in the future. +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); + +SELECT + a, + STR_TO_DATE(a, '%Y-%m-%d %H:%i:%s') AS c0, + STR_TO_DATE(a, '%Y-%m-%d %H:%i:%s.%f') AS c6 +FROM t1; +DROP TABLE t1; + + +--echo # +--echo # DATE_ADD behaviour is questionable in MySQL 5.6 (MySQL Bug#92473) +--echo # It rounds nanoseconds in the first argument, but truncates nanoseconds in the second argument. +--echo # This may change in the future, to round both arguments. +--echo # + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES +('2017-12-31 23:59:59'), +('2017-12-31 23:59:59.9'), +('2017-12-31 23:59:59.999999'), +('2017-12-31 23:59:59.9999999'); +CREATE TABLE t2 (b DECIMAL(32,9)); +INSERT INTO t2 VALUES +(0), +(0.9), +(0.999999), +(0.9999999); +SELECT a, b, DATE_ADD(a, INTERVAL b SECOND) FROM t1,t2 ORDER BY a,b; +DROP TABLE t1, t2; diff --git a/mysql-test/main/information_schema.result b/mysql-test/main/information_schema.result index b6e331a0382..4a39eea6115 100644 --- a/mysql-test/main/information_schema.result +++ b/mysql-test/main/information_schema.result @@ -664,7 +664,7 @@ proc body longblob proc definer char(141) proc created timestamp proc modified timestamp -proc sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') +proc sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') proc comment text proc character_set_client char(32) proc collation_connection char(32) diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 5ee85eee7de..cf25f377142 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -1209,7 +1209,8 @@ The following specify which files/extra groups are read (specified before remain ERROR_FOR_DIVISION_BY_ZERO, TRADITIONAL, NO_AUTO_CREATE_USER, HIGH_NOT_PRECEDENCE, NO_ENGINE_SUBSTITUTION, PAD_CHAR_TO_FULL_LENGTH, - EMPTY_STRING_IS_NULL, SIMULTANEOUS_ASSIGNMENT + EMPTY_STRING_IS_NULL, SIMULTANEOUS_ASSIGNMENT, + TIME_ROUND_FRACTIONAL --sql-safe-updates If set to 1, UPDATEs and DELETEs need either a key in the WHERE clause, or a LIMIT clause, or else they will aborted. Prevents the common mistake of accidentally diff --git a/mysql-test/main/sql_mode.result b/mysql-test/main/sql_mode.result index 238bae2efd8..25a90703bf5 100644 --- a/mysql-test/main/sql_mode.result +++ b/mysql-test/main/sql_mode.result @@ -476,10 +476,14 @@ select @@sql_mode; @@sql_mode REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI set sql_mode=2147483648*2*2*2; -ERROR 42000: Variable 'sql_mode' can't be set to the value of '17179869184' select @@sql_mode; @@sql_mode -REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,NO_TABLE_OPTIONS,ANSI +TIME_ROUND_FRACTIONAL +set sql_mode=2147483648*2*2*2*2; +ERROR 42000: Variable 'sql_mode' can't be set to the value of '34359738368' +select @@sql_mode; +@@sql_mode +TIME_ROUND_FRACTIONAL set sql_mode=PAD_CHAR_TO_FULL_LENGTH; create table t1 (a int auto_increment primary key, b char(5)); insert into t1 (b) values('a'),('b\t'),('c '); diff --git a/mysql-test/main/sql_mode.test b/mysql-test/main/sql_mode.test index 8cf50f73f6f..97f5cf42791 100644 --- a/mysql-test/main/sql_mode.test +++ b/mysql-test/main/sql_mode.test @@ -263,8 +263,10 @@ set sql_mode=4194304; select @@sql_mode; set sql_mode=16384+(65536*4); select @@sql_mode; +set sql_mode=2147483648*2*2*2; +select @@sql_mode; --error 1231 -set sql_mode=2147483648*2*2*2; # that mode does not exist +set sql_mode=2147483648*2*2*2*2; # that mode does not exist select @@sql_mode; # diff --git a/mysql-test/main/system_mysql_db.result b/mysql-test/main/system_mysql_db.result index a4c414888ca..bdc1df85131 100644 --- a/mysql-test/main/system_mysql_db.result +++ b/mysql-test/main/system_mysql_db.result @@ -191,7 +191,7 @@ proc CREATE TABLE `proc` ( `definer` char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, @@ -217,7 +217,7 @@ event CREATE TABLE `event` ( `ends` datetime DEFAULT NULL, `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `originator` int(10) unsigned NOT NULL, `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', diff --git a/mysql-test/main/system_mysql_db_fix40123.result b/mysql-test/main/system_mysql_db_fix40123.result index d76a2ef923e..abb6fbb38e1 100644 --- a/mysql-test/main/system_mysql_db_fix40123.result +++ b/mysql-test/main/system_mysql_db_fix40123.result @@ -192,7 +192,7 @@ proc CREATE TABLE `proc` ( `definer` char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, @@ -218,7 +218,7 @@ event CREATE TABLE `event` ( `ends` datetime DEFAULT NULL, `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `originator` int(10) unsigned NOT NULL, `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', diff --git a/mysql-test/main/system_mysql_db_fix50030.result b/mysql-test/main/system_mysql_db_fix50030.result index fdc708db313..b3a0caa282f 100644 --- a/mysql-test/main/system_mysql_db_fix50030.result +++ b/mysql-test/main/system_mysql_db_fix50030.result @@ -192,7 +192,7 @@ proc CREATE TABLE `proc` ( `definer` char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, @@ -218,7 +218,7 @@ event CREATE TABLE `event` ( `ends` datetime DEFAULT NULL, `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `originator` int(10) unsigned NOT NULL, `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', diff --git a/mysql-test/main/system_mysql_db_fix50117.result b/mysql-test/main/system_mysql_db_fix50117.result index d76a2ef923e..abb6fbb38e1 100644 --- a/mysql-test/main/system_mysql_db_fix50117.result +++ b/mysql-test/main/system_mysql_db_fix50117.result @@ -192,7 +192,7 @@ proc CREATE TABLE `proc` ( `definer` char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, @@ -218,7 +218,7 @@ event CREATE TABLE `event` ( `ends` datetime DEFAULT NULL, `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', - `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') NOT NULL DEFAULT '', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `originator` int(10) unsigned NOT NULL, `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', diff --git a/mysql-test/main/type_date_round.result b/mysql-test/main/type_date_round.result new file mode 100644 index 00000000000..0da78c6afe5 --- /dev/null +++ b/mysql-test/main/type_date_round.result @@ -0,0 +1,174 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; +# +# DATE: SET +# +CREATE TABLE t1 (a DATE, b DATETIME(4)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +SELECT a FROM t1; +a +2000-12-31 +DROP TABLE t1; +CREATE TABLE t1 (a DATE, b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2000-12-31 +2000-12-31 +DROP TABLE t1; +CREATE TABLE t1 (a DATE, b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2000-12-31 +2000-12-31 +DROP TABLE t1; +# +# DATE: ALTER +# +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a DATE; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +SELECT a FROM t1; +a +2000-12-31 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a DATE; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2000-12-31 +2000-12-31 +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a DATE; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2000-12-31 +2000-12-31 +DROP TABLE t1; +# +# DATE: CAST +# +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +SELECT a, CAST(a AS DATE) FROM t1; +a CAST(a AS DATE) +2000-12-31 23:59:59.9999 2000-12-31 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +SELECT a, CAST(a AS DATE) FROM t1; +a CAST(a AS DATE) +2000-12-31 23:59:59.9999 2000-12-31 +2000-12-31 23:59:59.9999999 2000-12-31 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +SELECT a, CAST(a AS DATE) FROM t1; +a CAST(a AS DATE) +20001231235959.9999000000 2000-12-31 +20001231235959.9999999000 2000-12-31 +DROP TABLE t1; +# +# Equal field propagation +# +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES (20010101); +INSERT INTO t1 VALUES (20010102); +SELECT * FROM t1 WHERE a= 20010101235959.9999999; +a +2001-01-02 +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999'; +a +2001-01-02 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND a>='2001-01-01 23:59:59.9999999'; +a +2001-01-02 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND CONCAT(a)='2001-01-02'; +a +2001-01-02 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND a>='2001-01-01 23:59:59.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = DATE'2001-01-02' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND CONCAT(a)='2001-01-02'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = DATE'2001-01-02' +DROP TABLE t1; +# +# Comparing non-temporal to DATE +# +# Although conversion from non-temporal to DATE (e.g. on SET) does not round, +# comparison between non-temporal to DATE is performed as DATETIME. +# So rounding does happen here. +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +SELECT * FROM t1 WHERE a=DATE'2001-01-02'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE CONCAT(a)=DATE'2001-01-02'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE COALESCE(a)=DATE'2001-01-02'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=DATE'2001-01-02'; +a +20010101235959.9999999 +SELECT * FROM t1 WHERE COALESCE(a)=DATE'2001-01-02'; +a +20010101235959.9999999 +DROP TABLE t1; diff --git a/mysql-test/main/type_date_round.test b/mysql-test/main/type_date_round.test new file mode 100644 index 00000000000..61e1d0a401c --- /dev/null +++ b/mysql-test/main/type_date_round.test @@ -0,0 +1,113 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; + +--echo # +--echo # DATE: SET +--echo # + +CREATE TABLE t1 (a DATE, b DATETIME(4)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DATE, b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DATE, b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # DATE: ALTER +--echo # + +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a DATE; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a DATE; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a DATE; +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # DATE: CAST +--echo # + +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +SELECT a, CAST(a AS DATE) FROM t1; +DROP TABLE t1; + +# This truncates microseconds but rounds nanoseconds (MySQL Bug #92475) +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +SELECT a, CAST(a AS DATE) FROM t1; +DROP TABLE t1; + +# This truncates microseconds but rounds nanoseconds (MySQL Bug #92475) +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +SELECT a, CAST(a AS DATE) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Equal field propagation +--echo # + +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES (20010101); +INSERT INTO t1 VALUES (20010102); +# DATE is compared to non-temporal as DATETIME +# In the below queries nanoseconds should round to microseconds +SELECT * FROM t1 WHERE a= 20010101235959.9999999; +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999'; +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND a>='2001-01-01 23:59:59.9999999'; +SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND CONCAT(a)='2001-01-02'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND a>='2001-01-01 23:59:59.9999999'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='2001-01-01 23:59:59.9999999' AND CONCAT(a)='2001-01-02'; +DROP TABLE t1; + + +--echo # +--echo # Comparing non-temporal to DATE +--echo # + +--echo # Although conversion from non-temporal to DATE (e.g. on SET) does not round, +--echo # comparison between non-temporal to DATE is performed as DATETIME. +--echo # So rounding does happen here. + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +SELECT * FROM t1 WHERE a=DATE'2001-01-02'; +SELECT * FROM t1 WHERE CONCAT(a)=DATE'2001-01-02'; +SELECT * FROM t1 WHERE COALESCE(a)=DATE'2001-01-02'; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=DATE'2001-01-02'; +SELECT * FROM t1 WHERE COALESCE(a)=DATE'2001-01-02'; +DROP TABLE t1; diff --git a/mysql-test/main/type_datetime_round.result b/mysql-test/main/type_datetime_round.result new file mode 100644 index 00000000000..c6584223268 --- /dev/null +++ b/mysql-test/main/type_datetime_round.result @@ -0,0 +1,205 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; +# +# DATETIME: SET +# +CREATE TABLE t1 (a DATETIME(3), b DATETIME(4)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME(3), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME(3), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +# +# DATETIME: ALTER +# +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a DATETIME(3); +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a DATETIME(3); +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a DATETIME(3); +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +# +# Corner case: +# ALTER DATETIME to a shorter DATETIME +# All values round, maximum possible value truncates. +# +SET time_zone='+00:00'; +CREATE TABLE t1 (ID INT, a DATETIME(6), comment VARCHAR(64)); +INSERT INTO t1 VALUES (0, '9999-12-30 23:59:58.999999', 'Should round'); +INSERT INTO t1 VALUES (1, '9999-12-31 22:59:59.999999', 'Should round'); +INSERT INTO t1 VALUES (2, '9999-12-31 23:59:58.999999', 'Should round'); +INSERT INTO t1 VALUES (3, '9999-12-31 23:59:59.999999', 'Should truncate'); +ALTER TABLE t1 MODIFY a DATETIME(5); +Warnings: +Warning 1264 Out of range value for column 'a' at row 4 +SELECT * FROM t1; +ID a comment +0 9999-12-30 23:59:59.00000 Should round +1 9999-12-31 23:00:00.00000 Should round +2 9999-12-31 23:59:59.00000 Should round +3 9999-12-31 23:59:59.99999 Should truncate +DROP TABLE t1; +SET time_zone=DEFAULT; +# +# NOW +# +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a DATETIME(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-01 00:00:00.0000 +3 2011-01-01 00:00:00.0000 +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# +# DATETIME: CAST +# +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +a CAST(a AS DATETIME(3)) +2000-12-31 23:59:59.9999 2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +a CAST(a AS DATETIME(3)) +2000-12-31 23:59:59.9999 2001-01-01 00:00:00.000 +2000-12-31 23:59:59.9999999 2001-01-01 00:00:00.000 +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +a CAST(a AS DATETIME(3)) +20001231235959.9999000000 2001-01-01 00:00:00.000 +20001231235959.9999999000 2001-01-01 00:00:00.000 +DROP TABLE t1; +# +# Equal field propagation +# +CREATE TABLE t1 (a DATETIME(6)); +INSERT INTO t1 VALUES (20010101235959.999999); +INSERT INTO t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=20010101235959.9999999; +a +2001-01-02 00:00:00.000000 +SELECT * FROM t1 WHERE a='20010101235959.9999999'; +a +2001-01-02 00:00:00.000000 +Warnings: +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +SELECT * FROM t1 WHERE a='20010101235959.9999999' AND a>='20010101235959.9999999'; +a +2001-01-02 00:00:00.000000 +Warnings: +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +SELECT * FROM t1 WHERE a='20010101235959.9999999' AND CONCAT(a)='2001-01-02 00:00:00.000000'; +a +2001-01-02 00:00:00.000000 +Warnings: +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='20010101235959.9999999' AND a>='20010101235959.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-02 00:00:00' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='20010101235959.9999999' AND CONCAT(a)='2001-01-02 00:00:00.000000'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect datetime value: '20010101235959.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'2001-01-02 00:00:00' +DROP TABLE t1; +# +# Comparing non-temporal to DATETIME +# +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-02 00:00:00'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE CONCAT(a)=TIMESTAMP'2001-01-02 00:00:00'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1 WHERE COALESCE(a)=TIMESTAMP'2001-01-02 00:00:00'; +a +2001-01-01 23:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-02 00:00:00'; +a +20010101235959.9999999 +SELECT * FROM t1 WHERE COALESCE(a)=TIMESTAMP'2001-01-02 00:00:00'; +a +20010101235959.9999999 +DROP TABLE t1; +# +# Literal corner case +# +SELECT TIMESTAMP'9999-12-31 23:59:59.999999'; +TIMESTAMP'9999-12-31 23:59:59.999999' +9999-12-31 23:59:59.999999 +SELECT TIME'9999-12-31 23:59:59.9999999'; +ERROR HY000: Incorrect TIME value: '9999-12-31 23:59:59.9999999' diff --git a/mysql-test/main/type_datetime_round.test b/mysql-test/main/type_datetime_round.test new file mode 100644 index 00000000000..15aec2cf4e0 --- /dev/null +++ b/mysql-test/main/type_datetime_round.test @@ -0,0 +1,147 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; + +--echo # +--echo # DATETIME: SET +--echo # + +CREATE TABLE t1 (a DATETIME(3), b DATETIME(4)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DATETIME(3), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DATETIME(3), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # DATETIME: ALTER +--echo # + +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a DATETIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a DATETIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a DATETIME(3); +SELECT a FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Corner case: +--echo # ALTER DATETIME to a shorter DATETIME +--echo # All values round, maximum possible value truncates. +--echo # + +SET time_zone='+00:00'; +CREATE TABLE t1 (ID INT, a DATETIME(6), comment VARCHAR(64)); +INSERT INTO t1 VALUES (0, '9999-12-30 23:59:58.999999', 'Should round'); +INSERT INTO t1 VALUES (1, '9999-12-31 22:59:59.999999', 'Should round'); +INSERT INTO t1 VALUES (2, '9999-12-31 23:59:58.999999', 'Should round'); +INSERT INTO t1 VALUES (3, '9999-12-31 23:59:59.999999', 'Should truncate'); +ALTER TABLE t1 MODIFY a DATETIME(5); +SELECT * FROM t1; +DROP TABLE t1; +SET time_zone=DEFAULT; + + +--echo # +--echo # NOW +--echo # + +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a DATETIME(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + + +--echo # +--echo # DATETIME: CAST +--echo # + +CREATE TABLE t1 (a DATETIME(4)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +SELECT a, CAST(a AS DATETIME(3)) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Equal field propagation +--echo # + +CREATE TABLE t1 (a DATETIME(6)); +INSERT INTO t1 VALUES (20010101235959.999999); +INSERT INTO t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=20010101235959.9999999; +SELECT * FROM t1 WHERE a='20010101235959.9999999'; +SELECT * FROM t1 WHERE a='20010101235959.9999999' AND a>='20010101235959.9999999'; +SELECT * FROM t1 WHERE a='20010101235959.9999999' AND CONCAT(a)='2001-01-02 00:00:00.000000'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='20010101235959.9999999' AND a>='20010101235959.9999999'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='20010101235959.9999999' AND CONCAT(a)='2001-01-02 00:00:00.000000'; +DROP TABLE t1; + +--echo # +--echo # Comparing non-temporal to DATETIME +--echo # + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-02 00:00:00'; +SELECT * FROM t1 WHERE CONCAT(a)=TIMESTAMP'2001-01-02 00:00:00'; +SELECT * FROM t1 WHERE COALESCE(a)=TIMESTAMP'2001-01-02 00:00:00'; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +SELECT * FROM t1 WHERE a=TIMESTAMP'2001-01-02 00:00:00'; +SELECT * FROM t1 WHERE COALESCE(a)=TIMESTAMP'2001-01-02 00:00:00'; +DROP TABLE t1; + +--echo # +--echo # Literal corner case +--echo # + +SELECT TIMESTAMP'9999-12-31 23:59:59.999999'; +--error ER_WRONG_VALUE +SELECT TIME'9999-12-31 23:59:59.9999999'; diff --git a/mysql-test/main/type_time_round.result b/mysql-test/main/type_time_round.result new file mode 100644 index 00000000000..31e97c888db --- /dev/null +++ b/mysql-test/main/type_time_round.result @@ -0,0 +1,260 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; +# +# TIME: SET +# +CREATE TABLE t1 (a TIME(3), b TIME(4)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(3), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999'); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(3), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,0.9999); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(3), b DOUBLE); +INSERT INTO t1 VALUES(NULL,0.9999); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(6), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +SELECT a FROM t1; +a +00:00:01.000000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(6), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +00:00:01.000000 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(6), b DOUBLE); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +00:00:01.000000 +DROP TABLE t1; +# +# TIME: ALTER +# +CREATE TABLE t1 (a TIME(4)); +INSERT INTO t1 VALUES('00:00:00.9999'); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +a +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999'); +INSERT INTO t1 VALUES('00:00:00.9999999'); +ALTER TABLE t1 MODIFY a TIME(3); +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +ALTER TABLE t1 MODIFY a TIME(3); +Warnings: +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +00:00:01.000 +00:00:01.000 +DROP TABLE t1; +# +# TIME: CAST +# +CREATE TABLE t1 (a TIME(4)); +INSERT INTO t1 VALUES('00:00:00.9999'); +SELECT a, CAST(a AS TIME(3)) FROM t1; +a CAST(a AS TIME(3)) +00:00:00.9999 00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999'); +INSERT INTO t1 VALUES('00:00:00.9999999'); +SELECT a, CAST(a AS TIME(3)) FROM t1; +a CAST(a AS TIME(3)) +00:00:00.9999 00:00:01.000 +00:00:00.9999999 00:00:01.000 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(3)) FROM t1; +a CAST(a AS TIME(3)) +0.9999000000 00:00:01.000 +0.9999999000 00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(3)) FROM t1; +a CAST(a AS TIME(3)) +0.9999 00:00:01.000 +0.9999999 00:00:01.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999999'); +SELECT a, CAST(a AS TIME(6)) FROM t1; +a CAST(a AS TIME(6)) +00:00:00.9999999 00:00:01.000000 +Warnings: +Note 1292 Truncated incorrect time value: '00:00:00.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(6)) FROM t1; +a CAST(a AS TIME(6)) +0.9999999000 00:00:01.000000 +DROP TABLE t1; +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(6)) FROM t1; +a CAST(a AS TIME(6)) +0.9999999 00:00:01.000000 +DROP TABLE t1; +# +# NOW +# +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a TIME(4)); +INSERT INTO t1 (a) VALUES (now(6)); +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +id a +1 24:00:00.0000 +2 24:00:00.0000 +3 24:00:00.0000 +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# +# Equal field propagation +# +CREATE TABLE t1 (a TIME(6)); +INSERT INTO t1 VALUES (0.999999); +INSERT INTO t1 VALUES (0.9999999); +SELECT * FROM t1 WHERE a=0.9999999; +a +00:00:01.000000 +SELECT * FROM t1 WHERE a='0.9999999'; +a +00:00:01.000000 +Warnings: +Note 1292 Truncated incorrect time value: '0.9999999' +SELECT * FROM t1 WHERE a='0.9999999' AND a>='0.9999999'; +a +00:00:01.000000 +Warnings: +Note 1292 Truncated incorrect time value: '0.9999999' +Note 1292 Truncated incorrect time value: '0.9999999' +SELECT * FROM t1 WHERE a='0.9999999' AND CONCAT(a)='00:00:01.000000'; +a +00:00:01.000000 +Warnings: +Note 1292 Truncated incorrect time value: '0.9999999' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0.9999999' AND a>='0.9999999'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect time value: '0.9999999' +Note 1292 Truncated incorrect time value: '0.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIME'00:00:01' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0.9999999' AND CONCAT(a)='00:00:01.000000'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1292 Truncated incorrect time value: '0.9999999' +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIME'00:00:01' +DROP TABLE t1; +# +# Comparing non-temporal to TIME +# +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('22:59:59.9999999'); +SELECT * FROM t1 WHERE a=TIME'23:00:00'; +a +22:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '22:59:59.9999999' +SELECT * FROM t1 WHERE CONCAT(a)=TIME'23:00:00'; +a +22:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '22:59:59.9999999' +SELECT * FROM t1 WHERE COALESCE(a)=TIME'23:00:00'; +a +22:59:59.9999999 +Warnings: +Note 1292 Truncated incorrect time value: '22:59:59.9999999' +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (225959.9999999); +SELECT * FROM t1 WHERE a=TIME'23:00:00'; +a +225959.9999999 +SELECT * FROM t1 WHERE COALESCE(a)=TIME'23:00:00'; +a +225959.9999999 +DROP TABLE t1; +# +# Literal corner case +# +SELECT TIME'838:59:59.999999'; +TIME'838:59:59.999999' +838:59:59.999999 +SELECT TIME'838:59:59.9999999'; +ERROR HY000: Incorrect TIME value: '838:59:59.9999999' +SELECT TIME'839:00:00'; +ERROR HY000: Incorrect TIME value: '839:00:00' diff --git a/mysql-test/main/type_time_round.test b/mysql-test/main/type_time_round.test new file mode 100644 index 00000000000..6d4b2d8947a --- /dev/null +++ b/mysql-test/main/type_time_round.test @@ -0,0 +1,184 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; + +--echo # +--echo # TIME: SET +--echo # + +CREATE TABLE t1 (a TIME(3), b TIME(4)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(3), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999'); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(3), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,0.9999); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(3), b DOUBLE); +INSERT INTO t1 VALUES(NULL,0.9999); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(6), b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'00:00:00.9999999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(6), b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIME(6), b DOUBLE); +INSERT INTO t1 VALUES(NULL,0.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # TIME: ALTER +--echo # + +CREATE TABLE t1 (a TIME(4)); +INSERT INTO t1 VALUES('00:00:00.9999'); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999'); +INSERT INTO t1 VALUES('00:00:00.9999999'); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +ALTER TABLE t1 MODIFY a TIME(3); +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # TIME: CAST +--echo # + +CREATE TABLE t1 (a TIME(4)); +INSERT INTO t1 VALUES('00:00:00.9999'); +SELECT a, CAST(a AS TIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999'); +INSERT INTO t1 VALUES('00:00:00.9999999'); +SELECT a, CAST(a AS TIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(3)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('00:00:00.9999999'); +SELECT a, CAST(a AS TIME(6)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(6)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DOUBLE); +INSERT INTO t1 VALUES(0.9999999); +SELECT a, CAST(a AS TIME(6)) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # NOW +--echo # + +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a TIME(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + + +--echo # +--echo # Equal field propagation +--echo # + +CREATE TABLE t1 (a TIME(6)); +INSERT INTO t1 VALUES (0.999999); +INSERT INTO t1 VALUES (0.9999999); +SELECT * FROM t1 WHERE a=0.9999999; +SELECT * FROM t1 WHERE a='0.9999999'; +SELECT * FROM t1 WHERE a='0.9999999' AND a>='0.9999999'; +SELECT * FROM t1 WHERE a='0.9999999' AND CONCAT(a)='00:00:01.000000'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0.9999999' AND a>='0.9999999'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='0.9999999' AND CONCAT(a)='00:00:01.000000'; +DROP TABLE t1; + + +--echo # +--echo # Comparing non-temporal to TIME +--echo # + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('22:59:59.9999999'); +SELECT * FROM t1 WHERE a=TIME'23:00:00'; +SELECT * FROM t1 WHERE CONCAT(a)=TIME'23:00:00'; +SELECT * FROM t1 WHERE COALESCE(a)=TIME'23:00:00'; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (225959.9999999); +SELECT * FROM t1 WHERE a=TIME'23:00:00'; +SELECT * FROM t1 WHERE COALESCE(a)=TIME'23:00:00'; +DROP TABLE t1; + +--echo # +--echo # Literal corner case +--echo # + +SELECT TIME'838:59:59.999999'; +--error ER_WRONG_VALUE +SELECT TIME'838:59:59.9999999'; +--error ER_WRONG_VALUE +SELECT TIME'839:00:00'; diff --git a/mysql-test/main/type_timestamp_round.result b/mysql-test/main/type_timestamp_round.result new file mode 100644 index 00000000000..69b2c8fdeba --- /dev/null +++ b/mysql-test/main/type_timestamp_round.result @@ -0,0 +1,164 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; +# +# TIMESTAMP: SET +# +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b TIMESTAMP(4) NULL DEFAULT NULL); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +Warnings: +Note 1265 Data truncated for column 'a' at row 2 +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +# +# TIMESTAMP: ALTER +# +CREATE TABLE t1 (a TIMESTAMP(4) NULL DEFAULT NULL); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +Warnings: +Note 1292 Truncated incorrect datetime value: '2000-12-31 23:59:59.9999999' +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +SELECT a FROM t1; +a +2001-01-01 00:00:00.000 +2001-01-01 00:00:00.000 +DROP TABLE t1; +# +# Corner case: +# ALTER TIMESTAMP to a shorter TIMESTAMP +# All values round, maximum possible value truncates. +# +SET time_zone='+00:00'; +CREATE TABLE t1 (ID INT, a TIMESTAMP(6), comment VARCHAR(64)); +INSERT INTO t1 VALUES (0, '2038-01-18 23:59:59.999999', 'Should round'); +INSERT INTO t1 VALUES (1, '2038-01-19 03:14:06.999999', 'Should round'); +INSERT INTO t1 VALUES (2, '2038-01-19 03:14:07.999999', 'Should truncate'); +ALTER TABLE t1 MODIFY a TIMESTAMP(5); +Warnings: +Warning 1264 Out of range value for column 'a' at row 3 +SELECT * FROM t1; +ID a comment +0 2038-01-19 00:00:00.00000 Should round +1 2038-01-19 03:14:07.00000 Should round +2 2038-01-19 03:14:07.99999 Should truncate +DROP TABLE t1; +SET time_zone=DEFAULT; +# +# NOW +# +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a TIMESTAMP(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-01 00:00:00.0000 +3 2011-01-01 00:00:00.0000 +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# +# DATETIME to TIMESTAMP conversion with DST change +# +SET sql_mode=IF(@@version LIKE '%MariaDB%', +'STRICT_ALL_TABLES,TIME_ROUND_FRACTIONAL', +'STRICT_ALL_TABLES'); +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.0' /* Winter time */); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.9' /* Rounds to the DST gap */); +ERROR 22007: Incorrect datetime value: '2010-03-28 01:59:59.9' for column 'a' at row 1 +SELECT * FROM t1; +a +2010-03-28 01:59:59 +DROP TABLE t1; +SET time_zone=DEFAULT; +SET sql_mode=@default_sql_mode; +SET sql_mode=IF(@@version LIKE '%MariaDB%','TIME_ROUND_FRACTIONAL',''); +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.0' /* Winter time */); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.9' /* Rounds to the DST gap, then gets fixed to the first second of the summer time */); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'a' at row 1 +SELECT a, UNIX_TIMESTAMP(a) FROM t1; +a UNIX_TIMESTAMP(a) +2010-03-28 01:59:59 1269730799 +2010-03-28 03:00:00 1269730800 +DROP TABLE t1; +SET time_zone=DEFAULT; +SET sql_mode=@default_sql_mode; +# +# Comparing non-temporal to TIMESTAMP +# +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +CREATE TABLE t2 (a TIMESTAMP); +INSERT INTO t2 VALUES ('2001-01-02 00:00:00'); +SELECT * FROM t1,t2 WHERE t1.a=t2.a; +a a +2001-01-01 23:59:59.9999999 2001-01-02 00:00:00 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1,t2 WHERE CONCAT(t1.a)=t2.a; +a a +2001-01-01 23:59:59.9999999 2001-01-02 00:00:00 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a; +a a +2001-01-01 23:59:59.9999999 2001-01-02 00:00:00 +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-01-01 23:59:59.9999999' +DROP TABLE t1,t2; +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +CREATE TABLE t2 (a TIMESTAMP); +INSERT INTO t2 VALUES ('2001-01-02 00:00:00'); +SELECT * FROM t1,t2 WHERE t1.a=t2.a; +a a +20010101235959.9999999 2001-01-02 00:00:00 +SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a; +a a +20010101235959.9999999 2001-01-02 00:00:00 +DROP TABLE t1,t2; diff --git a/mysql-test/main/type_timestamp_round.test b/mysql-test/main/type_timestamp_round.test new file mode 100644 index 00000000000..2ed01cc2a82 --- /dev/null +++ b/mysql-test/main/type_timestamp_round.test @@ -0,0 +1,138 @@ +SET sql_mode=IF(@@version LIKE '%MariaDB%', 'TIME_ROUND_FRACTIONAL', ''); +SET @default_sql_mode=@@sql_mode; + +--echo # +--echo # TIMESTAMP: SET +--echo # + +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b TIMESTAMP(4) NULL DEFAULT NULL); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b VARCHAR(64)); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES(NULL,'2000-12-31 23:59:59.9999999'); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a TIMESTAMP(3) NULL DEFAULT NULL, b DECIMAL(38,10)); +INSERT INTO t1 VALUES(NULL,20001231235959.9999); +INSERT INTO t1 VALUES(NULL,20001231235959.9999999); +UPDATE t1 SET a=b; +SELECT a FROM t1; +DROP TABLE t1; + +--echo # +--echo # TIMESTAMP: ALTER +--echo # + +CREATE TABLE t1 (a TIMESTAMP(4) NULL DEFAULT NULL); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999'); +INSERT INTO t1 VALUES('2000-12-31 23:59:59.9999999'); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +SELECT a FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a DECIMAL(38,10)); +INSERT INTO t1 VALUES(20001231235959.9999); +INSERT INTO t1 VALUES(20001231235959.9999999); +ALTER TABLE t1 MODIFY a TIMESTAMP(3) NULL DEFAULT NULL; +SELECT a FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Corner case: +--echo # ALTER TIMESTAMP to a shorter TIMESTAMP +--echo # All values round, maximum possible value truncates. +--echo # + +SET time_zone='+00:00'; +CREATE TABLE t1 (ID INT, a TIMESTAMP(6), comment VARCHAR(64)); +INSERT INTO t1 VALUES (0, '2038-01-18 23:59:59.999999', 'Should round'); +INSERT INTO t1 VALUES (1, '2038-01-19 03:14:06.999999', 'Should round'); +INSERT INTO t1 VALUES (2, '2038-01-19 03:14:07.999999', 'Should truncate'); +ALTER TABLE t1 MODIFY a TIMESTAMP(5); +SELECT * FROM t1; +DROP TABLE t1; +SET time_zone=DEFAULT; + +--echo # +--echo # NOW +--echo # + +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE OR REPLACE TABLE t1 (id SERIAL, a TIMESTAMP(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIMESTAMP(6)); +INSERT INTO t1 (a) VALUES (CURRENT_TIME(6)); +SELECT * FROM t1; +DROP TABLE t1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + + +--echo # +--echo # DATETIME to TIMESTAMP conversion with DST change +--echo # + +--disable_warnings +SET sql_mode=IF(@@version LIKE '%MariaDB%', + 'STRICT_ALL_TABLES,TIME_ROUND_FRACTIONAL', + 'STRICT_ALL_TABLES'); +--enable_warnings +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.0' /* Winter time */); +--error ER_TRUNCATED_WRONG_VALUE +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.9' /* Rounds to the DST gap */); +SELECT * FROM t1; +DROP TABLE t1; +SET time_zone=DEFAULT; +--disable_warnings +SET sql_mode=@default_sql_mode; +--enable_warnings + +SET sql_mode=IF(@@version LIKE '%MariaDB%','TIME_ROUND_FRACTIONAL',''); +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.0' /* Winter time */); +INSERT INTO t1 VALUES ('2010-03-28 01:59:59.9' /* Rounds to the DST gap, then gets fixed to the first second of the summer time */); +SELECT a, UNIX_TIMESTAMP(a) FROM t1; +DROP TABLE t1; +SET time_zone=DEFAULT; +--disable_warnings +SET sql_mode=@default_sql_mode; +--enable_warnings + + +--echo # +--echo # Comparing non-temporal to TIMESTAMP +--echo # + +CREATE TABLE t1 (a VARCHAR(64)); +INSERT t1 VALUES ('2001-01-01 23:59:59.9999999'); +CREATE TABLE t2 (a TIMESTAMP); +INSERT INTO t2 VALUES ('2001-01-02 00:00:00'); +SELECT * FROM t1,t2 WHERE t1.a=t2.a; +SELECT * FROM t1,t2 WHERE CONCAT(t1.a)=t2.a; +SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a; +DROP TABLE t1,t2; + +CREATE TABLE t1 (a DECIMAL(32,7)); +INSERT t1 VALUES (20010101235959.9999999); +CREATE TABLE t2 (a TIMESTAMP); +INSERT INTO t2 VALUES ('2001-01-02 00:00:00'); +SELECT * FROM t1,t2 WHERE t1.a=t2.a; +SELECT * FROM t1,t2 WHERE COALESCE(t1.a)=t2.a; +DROP TABLE t1,t2; diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result index 5a7fdbd63c9..e682a8599c4 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result @@ -61,7 +61,7 @@ def mysql event modified 9 '0000-00-00 00:00:00' NO timestamp NULL NULL NULL NUL def mysql event name 2 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) PRI select,insert,update,references NEVER NULL def mysql event on_completion 14 'DROP' NO enum 8 24 NULL NULL NULL utf8 utf8_general_ci enum('DROP','PRESERVE') select,insert,update,references NEVER NULL def mysql event originator 17 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references NEVER NULL -def mysql event sql_mode 15 '' NO set 539 1617 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') select,insert,update,references NEVER NULL +def mysql event sql_mode 15 '' NO set 561 1683 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') select,insert,update,references NEVER NULL def mysql event starts 11 NULL YES datetime NULL NULL NULL NULL 0 NULL NULL datetime select,insert,update,references NEVER NULL def mysql event status 13 'ENABLED' NO enum 18 54 NULL NULL NULL utf8 utf8_general_ci enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') select,insert,update,references NEVER NULL def mysql event time_zone 18 'SYSTEM' NO char 64 64 NULL NULL NULL latin1 latin1_swedish_ci char(64) select,insert,update,references NEVER NULL @@ -133,7 +133,7 @@ def mysql proc returns 10 NULL NO longblob 4294967295 4294967295 NULL NULL NULL def mysql proc security_type 8 'DEFINER' NO enum 7 21 NULL NULL NULL utf8 utf8_general_ci enum('INVOKER','DEFINER') select,insert,update,references NEVER NULL def mysql proc specific_name 4 '' NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references NEVER NULL def mysql proc sql_data_access 6 'CONTAINS_SQL' NO enum 17 51 NULL NULL NULL utf8 utf8_general_ci enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') select,insert,update,references NEVER NULL -def mysql proc sql_mode 15 '' NO set 539 1617 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') select,insert,update,references NEVER NULL +def mysql proc sql_mode 15 '' NO set 561 1683 NULL NULL NULL utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') select,insert,update,references NEVER NULL def mysql proc type 3 NULL NO enum 12 36 NULL NULL NULL utf8 utf8_general_ci enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') PRI select,insert,update,references NEVER NULL def mysql procs_priv Db 2 '' NO char 64 192 NULL NULL NULL utf8 utf8_bin char(64) PRI select,insert,update,references NEVER NULL def mysql procs_priv Grantor 6 '' NO char 141 423 NULL NULL NULL utf8 utf8_bin char(141) MUL select,insert,update,references NEVER NULL @@ -380,7 +380,7 @@ NULL mysql event starts datetime NULL NULL NULL NULL datetime NULL mysql event ends datetime NULL NULL NULL NULL datetime 3.0000 mysql event status enum 18 54 utf8 utf8_general_ci enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') 3.0000 mysql event on_completion enum 8 24 utf8 utf8_general_ci enum('DROP','PRESERVE') -3.0000 mysql event sql_mode set 539 1617 utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') +3.0000 mysql event sql_mode set 561 1683 utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') 3.0000 mysql event comment char 64 192 utf8 utf8_bin char(64) NULL mysql event originator int NULL NULL NULL NULL int(10) unsigned 1.0000 mysql event time_zone char 64 64 latin1 latin1_swedish_ci char(64) @@ -451,7 +451,7 @@ NULL mysql innodb_table_stats sum_of_other_index_sizes bigint NULL NULL NULL NUL 3.0000 mysql proc definer char 141 423 utf8 utf8_bin char(141) NULL mysql proc created timestamp NULL NULL NULL NULL timestamp NULL mysql proc modified timestamp NULL NULL NULL NULL timestamp -3.0000 mysql proc sql_mode set 539 1617 utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') +3.0000 mysql proc sql_mode set 561 1683 utf8 utf8_general_ci set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') 1.0000 mysql proc comment text 65535 65535 utf8 utf8_bin text 3.0000 mysql proc character_set_client char 32 96 utf8 utf8_bin char(32) 3.0000 mysql proc collation_connection char 32 96 utf8 utf8_bin char(32) diff --git a/mysql-test/suite/rpl/r/rpl_temporal_round.result b/mysql-test/suite/rpl/r/rpl_temporal_round.result new file mode 100644 index 00000000000..df8cc431a74 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_temporal_round.result @@ -0,0 +1,50 @@ +include/master-slave.inc +[connection master] +SET sql_mode=TIME_ROUND_FRACTIONAL; +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); +CREATE TABLE t1 (id SERIAL, a TIMESTAMP(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES ('2011-01-01 23:59:59.999999'); +CREATE TABLE t2 (id SERIAL, a DATETIME(4)); +INSERT INTO t2 (a) VALUES (now(6)); +INSERT INTO t2 (a) VALUES ('2011-01-01 23:59:59.999999'); +CREATE TABLE t3 (id SERIAL, a TIME(4)); +INSERT INTO t3 (a) VALUES (now(6)); +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +INSERT INTO t3 (a) VALUES ('2011-01-01 23:59:59.999999'); +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +SELECT * FROM t1; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-02 00:00:00.0000 +SELECT * FROM t2; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-02 00:00:00.0000 +SELECT * FROM t3; +id a +1 24:00:00.0000 +2 24:00:00.0000 +connection slave; +connection slave; +SET time_zone='+00:00'; +SELECT * FROM t1; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-02 00:00:00.0000 +SELECT * FROM t2; +id a +1 2011-01-01 00:00:00.0000 +2 2011-01-02 00:00:00.0000 +SELECT * FROM t3; +id a +1 24:00:00.0000 +2 24:00:00.0000 +connection master; +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_temporal_round.test b/mysql-test/suite/rpl/t/rpl_temporal_round.test new file mode 100644 index 00000000000..c13c18bddb5 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_temporal_round.test @@ -0,0 +1,35 @@ +--source include/master-slave.inc + +SET sql_mode=TIME_ROUND_FRACTIONAL; +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2010-12-31 23:59:59.999999'); + +CREATE TABLE t1 (id SERIAL, a TIMESTAMP(4)); +INSERT INTO t1 (a) VALUES (now(6)); +INSERT INTO t1 (a) VALUES ('2011-01-01 23:59:59.999999'); + +CREATE TABLE t2 (id SERIAL, a DATETIME(4)); +INSERT INTO t2 (a) VALUES (now(6)); +INSERT INTO t2 (a) VALUES ('2011-01-01 23:59:59.999999'); + +CREATE TABLE t3 (id SERIAL, a TIME(4)); +INSERT INTO t3 (a) VALUES (now(6)); +INSERT INTO t3 (a) VALUES ('2011-01-01 23:59:59.999999'); + +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; + +sync_slave_with_master; +connection slave; +SET time_zone='+00:00'; +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; + +connection master; +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; + +--source include/rpl_end.inc diff --git a/mysql-test/suite/sys_vars/r/sql_mode_basic.result b/mysql-test/suite/sys_vars/r/sql_mode_basic.result index a200f620a7c..d911e80b780 100644 --- a/mysql-test/suite/sys_vars/r/sql_mode_basic.result +++ b/mysql-test/suite/sys_vars/r/sql_mode_basic.result @@ -367,7 +367,15 @@ SELECT @@global.sql_mode; @@global.sql_mode REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,IGNORE_BAD_TABLE_OPTIONS,ONLY_FULL_GROUP_BY,NO_UNSIGNED_SUBTRACTION,NO_DIR_IN_CREATE,POSTGRESQL,ORACLE,MSSQL,DB2,MAXDB,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,MYSQL323,MYSQL40,ANSI,NO_AUTO_VALUE_ON_ZERO,NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,HIGH_NOT_PRECEDENCE,NO_ENGINE_SUBSTITUTION,PAD_CHAR_TO_FULL_LENGTH,EMPTY_STRING_IS_NULL,SIMULTANEOUS_ASSIGNMENT SET @@global.sql_mode = 17179869184; -ERROR 42000: Variable 'sql_mode' can't be set to the value of '17179869184' +SELECT @@global.sql_mode; +@@global.sql_mode +TIME_ROUND_FRACTIONAL +SET @@global.sql_mode = 34359738367; +SELECT @@global.sql_mode; +@@global.sql_mode +REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,IGNORE_BAD_TABLE_OPTIONS,ONLY_FULL_GROUP_BY,NO_UNSIGNED_SUBTRACTION,NO_DIR_IN_CREATE,POSTGRESQL,ORACLE,MSSQL,DB2,MAXDB,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,MYSQL323,MYSQL40,ANSI,NO_AUTO_VALUE_ON_ZERO,NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,HIGH_NOT_PRECEDENCE,NO_ENGINE_SUBSTITUTION,PAD_CHAR_TO_FULL_LENGTH,EMPTY_STRING_IS_NULL,SIMULTANEOUS_ASSIGNMENT,TIME_ROUND_FRACTIONAL +SET @@global.sql_mode = 34359738368; +ERROR 42000: Variable 'sql_mode' can't be set to the value of '34359738368' SET @@global.sql_mode = 0.4; ERROR 42000: Incorrect argument type to variable 'sql_mode' '#---------------------FN_DYNVARS_152_08----------------------#' diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 26acbfad9c6..56f7a136983 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -4755,7 +4755,7 @@ VARIABLE_COMMENT Sets the sql mode NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,IGNORE_BAD_TABLE_OPTIONS,ONLY_FULL_GROUP_BY,NO_UNSIGNED_SUBTRACTION,NO_DIR_IN_CREATE,POSTGRESQL,ORACLE,MSSQL,DB2,MAXDB,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,MYSQL323,MYSQL40,ANSI,NO_AUTO_VALUE_ON_ZERO,NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,HIGH_NOT_PRECEDENCE,NO_ENGINE_SUBSTITUTION,PAD_CHAR_TO_FULL_LENGTH,EMPTY_STRING_IS_NULL,SIMULTANEOUS_ASSIGNMENT +ENUM_VALUE_LIST REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,IGNORE_BAD_TABLE_OPTIONS,ONLY_FULL_GROUP_BY,NO_UNSIGNED_SUBTRACTION,NO_DIR_IN_CREATE,POSTGRESQL,ORACLE,MSSQL,DB2,MAXDB,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,MYSQL323,MYSQL40,ANSI,NO_AUTO_VALUE_ON_ZERO,NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,HIGH_NOT_PRECEDENCE,NO_ENGINE_SUBSTITUTION,PAD_CHAR_TO_FULL_LENGTH,EMPTY_STRING_IS_NULL,SIMULTANEOUS_ASSIGNMENT,TIME_ROUND_FRACTIONAL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME SQL_NOTES diff --git a/mysql-test/suite/sys_vars/t/sql_mode_basic.test b/mysql-test/suite/sys_vars/t/sql_mode_basic.test index b4841ecd3ff..68be8ba969b 100644 --- a/mysql-test/suite/sys_vars/t/sql_mode_basic.test +++ b/mysql-test/suite/sys_vars/t/sql_mode_basic.test @@ -310,8 +310,14 @@ SELECT @@global.sql_mode; SET @@global.sql_mode = 17179869183; SELECT @@global.sql_mode; ---Error ER_WRONG_VALUE_FOR_VAR SET @@global.sql_mode = 17179869184; +SELECT @@global.sql_mode; + +SET @@global.sql_mode = 34359738367; +SELECT @@global.sql_mode; + +--Error ER_WRONG_VALUE_FOR_VAR +SET @@global.sql_mode = 34359738368; # use of decimal values diff --git a/mysql-test/suite/versioning/r/sysvars.result b/mysql-test/suite/versioning/r/sysvars.result index b23742462d1..acc5d62e069 100644 --- a/mysql-test/suite/versioning/r/sysvars.result +++ b/mysql-test/suite/versioning/r/sysvars.result @@ -130,3 +130,14 @@ show status like "Feature_system_versioning"; Variable_name Value Feature_system_versioning 2 drop table t; +# +# MDEV-16991 Rounding vs truncation for TIME, DATETIME, TIMESTAMP +# +SET sql_mode=TIME_ROUND_FRACTIONAL; +SET @@global.system_versioning_asof= timestamp'2001-12-31 23:59:59.9999999'; +Warnings: +Note 1292 Truncated incorrect datetime value: '2001-12-31 23:59:59.9999999' +SELECT @@global.system_versioning_asof; +@@global.system_versioning_asof +2002-01-01 00:00:00.000000 +SET @@global.system_versioning_asof= DEFAULT; diff --git a/mysql-test/suite/versioning/t/sysvars.test b/mysql-test/suite/versioning/t/sysvars.test index 160af12fe02..52fab81b8e6 100644 --- a/mysql-test/suite/versioning/t/sysvars.test +++ b/mysql-test/suite/versioning/t/sysvars.test @@ -87,3 +87,13 @@ select * from t for system_time between '0-0-0' and current_timestamp(6); show status like "Feature_system_versioning"; drop table t; + + +--echo # +--echo # MDEV-16991 Rounding vs truncation for TIME, DATETIME, TIMESTAMP +--echo # + +SET sql_mode=TIME_ROUND_FRACTIONAL; +SET @@global.system_versioning_asof= timestamp'2001-12-31 23:59:59.9999999'; +SELECT @@global.system_versioning_asof; +SET @@global.system_versioning_asof= DEFAULT; diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index 0d59862f26f..6c61ebee425 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -80,7 +80,7 @@ CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsign CREATE TABLE IF NOT EXISTS time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=Aria transactional=1 CHARACTER SET utf8 comment='Leap seconds information for time zones'; -CREATE TABLE IF NOT EXISTS proc (db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum( 'CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns longblob NOT NULL, body longblob NOT NULL, definer char(141) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'IGNORE_BAD_TABLE_OPTIONS', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', 'SIMULTANEOUS_ASSIGNMENT') DEFAULT '' NOT NULL, comment text collate utf8_bin NOT NULL, character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, aggregate enum('NONE', 'GROUP') DEFAULT 'NONE' NOT NULL, PRIMARY KEY (db,name,type)) engine=Aria transactional=1 character set utf8 comment='Stored Procedures'; +CREATE TABLE IF NOT EXISTS proc (db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum( 'CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA') DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns longblob NOT NULL, body longblob NOT NULL, definer char(141) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'IGNORE_BAD_TABLE_OPTIONS', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', 'SIMULTANEOUS_ASSIGNMENT', 'TIME_ROUND_FRACTIONAL') DEFAULT '' NOT NULL, comment text collate utf8_bin NOT NULL, character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, aggregate enum('NONE', 'GROUP') DEFAULT 'NONE' NOT NULL, PRIMARY KEY (db,name,type)) engine=Aria transactional=1 character set utf8 comment='Stored Procedures'; CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(80) binary DEFAULT '' NOT NULL, Routine_name char(64) COLLATE utf8_general_ci DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') NOT NULL, Grantor char(141) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges'; @@ -101,7 +101,7 @@ PREPARE stmt FROM @str; EXECUTE stmt; DROP PREPARE stmt; -CREATE TABLE IF NOT EXISTS event ( db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', name char(64) CHARACTER SET utf8 NOT NULL default '', body longblob NOT NULL, definer char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', execute_at DATETIME default NULL, interval_value int(11) default NULL, interval_field ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') default NULL, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', last_executed DATETIME default NULL, starts DATETIME default NULL, ends DATETIME default NULL, status ENUM('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL default 'ENABLED', on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT') DEFAULT '' NOT NULL, comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', originator INTEGER UNSIGNED NOT NULL, time_zone char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, PRIMARY KEY (db, name) ) engine=Aria transactional=1 DEFAULT CHARSET=utf8 COMMENT 'Events'; +CREATE TABLE IF NOT EXISTS event ( db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', name char(64) CHARACTER SET utf8 NOT NULL default '', body longblob NOT NULL, definer char(141) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', execute_at DATETIME default NULL, interval_value int(11) default NULL, interval_field ENUM('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') default NULL, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', last_executed DATETIME default NULL, starts DATETIME default NULL, ends DATETIME default NULL, status ENUM('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL default 'ENABLED', on_completion ENUM('DROP','PRESERVE') NOT NULL default 'DROP', sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH','EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT','TIME_ROUND_FRACTIONAL') DEFAULT '' NOT NULL, comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', originator INTEGER UNSIGNED NOT NULL, time_zone char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', character_set_client char(32) collate utf8_bin, collation_connection char(32) collate utf8_bin, db_collation char(32) collate utf8_bin, body_utf8 longblob, PRIMARY KEY (db, name) ) engine=Aria transactional=1 DEFAULT CHARSET=utf8 COMMENT 'Events'; SET @create_innodb_table_stats="CREATE TABLE IF NOT EXISTS innodb_table_stats ( database_name VARCHAR(64) NOT NULL, diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index fd68367b42a..eb620322cd0 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -417,7 +417,8 @@ ALTER TABLE proc MODIFY name char(64) DEFAULT '' NOT NULL, 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', - 'SIMULTANEOUS_ASSIGNMENT' + 'SIMULTANEOUS_ASSIGNMENT', + 'TIME_ROUND_FRACTIONAL' ) DEFAULT '' NOT NULL, DEFAULT CHARACTER SET utf8; @@ -553,7 +554,8 @@ ALTER TABLE event MODIFY sql_mode 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH', 'EMPTY_STRING_IS_NULL', - 'SIMULTANEOUS_ASSIGNMENT' + 'SIMULTANEOUS_ASSIGNMENT', + 'TIME_ROUND_FRACTIONAL' ) DEFAULT '' NOT NULL AFTER on_completion; ALTER TABLE event MODIFY name char(64) CHARACTER SET utf8 NOT NULL default ''; diff --git a/sql-common/my_time.c b/sql-common/my_time.c index d16970c0a5f..2aa657ca14e 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -256,6 +256,14 @@ static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status, *val= (ulong) (tmp * log_10_int[6 - (*str - start)]); else *val= tmp; + if (str[0] < end && my_isdigit(&my_charset_latin1, str[0][0])) + { + /* + We don't need the exact nanoseconds value. + Knowing the first digit is enough for rounding. + */ + status->nanoseconds= 100 * (uint)(str[0][0] - '0'); + } if (skip_digits(str, end)) status->warnings|= MYSQL_TIME_NOTE_TRUNCATED; } diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index a6803982171..db056a9f08e 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -479,14 +479,24 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) uint not_used; if (!starts_null) { - table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE); + /* + The expected data type for these columns in mysql.events: + starts, ends, execute_at, last_executed + is DATETIME. No nanosecond truncation should normally be needed, + unless the DBA changes them, e.g. to VARCHAR, DECIMAL, etc. + For this unexpected case let's use the default round mode, + according to the current session settings. + */ + table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE | + thd->temporal_round_mode()); starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } ends_null= table->field[ET_FIELD_ENDS]->is_null(); if (!ends_null) { - table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE); + table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE | + thd->temporal_round_mode()); ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } @@ -502,8 +512,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null)); if (!expression && !execute_at_null) { - if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, - TIME_NO_ZERO_DATE)) + if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) DBUG_RETURN(TRUE); execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } @@ -535,8 +545,8 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null()) { - table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time, - TIME_NO_ZERO_DATE); + table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time, TIME_NO_ZERO_DATE | + thd->temporal_round_mode()); last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,¬_used); } diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index bfda8438885..00d625879de 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -216,7 +216,13 @@ Event_parse_data::init_execute_at(THD *thd) (starts_null && ends_null))); DBUG_ASSERT(starts_null && ends_null); - if (item_execute_at->get_date(thd, <ime, TIME_NO_ZERO_DATE)) + /* + The expected data type is DATETIME. No nanoseconds truncation should + normally be needed. Using the default rounding mode. + See more comments in event_data_object.cc. + */ + if (item_execute_at->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) goto wrong_value; ltime_utc= TIME_to_timestamp(thd,<ime,¬_used); @@ -378,7 +384,8 @@ Event_parse_data::init_starts(THD *thd) if (item_starts->fix_fields(thd, &item_starts)) goto wrong_value; - if (item_starts->get_date(thd, <ime, TIME_NO_ZERO_DATE)) + if (item_starts->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) goto wrong_value; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); @@ -433,7 +440,8 @@ Event_parse_data::init_ends(THD *thd) goto error_bad_params; DBUG_PRINT("info", ("convert to TIME")); - if (item_ends->get_date(thd, <ime, TIME_NO_ZERO_DATE)) + if (item_ends->get_date(thd, <ime, TIME_NO_ZERO_DATE | + thd->temporal_round_mode())) goto error_bad_params; ltime_utc= TIME_to_timestamp(thd, <ime, ¬_used); diff --git a/sql/field.cc b/sql/field.cc index cc95a8c8e84..21d4aea6fb4 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5084,10 +5084,11 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, } -date_mode_t Field_timestamp::sql_mode_for_timestamp(THD *thd) const +date_conv_mode_t Timestamp::sql_mode_for_timestamp(THD *thd) { // We don't want to store invalid or fuzzy datetime values in TIMESTAMP - return date_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE); + return date_conv_mode_t((thd->variables.sql_mode & MODE_NO_ZERO_DATE) | + MODE_NO_ZERO_IN_DATE); } @@ -5096,7 +5097,7 @@ int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) int warn; ErrConvTime str(ltime); THD *thd= get_thd(); - Datetime dt(thd, &warn, ltime, sql_mode_for_timestamp(thd), decimals()); + Datetime dt(thd, &warn, ltime, Timestamp::DatetimeOptions(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, warn); } @@ -5106,7 +5107,7 @@ int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) ErrConvString str(from, len, cs); THD *thd= get_thd(); MYSQL_TIME_STATUS st; - Datetime dt(&st, from, len, cs, sql_mode_for_timestamp(thd), decimals()); + Datetime dt(thd, &st, from, len, cs, Timestamp::DatetimeOptions(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, st.warnings); } @@ -5116,7 +5117,7 @@ int Field_timestamp::store(double nr) int error; ErrConvDouble str(nr); THD *thd= get_thd(); - Datetime dt(&error, nr, sql_mode_for_timestamp(thd), decimals()); + Datetime dt(thd, &error, nr, Timestamp::DatetimeOptions(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5127,14 +5128,29 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) Longlong_hybrid tmp(nr, unsigned_val); ErrConvInteger str(tmp); THD *thd= get_thd(); - Datetime dt(&error, tmp, sql_mode_for_timestamp(thd)); + Datetime dt(thd, &error, tmp, Timestamp::DatetimeOptions(thd)); return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part) { - store_TIMESTAMP(Timestamp(ts, sec_part).trunc(decimals())); + int warn= 0; + time_round_mode_t mode= Datetime::default_round_mode(get_thd()); + store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn)); + if (warn) + { + /* + We're here if rounding would overflow outside of the supported TIMESTAMP + range, so truncation happened instead: + CREATE TABLE t1 (a TIMESTAMP(6)); + INSERT INTO t1 VALUES ('maximum-possible-timestamp.999999'); + ALTER TABLE t1 MODIFY a TIMESTAMP(5); + SELECT * FROM t1; --> 'maximum-possible-timestamp.99999' (5 digits) + Raise a warning, like DATETIME does for '9999-12-31 23:59:59.999999'. + */ + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + } if (ts == 0 && sec_part == 0 && get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) { @@ -5157,7 +5173,7 @@ double Field_timestamp::val_real(void) longlong Field_timestamp::val_int(void) { MYSQL_TIME ltime; - if (get_date(<ime, TIME_NO_ZERO_DATE)) + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) return 0; return ltime.year * 10000000000LL + ltime.month * 100000000LL + @@ -5177,7 +5193,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) to= (char*) val_buffer->ptr(); val_buffer->length(field_length); - if (get_date(<ime, TIME_NO_ZERO_DATE)) + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) { /* Zero time is "000000" */ val_ptr->set(zero_timestamp, field_length, &my_charset_numeric); return val_ptr; @@ -5407,7 +5423,7 @@ my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos, double Field_timestamp_with_dec::val_real(void) { MYSQL_TIME ltime; - if (get_date(<ime, TIME_NO_ZERO_DATE)) + if (get_date(<ime, Datetime::Options(TIME_NO_ZERO_DATE, get_thd()))) return 0; return ltime.year * 1e10 + ltime.month * 1e8 + @@ -5427,7 +5443,7 @@ int Field_timestamp::store_decimal(const my_decimal *d) int error; THD *thd= get_thd(); ErrConvDecimal str(d); - Datetime dt(&error, d, sql_mode_for_timestamp(thd), decimals()); + Datetime dt(thd, &error, d, Timestamp::DatetimeOptions(thd), decimals()); return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5570,7 +5586,8 @@ int Field_datetime::store(const char *from, size_t len, CHARSET_INFO *cs) { MYSQL_TIME_STATUS st; ErrConvString str(from, len, cs); - Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd()), decimals()); + THD *thd= get_thd(); + Datetime dt(thd, &st, from, len, cs, Datetime::Options(thd), decimals()); return store_TIME_with_warning(&dt, &str, st.warnings); } @@ -5578,7 +5595,8 @@ int Field_datetime::store(double nr) { int error; ErrConvDouble str(nr); - Datetime dt(&error, nr, sql_mode_for_dates(get_thd()), decimals()); + THD *thd= get_thd(); + Datetime dt(thd, &error, nr, Datetime::Options(thd), decimals()); return store_TIME_with_warning(&dt, &str, error); } @@ -5588,7 +5606,8 @@ int Field_datetime::store(longlong nr, bool unsigned_val) int error; Longlong_hybrid tmp(nr, unsigned_val); ErrConvInteger str(tmp); - Datetime dt(&error, tmp, sql_mode_for_dates(get_thd())); + THD *thd= get_thd(); + Datetime dt(thd, &error, tmp, Datetime::Options(thd)); return store_TIME_with_warning(&dt, &str, error); } @@ -5597,7 +5616,7 @@ int Field_datetime::store_time_dec(const MYSQL_TIME *ltime, uint dec) int error; ErrConvTime str(ltime); THD *thd= get_thd(); - Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd), decimals()); + Datetime dt(thd, &error, ltime, Datetime::Options(thd), decimals()); return store_TIME_with_warning(&dt, &str, error); } @@ -5606,7 +5625,8 @@ int Field_datetime::store_decimal(const my_decimal *d) { int error; ErrConvDecimal str(d); - Datetime tm(&error, d, sql_mode_for_dates(get_thd()), decimals()); + THD *thd= get_thd(); + Datetime tm(thd, &error, d, Datetime::Options(thd), decimals()); return store_TIME_with_warning(&tm, &str, error); } @@ -5617,7 +5637,7 @@ Field_temporal_with_date::validate_value_in_record(THD *thd, { DBUG_ASSERT(!is_null_in_record(record)); MYSQL_TIME ltime; - return get_TIME(<ime, ptr_in_record(record), sql_mode_for_dates(thd)); + return get_TIME(<ime, ptr_in_record(record), Datetime::Options(thd)); } @@ -5657,7 +5677,8 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, const_item->field_type() != MYSQL_TYPE_TIMESTAMP) || const_item->decimals != decimals()) { - Datetime dt(thd, const_item, date_mode_t(0)); + Datetime::Options opt(TIME_CONV_NONE, thd); + Datetime dt(thd, const_item, opt, decimals()); if (!dt.is_valid_datetime()) return NULL; /* @@ -5672,7 +5693,7 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, case ANY_SUBST: if (!is_temporal_type_with_date(const_item->field_type())) { - Datetime dt(thd, const_item, Datetime::comparison_flags_for_get_date()); + Datetime dt(thd, const_item, Datetime::Options_cmp(thd)); if (!dt.is_valid_datetime()) return NULL; return new (thd->mem_root) @@ -5724,7 +5745,17 @@ int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) ErrConvString str(from, len, cs); MYSQL_TIME_STATUS st; THD *thd= get_thd(); - Time tm(thd, &st, from, len, cs, sql_mode_for_dates(thd), decimals()); + /* + Unlike number-to-time conversion, we need to additionally pass + MODE_NO_ZERO_DATE here (if it presents in the current sql_mode): + SET sql_mode='STRICT_ALL_TABLES,NO_ZERO_DATE'; + INSERT INTO t1 VALUES ('0000-00-00 00:00:00'); -- error + INSERT INTO t1 VALUES (0); -- ok + In the first INSERT we have a zero date. + In the second INSERT we don't have a zero date (it is just a zero time). + */ + Time::Options opt(sql_mode_for_dates(thd), thd); + Time tm(thd, &st, from, len, cs, opt, decimals()); return store_TIME_with_warning(&tm, &str, st.warnings); } @@ -5733,7 +5764,7 @@ int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) { ErrConvTime str(ltime); int warn; - Time tm(&warn, ltime, curdays, decimals()); + Time tm(&warn, ltime, curdays, Time::Options(get_thd()), decimals()); return store_TIME_with_warning(&tm, &str, warn); } @@ -5742,7 +5773,7 @@ int Field_time::store(double nr) { ErrConvDouble str(nr); int was_cut; - Time tm(get_thd(), &was_cut, nr, Time::Options(), decimals()); + Time tm(get_thd(), &was_cut, nr, Time::Options(get_thd()), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5752,8 +5783,14 @@ int Field_time::store(longlong nr, bool unsigned_val) Longlong_hybrid tmp(nr, unsigned_val); ErrConvInteger str(tmp); int was_cut; - // Need fractional digit truncation if nr overflows to '838:59:59.999999' - Time tm(get_thd(), &was_cut, tmp, Time::Options(), decimals()); + THD *thd= get_thd(); + /* + Need fractional digit truncation if nr overflows to '838:59:59.999999'. + The constructor used below will always truncate (never round). + We don't need to care to overwrite the default session rounding mode + from HALF_UP to TRUNCATE. + */ + Time tm(thd, &was_cut, tmp, Time::Options(thd), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5805,7 +5842,7 @@ String *Field_time::val_str(String *str, { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - get_date(<ime, TIME_TIME_ONLY); + get_date(<ime, Datetime::Options(TIME_TIME_ONLY, get_thd())); str->alloc(field_length + 1); str->length(my_time_to_str(<ime, const_cast<char*>(str->ptr()), decimals())); str->set_charset(&my_charset_numeric); @@ -5815,7 +5852,8 @@ String *Field_time::val_str(String *str, bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate) { - if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE)) + date_conv_mode_t tmp= date_conv_mode_t(fuzzydate); + if (!(tmp & TIME_TIME_ONLY) && (tmp & TIME_NO_ZERO_IN_DATE)) { THD *thd= get_thd(); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -5860,7 +5898,7 @@ bool Field_time::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) bool Field_time::send_binary(Protocol *protocol) { MYSQL_TIME ltime; - get_date(<ime, TIME_TIME_ONLY); + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); return protocol->store_time(<ime, decimals()); } @@ -5911,7 +5949,7 @@ int Field_time::store_decimal(const my_decimal *d) { ErrConvDecimal str(d); int was_cut; - Time tm(get_thd(), &was_cut, d, Time::Options(), decimals()); + Time tm(get_thd(), &was_cut, d, Time::Options(get_thd()), decimals()); return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5971,8 +6009,7 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_TIME) { // Get the value of const_item with conversion from DATETIME to TIME - Time tm(get_thd(), const_item, - Time::Options(Time::comparison_flags_for_get_date(), mode)); + Time tm(get_thd(), const_item, Time::Options_cmp(thd, mode)); if (!tm.is_valid_time()) return NULL; /* @@ -5996,10 +6033,6 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, if (const_item->field_type() != MYSQL_TYPE_TIME || const_item->decimals != decimals()) { - Time tm(get_thd(), const_item, - Time::Options(TIME_TIME_ONLY, mode)); - if (!tm.is_valid_time()) - return NULL; /* Note, the value returned in "ltime" can have more fractional digits that decimals(). The Item_time_literal constructor will @@ -6014,6 +6047,10 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, The optimized WHERE will return with "Impossible WHERE", without having to do the full table scan. */ + Time tm(thd, const_item, Time::Options(TIME_TIME_ONLY, thd, mode), + decimals()); + if (!tm.is_valid_time()) + return NULL; return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(), decimals()); } @@ -6027,7 +6064,7 @@ longlong Field_time_with_dec::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - get_date(<ime, TIME_TIME_ONLY); + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); longlong val= TIME_to_ulonglong_time(<ime); return ltime.neg ? -val : val; } @@ -6036,7 +6073,7 @@ double Field_time_with_dec::val_real(void) { ASSERT_COLUMN_MARKED_FOR_READ; MYSQL_TIME ltime; - get_date(<ime, TIME_TIME_ONLY); + get_date(<ime, Time::Options(TIME_TIME_ONLY, get_thd())); return TIME_to_double(<ime); } @@ -6268,7 +6305,8 @@ int Field_date_common::store(const char *from, size_t len, CHARSET_INFO *cs) { MYSQL_TIME_STATUS st; ErrConvString str(from, len, cs); - Datetime dt(&st, from, len, cs, sql_mode_for_dates(get_thd())); + THD *thd= get_thd(); + Datetime dt(thd, &st, from, len, cs, Date::Options(thd), 0); return store_TIME_with_warning(&dt, &str, st.warnings); } @@ -6276,7 +6314,8 @@ int Field_date_common::store(double nr) { int error; ErrConvDouble str(nr); - Datetime dt(&error, nr, sql_mode_for_dates(get_thd())); + THD *thd= get_thd(); + Datetime dt(thd, &error, nr, Date::Options(thd), 0); return store_TIME_with_warning(&dt, &str, error); } @@ -6285,7 +6324,8 @@ int Field_date_common::store(longlong nr, bool unsigned_val) int error; Longlong_hybrid tmp(nr, unsigned_val); ErrConvInteger str(tmp); - Datetime dt(&error, tmp, sql_mode_for_dates(get_thd())); + THD *thd= get_thd(); + Datetime dt(thd, &error, tmp, Date::Options(thd)); return store_TIME_with_warning(&dt, &str, error); } @@ -6294,7 +6334,7 @@ int Field_date_common::store_time_dec(const MYSQL_TIME *ltime, uint dec) int error; ErrConvTime str(ltime); THD *thd= get_thd(); - Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd)); + Datetime dt(thd, &error, ltime, Date::Options(thd), 0); return store_TIME_with_warning(&dt, &str, error); } @@ -6302,7 +6342,8 @@ int Field_date_common::store_decimal(const my_decimal *d) { int error; ErrConvDecimal str(d); - Datetime tm(&error, d, sql_mode_for_dates(get_thd())); + THD *thd= get_thd(); + Datetime tm(thd, &error, d, Date::Options(thd), 0); return store_TIME_with_warning(&tm, &str, error); } @@ -6512,8 +6553,14 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, case ANY_SUBST: if (!is_temporal_type_with_date(const_item->field_type())) { - // Get the value of const_item with conversion from TIME to DATETIME - Datetime dt(thd, const_item, Datetime::comparison_flags_for_get_date()); + /* + DATE is compared to DATETIME-alike non-temporal values + (such as VARCHAR, DECIMAL) as DATETIME, e.g.: + WHERE date_column=20010101235959.0000009 + So here we convert the constant to DATETIME normally. + In case if TIME_ROUND_FRACTIONAL is enabled, nanoseconds will round. + */ + Datetime dt(thd, const_item, Datetime::Options_cmp(thd)); if (!dt.is_valid_datetime()) return NULL; /* @@ -6540,10 +6587,17 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, case IDENTITY_SUBST: if (const_item->field_type() != MYSQL_TYPE_DATE) { - Date d(thd, const_item, date_mode_t(0)); - if (!d.is_valid_date()) + /* + DATE is compared to non-temporal as DATETIME. + We need to convert to DATETIME first, taking into account the + current session rounding mode (even though this is IDENTITY_SUBSTS!), + then convert the result to DATE. + */ + Datetime dt(thd, const_item, Datetime::Options(TIME_CONV_NONE, thd)); + if (!dt.is_valid_datetime()) return NULL; - return new (thd->mem_root) Item_date_literal(thd, d.get_mysql_time()); + return new (thd->mem_root) + Item_date_literal(thd, Date(&dt).get_mysql_time()); } break; } @@ -6693,7 +6747,8 @@ int Field_datetime::set_time() { THD *thd= table->in_use; set_notnull(); - store_datetime(Datetime(thd, thd->query_start_timeval(), decimals())); + // Here we always truncate (not round), no matter what sql_mode is + store_datetime(Datetime(thd, thd->query_start_timeval()).trunc(decimals())); return 0; } diff --git a/sql/field.h b/sql/field.h index 7c5552ec13c..cca3b186668 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1346,7 +1346,6 @@ public: void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); - bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } virtual TYPELIB *get_typelib() const { return NULL; } virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } virtual CHARSET_INFO *charset_for_protocol(void) const @@ -2643,7 +2642,8 @@ public: int save_in_field(Field *to) { MYSQL_TIME ltime; - if (get_date(<ime, date_mode_t(0))) + // For temporal types no truncation needed. Rounding mode is not important. + if (get_date(<ime, TIME_CONV_NONE | TIME_FRAC_NONE)) return to->reset(); return to->store_time_dec(<ime, decimals()); } @@ -2721,7 +2721,6 @@ public: class Field_timestamp :public Field_temporal { protected: - date_mode_t sql_mode_for_timestamp(THD *thd) const; int store_TIME_with_warning(THD *, const Datetime *, const ErrConv *, int warn); virtual void store_TIMEVAL(const timeval &tv) @@ -2771,10 +2770,15 @@ public: { return get_timestamp(ptr, sec_part); } - // This is used by storage/perfschema - void store_TIME(my_time_t timestamp, ulong sec_part) + /* + This method is used by storage/perfschema and + Item_func_now_local::save_in_field(). + */ + void store_TIME(my_time_t ts, ulong sec_part) { - store_TIMESTAMP(Timestamp(timestamp, sec_part).trunc(decimals())); + int warn; + time_round_mode_t mode= Datetime::default_round_mode(get_thd()); + store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn)); } bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate); uchar *pack(uchar *to, const uchar *from, diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 7a064f64570..2f56be60dd6 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -438,19 +438,19 @@ void Field::do_field_temporal(Copy_field *copy, date_mode_t fuzzydate) void Field::do_field_datetime(Copy_field *copy) { - return do_field_temporal(copy, date_mode_t(0)); + return do_field_temporal(copy, Datetime::Options(TIME_CONV_NONE, current_thd)); } void Field::do_field_date(Copy_field *copy) { - return do_field_temporal(copy, date_mode_t(0)); + return do_field_temporal(copy, Date::Options(TIME_CONV_NONE)); } void Field_time::do_field_time(Copy_field *copy) { - return do_field_temporal(copy, TIME_TIME_ONLY); + return do_field_temporal(copy, Time::Options(current_thd)); } diff --git a/sql/filesort.cc b/sql/filesort.cc index cbe79967647..d0cb77ac63d 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1051,7 +1051,10 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item, Sort_param *param) const { MYSQL_TIME buf; - if (item->get_date_result(current_thd, &buf, TIME_INVALID_DATES)) + // This is a temporal type. No nanoseconds. Rounding mode is not important. + DBUG_ASSERT(item->cmp_type() == TIME_RESULT); + static const Temporal::Options opt(TIME_INVALID_DATES, TIME_FRAC_NONE); + if (item->get_date_result(current_thd, &buf, opt)) { DBUG_ASSERT(item->maybe_null); DBUG_ASSERT(item->null_value); diff --git a/sql/item.cc b/sql/item.cc index 26a8aa44b80..99f801bb292 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -118,12 +118,12 @@ void Item::push_note_converted_to_positive_complement(THD *thd) longlong Item::val_datetime_packed_result(THD *thd) { MYSQL_TIME ltime, tmp; - if (get_date_result(thd, <ime, Datetime::comparison_flags_for_get_date())) + if (get_date_result(thd, <ime, Datetime::Options_cmp(thd))) return 0; if (ltime.time_type != MYSQL_TIMESTAMP_TIME) return pack_time(<ime); - if ((null_value= time_to_datetime_with_warn(thd, <ime, - &tmp, date_mode_t(0)))) + if ((null_value= time_to_datetime_with_warn(thd, <ime, &tmp, + TIME_CONV_NONE))) return 0; return pack_time(&tmp); } @@ -305,7 +305,7 @@ int Item::save_date_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; THD *thd= field->table->in_use; - if (get_date(thd, <ime, sql_mode_for_dates(thd))) + if (get_date(thd, <ime, Datetime::Options(thd))) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -9732,7 +9732,8 @@ bool Item_cache_temporal::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzz int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; - if (get_date(field->get_thd(), <ime, date_mode_t(0))) + // This is a temporal type. No nanoseconds, so round mode is not important. + if (get_date(field->get_thd(), <ime, TIME_CONV_NONE | TIME_FRAC_NONE)) return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); int error= field->store_time_dec(<ime, decimals); diff --git a/sql/item.h b/sql/item.h index 2c9395ff89d..2ee2eabddac 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1636,28 +1636,28 @@ public: bool get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool get_date_from_string(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate); bool get_time(THD *thd, MYSQL_TIME *ltime) - { return get_date(thd, ltime, Time::flags_for_get_date()); } + { return get_date(thd, ltime, Time::Options(thd)); } // Get a DATE or DATETIME value in numeric packed format for comparison virtual longlong val_datetime_packed(THD *thd) { - date_mode_t fuzzydate= Datetime::comparison_flags_for_get_date(); - return Datetime(current_thd, this, fuzzydate).to_packed(); + return Datetime(thd, this, Datetime::Options_cmp(thd)).to_packed(); } // Get a TIME value in numeric packed format for comparison virtual longlong val_time_packed(THD *thd) { - return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::Options_cmp(thd)).to_packed(); } longlong val_datetime_packed_result(THD *thd); longlong val_time_packed_result(THD *thd) { MYSQL_TIME ltime; - date_mode_t fuzzydate= Time::comparison_flags_for_get_date(); - return get_date_result(thd, <ime, fuzzydate) ? 0 : pack_time(<ime); + return get_date_result(thd, <ime, Time::Options_cmp(thd)) ? 0 : + pack_time(<ime); } virtual bool get_date_result(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { return get_date(thd, ltime,fuzzydate); } + /* The method allows to determine nullness of a complex expression without fully evaluating it, instead of calling val/result*() then @@ -6441,8 +6441,8 @@ public: Item *make_literal(THD *); longlong val_datetime_packed(THD *thd) { - date_mode_t fuzzy= Datetime::comparison_flags_for_get_date(); - return has_value() ? Datetime(thd, this, fuzzy).to_packed() : 0; + Datetime::Options_cmp opt(thd); + return has_value() ? Datetime(thd, this, opt).to_packed() : 0; } longlong val_time_packed(THD *thd) { @@ -6481,7 +6481,7 @@ public: } longlong val_time_packed(THD *thd) { - return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::Options_cmp(thd)).to_packed(); } longlong val_int() { @@ -6516,7 +6516,7 @@ public: } longlong val_time_packed(THD *thd) { - return Time(thd, this, Time::comparison_flags_for_get_date()).to_packed(); + return Time(thd, this, Time::Options_cmp(thd)).to_packed(); } longlong val_int() { return has_value() ? Date(this).to_longlong() : 0; } double val_real() { return has_value() ? Date(this).to_double() : 0; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9488391f602..15f1f917b35 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2312,7 +2312,8 @@ bool Item_func_ifnull::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydat DBUG_ASSERT(fixed == 1); for (uint i= 0; i < 2; i++) { - Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime_truncation_not_needed dt(thd, args[i], + fuzzydate & ~TIME_FUZZY_DATES); if (!(dt.copy_to_mysql_time(ltime, mysql_timestamp_type()))) return (null_value= false); } @@ -2812,7 +2813,7 @@ Item_func_nullif::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) DBUG_ASSERT(fixed == 1); if (!compare()) return (null_value= true); - Datetime dt(thd, args[2], fuzzydate); + Datetime_truncation_not_needed dt(thd, args[2], fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } @@ -2987,7 +2988,7 @@ bool Item_func_case::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) Item *item= find_item(); if (!item) return (null_value= true); - Datetime dt(thd, item, fuzzydate); + Datetime_truncation_not_needed dt(thd, item, fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } @@ -3339,7 +3340,8 @@ bool Item_func_coalesce::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzyd DBUG_ASSERT(fixed == 1); for (uint i= 0; i < arg_count; i++) { - Datetime dt(thd, args[i], fuzzydate & ~TIME_FUZZY_DATES); + Datetime_truncation_not_needed dt(thd, args[i], + fuzzydate & ~TIME_FUZZY_DATES); if (!dt.copy_to_mysql_time(ltime, mysql_timestamp_type())) return (null_value= false); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index cf210a71433..213f666dd74 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1127,7 +1127,7 @@ public: bool date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Datetime dt(thd, find_item(), fuzzydate); + Datetime_truncation_not_needed dt(thd, find_item(), fuzzydate); return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type())); } bool time_op(THD *thd, MYSQL_TIME *ltime) diff --git a/sql/item_func.cc b/sql/item_func.cc index c176a7e43a7..ee2367a87b0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2610,13 +2610,13 @@ bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); - Time value(thd, args[0], Time::Options(), decimals); + Time value(thd, args[0], Time::Options(thd), decimals); if (!value.is_valid_time()) return (null_value= true); for (uint i= 1; i < arg_count ; i++) { - Time tmp(thd, args[i], Time::Options(), decimals); + Time tmp(thd, args[i], Time::Options(thd), decimals); if (!tmp.is_valid_time()) return (null_value= true); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 6c82c580858..6f2af72dabf 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -4544,7 +4544,7 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) case DYN_COL_DATETIME: case DYN_COL_DATE: args[valpos]->get_date(thd, &vals[i].x.time_value, - sql_mode_for_dates(thd)); + Datetime::Options(thd)); break; case DYN_COL_TIME: args[valpos]->get_time(thd, &vals[i].x.time_value); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index df4dc8cb96d..1b728f561b7 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -131,7 +131,7 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format, timestamp_type cached_timestamp_type, const char **sub_pattern_end, const char *date_time_type, - date_mode_t fuzzydate) + date_conv_mode_t fuzzydate) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -808,8 +808,9 @@ longlong Item_func_period_diff::val_int() longlong Item_func_to_days::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); - return (null_value= !d.is_valid_date()) ? 0 : d.daynr(); + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.daynr(); } @@ -817,14 +818,15 @@ longlong Item_func_to_seconds::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - Datetime dt(current_thd, args[0], TIME_FUZZY_DATES); + // val_int_endpoint() is called only if args[0] is a temporal Item_field + Datetime_from_temporal dt(current_thd, args[0], TIME_FUZZY_DATES); if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ return LONGLONG_MIN; } /* Set to NULL if invalid date, but keep the value */ - null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); + null_value= dt.check_date(TIME_NO_ZEROS); /* Even if the evaluation return NULL, seconds is useful for pruning */ @@ -835,7 +837,11 @@ longlong Item_func_to_seconds::val_int() { DBUG_ASSERT(fixed == 1); THD *thd= current_thd; - Datetime dt(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + /* + Unlike val_int_endpoint(), we cannot use Datetime_from_temporal here. + The argument can be of a non-temporal data type. + */ + Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); return (null_value= !dt.is_valid_datetime()) ? 0 : dt.to_seconds(); } @@ -880,7 +886,8 @@ enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - Datetime dt(current_thd, args[0], date_mode_t(0)); + // val_int_endpoint() is only called if args[0] is a temporal Item_field + Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE); longlong res; if ((null_value= !dt.is_valid_datetime())) { @@ -889,7 +896,7 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) } res= (longlong) dt.daynr(); /* Set to NULL if invalid date, but keep the value */ - null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); + null_value= dt.check_date(TIME_NO_ZEROS); if (null_value) { /* @@ -933,22 +940,25 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp) longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE); - return (null_value= !d.is_valid_date()) ? 0 : d.dayofyear(); + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.dayofyear(); } longlong Item_func_dayofmonth::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], date_mode_t(0)); - return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->day; + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->day; } longlong Item_func_month::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], date_mode_t(0)); - return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->month; + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->month; } @@ -970,9 +980,9 @@ String* Item_func_monthname::val_str(String* str) DBUG_ASSERT(fixed == 1); const char *month_name; uint err; - Date d(current_thd, args[0], date_mode_t(0)); - - if ((null_value= (!d.is_valid_date() || !d.get_mysql_time()->month))) + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); + if ((null_value= (!d.is_valid_datetime() || !d.get_mysql_time()->month))) return (String *) 0; month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1]; @@ -989,21 +999,24 @@ String* Item_func_monthname::val_str(String* str) longlong Item_func_quarter::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], date_mode_t(0)); - return (null_value= !d.is_valid_date()) ? 0 : d.quarter(); + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.quarter(); } longlong Item_func_hour::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour; } longlong Item_func_minute::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute; } @@ -1013,7 +1026,8 @@ longlong Item_func_minute::val_int() longlong Item_func_second::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second; } @@ -1062,8 +1076,8 @@ longlong Item_func_week::val_int() DBUG_ASSERT(fixed == 1); uint week_format; THD *thd= current_thd; - Date d(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); - if ((null_value= !d.is_valid_date())) + Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); + if ((null_value= !d.is_valid_datetime())) return 0; if (arg_count > 1) week_format= (uint)args[1]->val_int(); @@ -1076,8 +1090,9 @@ longlong Item_func_week::val_int() longlong Item_func_yearweek::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); - return (null_value= !d.is_valid_date()) ? 0 : + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.yearweek((week_mode((uint) args[1]->val_int()) | WEEK_YEAR)); } @@ -1085,8 +1100,9 @@ longlong Item_func_yearweek::val_int() longlong Item_func_weekday::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); - return ((null_value= !d.is_valid_date())) ? 0 : + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd)); + return ((null_value= !d.is_valid_datetime())) ? 0 : calc_weekday(d.daynr(), odbc_type) + MY_TEST(odbc_type); } @@ -1123,8 +1139,9 @@ String* Item_func_dayname::val_str(String* str) longlong Item_func_year::val_int() { DBUG_ASSERT(fixed == 1); - Date d(current_thd, args[0], date_mode_t(0)); - return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->year; + THD *thd= current_thd; + Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); + return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->year; } @@ -1155,7 +1172,8 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp) { DBUG_ASSERT(fixed == 1); - Datetime dt(current_thd, args[0], date_mode_t(0)); + // val_int_endpoint() is cally only if args[0] is a temporal Item_field + Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE); if ((null_value= !dt.is_valid_datetime())) { /* got NULL, leave the incl_endp intact */ @@ -1200,7 +1218,7 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds, } THD *thd= current_thd; - Datetime dt(thd, args[0], TIME_NO_ZERO_IN_DATE); + Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)); if ((null_value= !dt.is_valid_datetime())) return true; @@ -1264,7 +1282,8 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e longlong Item_func_time_to_sec::int_op() { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds(); } @@ -1272,7 +1291,8 @@ longlong Item_func_time_to_sec::int_op() my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf) { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); if ((null_value= !tm.is_valid_time())) return 0; const MYSQL_TIME *ltime= tm.get_mysql_time(); @@ -1621,9 +1641,8 @@ int Item_func_now_local::save_in_field(Field *field, bool no_conversions) { THD *thd= field->get_thd(); my_time_t ts= thd->query_start(); - uint dec= MY_MIN(decimals, field->decimals()); - ulong sec_part= dec ? thd->query_start_sec_part() : 0; - sec_part-= my_time_fraction_remainder(sec_part, dec); + ulong sec_part= decimals ? thd->query_start_sec_part() : 0; + sec_part-= my_time_fraction_remainder(sec_part, decimals); field->set_notnull(); ((Field_timestamp*)field)->store_TIME(ts, sec_part); return 0; @@ -1698,9 +1717,10 @@ bool Item_func_sysdate_local::get_date(THD *thd, MYSQL_TIME *res, bool Item_func_sec_to_time::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { DBUG_ASSERT(fixed == 1); - VSec6 sec(thd, args[0], "seconds", LONGLONG_MAX); + VSec9 sec(thd, args[0], "seconds", LONGLONG_MAX); if ((null_value= sec.is_null())) return true; + sec.round(decimals, thd->temporal_round_mode()); if (sec.sec_to_time(ltime, decimals) && !sec.truncated()) sec.make_truncated_warning(thd, "seconds"); return false; @@ -1858,10 +1878,10 @@ String *Item_func_date_format::val_str(String *str) uint size; const MY_LOCALE *lc= 0; DBUG_ASSERT(fixed == 1); - - if ((null_value= args[0]->get_date(current_thd, &l_time, - is_time_format ? TIME_TIME_ONLY : - date_mode_t(0)))) + date_conv_mode_t mode= is_time_format ? TIME_TIME_ONLY : TIME_CONV_NONE; + THD *thd= current_thd; + if ((null_value= args[0]->get_date(thd, &l_time, + Temporal::Options(mode, thd)))) return 0; if (!(format = args[1]->val_str(str)) || !format->length()) @@ -1918,12 +1938,16 @@ bool Item_func_from_unixtime::get_date(THD *thd, MYSQL_TIME *ltime, bzero((char *)ltime, sizeof(*ltime)); ltime->time_type= MYSQL_TIMESTAMP_TIME; - VSec6 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE); + VSec9 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE); DBUG_ASSERT(sec.sec() <= TIMESTAMP_MAX_VALUE); if (sec.is_null() || sec.truncated() || sec.neg()) return (null_value= 1); + sec.round(MY_MIN(decimals, TIME_SECOND_PART_DIGITS), thd->temporal_round_mode()); + if (sec.sec() > TIMESTAMP_MAX_VALUE) + return (null_value= true); // Went out of range after rounding + tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec()); ltime->second_part= sec.usec(); @@ -1952,7 +1976,8 @@ bool Item_func_convert_tz::get_date(THD *thd, MYSQL_TIME *ltime, if ((null_value= (from_tz == 0 || to_tz == 0))) return true; - Datetime *dt= new(ltime) Datetime(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE); + Datetime::Options opt(TIME_NO_ZEROS, thd); + Datetime *dt= new(ltime) Datetime(thd, args[0], opt); if ((null_value= !dt->is_valid_datetime())) return true; @@ -2133,7 +2158,8 @@ uint Extract_source::week(THD *thd) const longlong Item_extract::val_int() { DBUG_ASSERT(fixed == 1); - Extract_source dt(current_thd, args[0], m_date_mode); + THD *thd= current_thd; + Extract_source dt(thd, args[0], m_date_mode); if ((null_value= !dt.is_valid_extract_source())) return 0; switch (int_type) { @@ -2141,7 +2167,7 @@ longlong Item_extract::val_int() case INTERVAL_YEAR_MONTH: return dt.year_month(); case INTERVAL_QUARTER: return dt.quarter(); case INTERVAL_MONTH: return dt.month(); - case INTERVAL_WEEK: return dt.week(current_thd); + case INTERVAL_WEEK: return dt.week(thd); case INTERVAL_DAY: return dt.day(); case INTERVAL_DAY_HOUR: return dt.day_hour(); case INTERVAL_DAY_MINUTE: return dt.day_minute(); @@ -2420,7 +2446,7 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs) bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) { - Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode), + Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode, thd), MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); return (null_value= !tm->is_valid_time()); } @@ -2429,7 +2455,8 @@ bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode) bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; - Date *d= new(ltime) Date(thd, args[0], tmp); + // Force truncation + Date *d= new(ltime) Date(thd, args[0], Date::Options(date_conv_mode_t(tmp))); return (null_value= !d->is_valid_date()); } @@ -2437,7 +2464,9 @@ bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy bool Item_datetime_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY; - Datetime *dt= new(ltime) Datetime(thd, args[0], tmp, + // Force rounding if the current sql_mode says so + Datetime::Options opt(date_conv_mode_t(tmp), thd); + Datetime *dt= new(ltime) Datetime(thd, args[0], opt, MY_MIN(decimals, TIME_SECOND_PART_DIGITS)); return (null_value= !dt->is_valid_datetime()); } @@ -2558,6 +2587,7 @@ bool Item_func_timediff::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy return (null_value= adjust_time_range_with_warn(thd, ltime, decimals)); } + /** MAKETIME(h,m,s) is a time function that calculates a time value from the total number of hours, minutes, and seconds. @@ -2569,7 +2599,7 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy DBUG_ASSERT(fixed == 1); Longlong_hybrid hour(args[0]->val_int(), args[0]->unsigned_flag); longlong minute= args[1]->val_int(); - VSec6 sec(thd, args[2], "seconds", 59); + VSec9 sec(thd, args[2], "seconds", 59); DBUG_ASSERT(sec.sec() <= 59); if (args[0]->null_value || args[1]->null_value || sec.is_null() || @@ -2577,7 +2607,8 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy return (null_value= 1); int warn; - new(ltime) Time(&warn, hour.neg(), hour.abs(), (uint) minute, sec); + new(ltime) Time(&warn, hour.neg(), hour.abs(), (uint) minute, sec, + thd->temporal_round_mode(), decimals); if (warn) { // use check_time_range() to set ltime to the max value depending on dec @@ -2607,7 +2638,8 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy longlong Item_func_microsecond::val_int() { DBUG_ASSERT(fixed == 1); - Time tm(current_thd, args[0], Time::Options_for_cast()); + THD *thd= current_thd; + Time tm(thd, args[0], Time::Options_for_cast(thd)); return ((null_value= !tm.is_valid_time())) ? 0 : tm.get_mysql_time()->second_part; } @@ -2621,12 +2653,12 @@ longlong Item_func_timestamp_diff::val_int() long months= 0; int neg= 1; THD *thd= current_thd; - date_mode_t fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE; + Datetime::Options opt(TIME_NO_ZEROS, thd); null_value= 0; - if (Datetime(thd, args[0], fuzzydate).copy_to_mysql_time(<ime1) || - Datetime(thd, args[1], fuzzydate).copy_to_mysql_time(<ime2)) + if (Datetime(thd, args[0], opt).copy_to_mysql_time(<ime1) || + Datetime(thd, args[1], opt).copy_to_mysql_time(<ime2)) goto null_date; if (calc_time_diff(<ime2,<ime1, 1, @@ -2935,7 +2967,8 @@ bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime, date_time_format.format.length= format->length(); if (extract_date_time(thd, &date_time_format, val->ptr(), val->length(), ltime, tstype, 0, "datetime", - fuzzydate | sql_mode_for_dates(thd))) + date_conv_mode_t(fuzzydate) | + sql_mode_for_dates(thd))) return (null_value=1); return (null_value= 0); } @@ -2943,8 +2976,10 @@ bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime, bool Item_func_last_day::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Date *d= new(ltime) Date(thd, args[0], fuzzydate & ~TIME_TIME_ONLY); - if ((null_value= (!d->is_valid_date() || ltime->month == 0))) + Datetime::Options opt(date_conv_mode_t(fuzzydate & ~TIME_TIME_ONLY), + time_round_mode_t(fuzzydate)); + Datetime *d= new(ltime) Datetime(thd, args[0], opt); + if ((null_value= (!d->is_valid_datetime() || ltime->month == 0))) return true; uint month_idx= ltime->month-1; ltime->day= days_in_month[month_idx]; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 318bef22ad1..3824f87d12b 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -993,12 +993,12 @@ class Item_extract :public Item_int_func, EXTRACT(DAY FROM '-24:00:00') -> -1 */ set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); - m_date_mode= TIME_INTERVAL_DAY; + m_date_mode= Temporal::Options(TIME_INTERVAL_DAY, current_thd); } void set_time_length(uint32 length) { set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); - m_date_mode= TIME_INTERVAL_hhmmssff; + m_date_mode= Temporal::Options(TIME_INTERVAL_hhmmssff, current_thd); } public: const interval_type int_type; // keep it public @@ -1117,7 +1117,7 @@ public: { } String *val_str(String *to) { - Interval_DDhhmmssff it(current_thd, args[0]); + Interval_DDhhmmssff it(current_thd, args[0], m_fsp); null_value= !it.is_valid_interval_DDhhmmssff(); return it.to_string(to, m_fsp); } @@ -1222,7 +1222,7 @@ public: } bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { - Datetime dt(thd, args[0], date_mode_t(0)); + Datetime dt(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd)); if (!dt.is_valid_datetime()) return null_value= true; Interval_DDhhmmssff it(thd, args[1]); @@ -1461,7 +1461,8 @@ public: bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, date_mode_t fuzzy) const { - Datetime dt(thd, item->arguments()[0], date_mode_t(0)); + Datetime::Options opt(TIME_CONV_NONE, thd); + Datetime dt(thd, item->arguments()[0], opt); if (!dt.is_valid_datetime() || dt.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) return (item->null_value= true); @@ -1489,7 +1490,11 @@ public: bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, date_mode_t fuzzy) const { - Date d(thd, item->arguments()[0], date_mode_t(0)); + /* + The first argument is known to be of the DATE data type (not DATETIME). + We don't need rounding here. + */ + Date d(thd, item->arguments()[0], TIME_CONV_NONE); if (!d.is_valid_date() || d.check_date_with_warn(thd, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) return (item->null_value= true); @@ -1542,10 +1547,10 @@ public: bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, date_mode_t fuzzy) const { - if (item->arguments()[0]->get_date(thd, to, date_mode_t(0)) || + if (item->arguments()[0]-> + get_date(thd, to, Datetime::Options(TIME_CONV_NONE, thd)) || (to->time_type != MYSQL_TIMESTAMP_TIME && - check_date_with_warn(thd, to, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, - MYSQL_TIMESTAMP_ERROR))) + check_date_with_warn(thd, to, TIME_NO_ZEROS, MYSQL_TIMESTAMP_ERROR))) return (item->null_value= true); return (item->null_value= add(thd, item->arguments()[1], int_type(item), sub(item), to)); @@ -1581,7 +1586,8 @@ public: MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); - Datetime dt(thd, item->arguments()[0], date_mode_t(0)); + Datetime::Options opt(TIME_CONV_NONE, thd); + Datetime dt(thd, item->arguments()[0], opt); if (!dt.is_valid_datetime()) return item->null_value= true; Interval_DDhhmmssff it(thd, item->arguments()[1]); @@ -1650,7 +1656,8 @@ public: { DBUG_ASSERT(item->is_fixed()); // Detect a proper timestamp type based on the argument values - Temporal_hybrid l_time1(thd, item->arguments()[0], TIME_TIME_ONLY); + Temporal_hybrid l_time1(thd, item->arguments()[0], + Temporal::Options(TIME_TIME_ONLY, thd)); if (!l_time1.is_valid_temporal()) return (item->null_value= true); Interval_DDhhmmssff l_time2(thd, item->arguments()[1]); diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 6946ae0e1e5..c4bb734096f 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -142,7 +142,9 @@ Item_func_trt_id::val_int() else { MYSQL_TIME commit_ts; - if (args[0]->get_date(current_thd, &commit_ts, date_mode_t(0))) + THD *thd= current_thd; + Datetime::Options opt(TIME_CONV_NONE, thd); + if (args[0]->get_date(thd, &commit_ts, opt)) { null_value= true; return 0; diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index de7018c53cb..6927b181d92 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -268,7 +268,8 @@ int str2my_decimal(uint mask, const char *from, size_t length, integer part cannot be larger that 1e18 (otherwise it's an overflow). fractional part is microseconds. */ -bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec) +bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, + ulong *microsec, ulong *nanosec) { int pos; @@ -286,6 +287,7 @@ bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec) } *microsec= d->frac ? static_cast<longlong>(d->buf[pos+1]) / (DIG_BASE/1000000) : 0; + *nanosec= d->frac ? static_cast<longlong>(d->buf[pos+1]) % (DIG_BASE/1000000) : 0; if (pos > 1) { diff --git a/sql/my_decimal.h b/sql/my_decimal.h index b22c686cc90..c196d43e001 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -362,7 +362,8 @@ inline bool str_set_decimal(const my_decimal *val, String *str, } -bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec); +bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, + ulong *microsec, ulong *nanosec); my_decimal *seconds2my_decimal(bool sign, ulonglong sec, ulong microsec, my_decimal *d); diff --git a/sql/sp.cc b/sql/sp.cc index 723f30ec85d..d37ea543219 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -200,7 +200,8 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = "'STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES'," "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER'," "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH'," - "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT')") }, + "'EMPTY_STRING_IS_NULL','SIMULTANEOUS_ASSIGNMENT'," + "'TIME_ROUND_FRACTIONAL')") }, { NULL, 0 } }, { diff --git a/sql/sql_basic_types.h b/sql/sql_basic_types.h index 5062817f911..a790b68fc0c 100644 --- a/sql/sql_basic_types.h +++ b/sql/sql_basic_types.h @@ -26,13 +26,15 @@ typedef int64 query_id_t; /* "fuzzydate" with strict data type control. + Represents a mixture of *only* data type conversion flags, without rounding. Please keep "explicit" in constructors and conversion methods. */ -class date_mode_t +class date_conv_mode_t { public: enum value_t { + CONV_NONE= 0U, /* FUZZY_DATES is used for the result will only be used for comparison purposes. Conversion is as relaxed as possible. @@ -41,17 +43,33 @@ public: TIME_ONLY= 4U, INTERVAL_hhmmssff= 8U, INTERVAL_DAY= 16U, + RANGE0_LAST= INTERVAL_DAY, NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES }; + /* + BIT-OR for all known values. Let's have a separate enum for it. + - We don't put this value "value_t", to avoid handling it in switch(). + - We don't put this value as a static const inside the class, + because "gdb" would display it every time when we do "print" + for a time_round_mode_t value. + - We can't put into into a function returning this value, because + it's not allowed to use functions in static_assert. + */ + enum known_values_t + { + KNOWN_MODES= FUZZY_DATES | + TIME_ONLY | INTERVAL_hhmmssff | INTERVAL_DAY | + NO_ZERO_IN_DATE | NO_ZERO_DATE | INVALID_DATES + }; private: value_t m_mode; public: // Constructors - explicit date_mode_t(ulonglong fuzzydate) + explicit date_conv_mode_t(ulonglong fuzzydate) :m_mode((value_t) fuzzydate) { } @@ -66,9 +84,148 @@ public: } // Unary operators - date_mode_t operator~() const + ulonglong operator~() const { - return date_mode_t(~m_mode); + return ~m_mode; + } + + // Dyadic bitwise operators + date_conv_mode_t operator&(const date_conv_mode_t &other) const + { + return date_conv_mode_t(m_mode & other.m_mode); + } + date_conv_mode_t operator&(const ulonglong other) const + { + return date_conv_mode_t(m_mode & other); + } + + date_conv_mode_t operator|(const date_conv_mode_t &other) const + { + return date_conv_mode_t(m_mode | other.m_mode); + } + + // Dyadic bitwise assignment operators + date_conv_mode_t &operator&=(const date_conv_mode_t &other) + { + m_mode= value_t(m_mode & other.m_mode); + return *this; + } + + date_conv_mode_t &operator|=(const date_conv_mode_t &other) + { + m_mode= value_t(m_mode | other.m_mode); + return *this; + } +}; + + +/* + Fractional rounding mode for temporal data types. +*/ +class time_round_mode_t +{ +public: + enum value_t + { + /* + Use FRAC_NONE when the value needs no rounding nor truncation, + because it is already known not to haveany fractional digits outside + of the requested precision. + */ + FRAC_NONE= 0, + FRAC_TRUNCATE= date_conv_mode_t::RANGE0_LAST << 1, // 32 + FRAC_ROUND= date_conv_mode_t::RANGE0_LAST << 2 // 64 + }; + // BIT-OR for all known values. See comments in time_conv_mode_t. + enum known_values_t + { + KNOWN_MODES= FRAC_TRUNCATE | FRAC_ROUND + }; +private: + value_t m_mode; +public: + // Constructors + explicit time_round_mode_t(ulonglong mode) + :m_mode((value_t) mode) + { + DBUG_ASSERT(mode == FRAC_NONE || + mode == FRAC_TRUNCATE || + mode == FRAC_ROUND); + } + // Conversion operators + explicit operator ulonglong() const + { + return m_mode; + } + value_t mode() const + { + return m_mode; + } + // Comparison operators + bool operator==(const time_round_mode_t &other) + { + return m_mode == other.m_mode; + } +}; + + +/* + "fuzzydate" with strict data type control. + Used as a parameter to get_date() and represents a mixture of: + - data type conversion flags + - fractional second rounding flags + Please keep "explicit" in constructors and conversion methods. +*/ +class date_mode_t +{ +public: + enum value_t + { + CONV_NONE= date_conv_mode_t::CONV_NONE, // 0 + FUZZY_DATES= date_conv_mode_t::FUZZY_DATES, // 1 + TIME_ONLY= date_conv_mode_t::TIME_ONLY, // 4 + INTERVAL_hhmmssff= date_conv_mode_t::INTERVAL_hhmmssff, // 8 + INTERVAL_DAY= date_conv_mode_t::INTERVAL_DAY, // 16 + FRAC_TRUNCATE= time_round_mode_t::FRAC_TRUNCATE, // 32 + FRAC_ROUND= time_round_mode_t::FRAC_ROUND, // 64 + NO_ZERO_IN_DATE= date_conv_mode_t::NO_ZERO_IN_DATE, // (1UL << 23) + NO_ZERO_DATE= date_conv_mode_t::NO_ZERO_DATE, // (1UL << 24) + INVALID_DATES= date_conv_mode_t::INVALID_DATES, // (1UL << 25) + }; +protected: + value_t m_mode; +public: + + // Constructors + explicit date_mode_t(ulonglong fuzzydate) + :m_mode((value_t) fuzzydate) + { } + + // Conversion operators + explicit operator ulonglong() const + { + return m_mode; + } + explicit operator bool() const + { + return m_mode != 0; + } + explicit operator date_conv_mode_t() const + { + return date_conv_mode_t(ulonglong(m_mode) & date_conv_mode_t::KNOWN_MODES); + } + explicit operator time_round_mode_t() const + { + return time_round_mode_t(ulonglong(m_mode) & time_round_mode_t::KNOWN_MODES); + } + // Unary operators + ulonglong operator~() const + { + return ~m_mode; + } + bool operator!() const + { + return !m_mode; } // Dyadic bitwise operators @@ -76,6 +233,10 @@ public: { return date_mode_t(m_mode & other.m_mode); } + date_mode_t operator&(ulonglong other) const + { + return date_mode_t(m_mode & other); + } date_mode_t operator|(const date_mode_t &other) const { @@ -94,22 +255,81 @@ public: m_mode= value_t(m_mode | other.m_mode); return *this; } + + date_mode_t &operator|=(const date_conv_mode_t &other) + { + m_mode= value_t(m_mode | ulonglong(other)); + return *this; + } }; -const date_mode_t - TIME_FUZZY_DATES (date_mode_t::value_t::FUZZY_DATES), - TIME_TIME_ONLY (date_mode_t::value_t::TIME_ONLY), - TIME_INTERVAL_hhmmssff (date_mode_t::value_t::INTERVAL_hhmmssff), - TIME_INTERVAL_DAY (date_mode_t::value_t::INTERVAL_DAY), - TIME_NO_ZERO_IN_DATE (date_mode_t::value_t::NO_ZERO_IN_DATE), - TIME_NO_ZERO_DATE (date_mode_t::value_t::NO_ZERO_DATE), - TIME_INVALID_DATES (date_mode_t::value_t::INVALID_DATES); +// Bitwise OR out-of-class operators for data type mixtures +static inline date_mode_t operator|(const date_mode_t &a, + const date_conv_mode_t &b) +{ + return date_mode_t(ulonglong(a) | ulonglong(b)); +} + +static inline date_mode_t operator|(const date_conv_mode_t &a, + const time_round_mode_t &b) +{ + return date_mode_t(ulonglong(a) | ulonglong(b)); +} + + +static inline date_mode_t operator|(const date_conv_mode_t &a, + const date_mode_t &b) +{ + return date_mode_t(ulonglong(a) | ulonglong(b)); +} + + +// Bitwise AND out-of-class operators for data type mixtures +static inline date_conv_mode_t operator&(const date_mode_t &a, + const date_conv_mode_t &b) +{ + return date_conv_mode_t(ulonglong(a) & ulonglong(b)); +} + +static inline date_conv_mode_t operator&(const date_conv_mode_t &a, + const date_mode_t &b) +{ + return date_conv_mode_t(ulonglong(a) & ulonglong(b)); +} + +static inline date_conv_mode_t operator&(sql_mode_t &a, + const date_conv_mode_t &b) +{ + return date_conv_mode_t(a & ulonglong(b)); +} + + +static const date_conv_mode_t + TIME_CONV_NONE (date_conv_mode_t::CONV_NONE), + TIME_FUZZY_DATES (date_conv_mode_t::FUZZY_DATES), + TIME_TIME_ONLY (date_conv_mode_t::TIME_ONLY), + TIME_INTERVAL_hhmmssff (date_conv_mode_t::INTERVAL_hhmmssff), + TIME_INTERVAL_DAY (date_conv_mode_t::INTERVAL_DAY), + TIME_NO_ZERO_IN_DATE (date_conv_mode_t::NO_ZERO_IN_DATE), + TIME_NO_ZERO_DATE (date_conv_mode_t::NO_ZERO_DATE), + TIME_INVALID_DATES (date_conv_mode_t::INVALID_DATES); + +// An often used combination +static const date_conv_mode_t + TIME_NO_ZEROS (date_conv_mode_t::NO_ZERO_DATE| + date_conv_mode_t::NO_ZERO_IN_DATE); // Flags understood by str_to_xxx, number_to_xxx, check_date -static const date_mode_t +static const date_conv_mode_t TIME_MODE_FOR_XXX_TO_DATE (date_mode_t::NO_ZERO_IN_DATE | date_mode_t::NO_ZERO_DATE | date_mode_t::INVALID_DATES); +static const time_round_mode_t + TIME_FRAC_NONE (time_round_mode_t::FRAC_NONE), + TIME_FRAC_TRUNCATE (time_round_mode_t::FRAC_TRUNCATE), + TIME_FRAC_ROUND (time_round_mode_t::FRAC_ROUND); + + #endif diff --git a/sql/sql_class.h b/sql/sql_class.h index 8174a8b313b..55766af383d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -158,6 +158,7 @@ enum enum_binlog_row_image { #define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31) #define MODE_EMPTY_STRING_IS_NULL (1ULL << 32) #define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33) +#define MODE_TIME_ROUND_FRACTIONAL (1ULL << 34) /* Bits for different old style modes */ #define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0) @@ -3419,6 +3420,11 @@ public: { return Timeval(query_start(), query_start_sec_part()); } + time_round_mode_t temporal_round_mode() const + { + return variables.sql_mode & MODE_TIME_ROUND_FRACTIONAL ? + TIME_FRAC_ROUND : TIME_FRAC_TRUNCATE; + } private: struct { @@ -4957,13 +4963,17 @@ my_eof(THD *thd) (A)->variables.sql_log_bin_off= 0;} -inline date_mode_t sql_mode_for_dates(THD *thd) +inline date_conv_mode_t sql_mode_for_dates(THD *thd) { + static_assert((date_conv_mode_t::KNOWN_MODES & + time_round_mode_t::KNOWN_MODES) == 0, + "date_conv_mode_t and time_round_mode_t must use different " + "bit values"); static_assert(MODE_NO_ZERO_DATE == date_mode_t::NO_ZERO_DATE && MODE_NO_ZERO_IN_DATE == date_mode_t::NO_ZERO_IN_DATE && MODE_INVALID_DATES == date_mode_t::INVALID_DATES, "sql_mode_t and date_mode_t values must be equal"); - return date_mode_t(thd->variables.sql_mode & + return date_conv_mode_t(thd->variables.sql_mode & (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES)); } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 96e07b19dd8..6b530a95efb 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -8217,6 +8217,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, field->type() == MYSQL_TYPE_DATETIME)) { /* Monotonic, but return NULL for dates with zeros in month/day. */ + DBUG_ASSERT(field->cmp_type() == TIME_RESULT); // No rounding/truncation zero_in_start_date= field->get_date(&start_date, date_mode_t(0)); DBUG_PRINT("info", ("zero start %u %04d-%02d-%02d", zero_in_start_date, start_date.year, @@ -8241,6 +8242,7 @@ static int get_part_iter_for_interval_via_mapping(partition_info *part_info, !part_info->part_expr->null_value) { MYSQL_TIME end_date; + DBUG_ASSERT(field->cmp_type() == TIME_RESULT); // No rounding/truncation bool zero_in_end_date= field->get_date(&end_date, date_mode_t(0)); /* This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 35a4e1e47c3..3977df675b3 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -289,8 +289,8 @@ ulong convert_month_to_period(ulong month) bool -check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzydate, - timestamp_type ts_type) +check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, + date_conv_mode_t fuzzydate, timestamp_type ts_type) { int unused; if (check_date(ltime, fuzzydate, &unused)) @@ -372,34 +372,37 @@ public: /* Character set-aware version of ascii_to_datetime_or_date_or_time() */ -bool Temporal::str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *st, +bool Temporal::str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t fuzzydate) { TemporalAsciiBuffer tmp(str, length, cs); - return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate); + return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate)|| + add_nanoseconds(thd, &st->warnings, fuzzydate, st->nanoseconds); } /* Character set-aware version of str_to_datetime_or_date() */ -bool Temporal::str_to_datetime_or_date(MYSQL_TIME_STATUS *status, +bool Temporal::str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t flags) { TemporalAsciiBuffer tmp(str, length, cs); - return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags); + return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags) || + add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds); } /* Character set-aware version of ascii_to_temporal() */ -bool Temporal::str_to_temporal(MYSQL_TIME_STATUS *status, +bool Temporal::str_to_temporal(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t flags) { TemporalAsciiBuffer tmp(str, length, cs); - return ascii_to_temporal(status, tmp.str, tmp.length, flags); + return ascii_to_temporal(status, tmp.str, tmp.length, flags) || + add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds); } @@ -1307,7 +1310,7 @@ time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to, - date_mode_t fuzzydate) + date_conv_mode_t fuzzydate) { int warn= 0; DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); diff --git a/sql/sql_time.h b/sql/sql_time.h index 433374e2e9a..cfbff205667 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -57,7 +57,7 @@ bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt); bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt, - date_mode_t fuzzydate); + date_conv_mode_t fuzzydate); inline void datetime_to_date(MYSQL_TIME *ltime) { @@ -166,13 +166,20 @@ non_zero_date(const MYSQL_TIME *ltime) non_zero_hhmmssuu(ltime)); } static inline bool -check_date(const MYSQL_TIME *ltime, date_mode_t flags, int *was_cut) +check_date(const MYSQL_TIME *ltime, date_conv_mode_t flags, int *was_cut) { return check_date(ltime, non_zero_date(ltime), ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), was_cut); } -bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, date_mode_t fuzzy_date, - timestamp_type ts_type); +bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, + date_conv_mode_t fuzzy_date, timestamp_type ts_type); +static inline bool +check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, + date_mode_t fuzzydate, timestamp_type ts_type) +{ + return check_date_with_warn(thd, ltime, date_conv_mode_t(fuzzydate), ts_type); +} + bool adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec); longlong pack_time(const MYSQL_TIME *my_time); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 60c3747f2ea..cd993e0524e 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -156,12 +156,24 @@ VDec_op::VDec_op(Item_func_hybrid_field_type *item) } -date_mode_t Temporal::sql_mode_for_dates(THD *thd) +date_conv_mode_t Temporal::sql_mode_for_dates(THD *thd) { return ::sql_mode_for_dates(thd); } +time_round_mode_t Temporal::default_round_mode(THD *thd) +{ + return thd->temporal_round_mode(); +} + + +time_round_mode_t Timestamp::default_round_mode(THD *thd) +{ + return thd->temporal_round_mode(); +} + + my_decimal *Temporal::to_decimal(my_decimal *to) const { return date2my_decimal(this, to); @@ -183,8 +195,8 @@ void Temporal::make_from_str(THD *thd, Warn *warn, push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, ErrConvString(str, length,cs).ptr());); - if (str_to_temporal(warn, str, length, cs, fuzzydate)) - make_fuzzy_date(&warn->warnings, fuzzydate); + if (str_to_temporal(thd, warn, str, length, cs, fuzzydate)) + make_fuzzy_date(&warn->warnings, date_conv_mode_t(fuzzydate)); if (warn->warnings) warn->set_str(str, length, &my_charset_bin); } @@ -197,14 +209,14 @@ Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) } -void Sec6::make_from_decimal(const my_decimal *d) +void Sec6::make_from_decimal(const my_decimal *d, ulong *nanoseconds) { - m_neg= my_decimal2seconds(d, &m_sec, &m_usec); + m_neg= my_decimal2seconds(d, &m_sec, &m_usec, nanoseconds); m_truncated= (m_sec >= LONGLONG_MAX); } -void Sec6::make_from_double(double nr) +void Sec6::make_from_double(double nr, ulong *nanoseconds) { if ((m_neg= nr < 0)) nr= -nr; @@ -216,7 +228,9 @@ void Sec6::make_from_double(double nr) else { m_sec= (ulonglong) nr; - m_usec= (ulong) ((nr - floor(nr)) * 1000000); + m_usec= (ulong) ((nr - floor(nr)) * 1000000000); + *nanoseconds= m_usec % 1000; + m_usec/= 1000; } } @@ -235,8 +249,8 @@ bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime, bool rc= fuzzydate & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ? to_datetime_or_to_interval_hhmmssff(ltime, warn) : fuzzydate & TIME_TIME_ONLY ? - to_datetime_or_time(ltime, warn, fuzzydate) : - to_datetime_or_date(ltime, warn, fuzzydate); + to_datetime_or_time(ltime, warn, date_conv_mode_t(fuzzydate)) : + to_datetime_or_date(ltime, warn, date_conv_mode_t(fuzzydate)); DBUG_ASSERT(*warn || !rc); if (truncated()) *warn|= MYSQL_TIME_WARN_TRUNCATED; @@ -259,7 +273,7 @@ void Temporal::push_conversion_warnings(THD *thd, bool totally_useless_value, in } -VSec6::VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit) +VSec9::VSec9(THD *thd, Item *item, const char *type_str, ulonglong limit) { if (item->decimals == 0) { // optimize for an important special case @@ -277,7 +291,7 @@ VSec6::VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit) else if (item->cmp_type() == REAL_RESULT) { double nr= item->val_real(); - make_from_double(nr); + make_from_double(nr, &m_nsec); m_is_null= item->null_value; if (!m_is_null && m_sec > limit) { @@ -293,7 +307,7 @@ VSec6::VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit) else { VDec tmp(item); - (m_is_null= tmp.is_null()) ? reset() : make_from_decimal(tmp.ptr()); + (m_is_null= tmp.is_null()) ? reset() : make_from_decimal(tmp.ptr(), &m_nsec); if (!m_is_null && m_sec > limit) { m_sec= limit; @@ -352,7 +366,8 @@ const LEX_CSTRING Interval_DDhhmmssff::m_type_name= Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings, - Item *item, ulong max_hour) + Item *item, ulong max_hour, + time_round_mode_t mode, uint dec) { switch (item->cmp_type()) { case ROW_RESULT: @@ -361,7 +376,8 @@ Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st, break; case TIME_RESULT: { - if (item->get_date(thd, this, TIME_TIME_ONLY)) + // Rounding mode is not important here + if (item->get_date(thd, this, Options(TIME_TIME_ONLY, TIME_FRAC_NONE))) time_type= MYSQL_TIMESTAMP_NONE; else if (time_type != MYSQL_TIMESTAMP_TIME) { @@ -392,6 +408,8 @@ Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st, } else { + if (mode == TIME_FRAC_ROUND) + time_round_or_set_max(dec, &st->warnings, max_hour, st->nanoseconds); if (hour > max_hour) { st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; @@ -450,7 +468,8 @@ uint Interval_DDhhmmssff::fsp(THD *thd, Item *item) if (!item->const_item() || item->is_expensive()) return TIME_SECOND_PART_DIGITS; Status st; - Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32); + Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32, + TIME_FRAC_TRUNCATE, TIME_SECOND_PART_DIGITS); return it.is_valid_interval_DDhhmmssff() ? st.precision : TIME_SECOND_PART_DIGITS; } @@ -459,13 +478,101 @@ uint Interval_DDhhmmssff::fsp(THD *thd, Item *item) void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt) { *warn= 0; - if (item->get_date(thd, this, opt.get_date_flags())) + if (item->get_date(thd, this, opt)) time_type= MYSQL_TIMESTAMP_NONE; else valid_MYSQL_TIME_to_valid_value(thd, warn, opt); } +static uint msec_round_add[7]= +{ + 500000000, + 50000000, + 5000000, + 500000, + 50000, + 5000, + 0 +}; + + +Sec9 & Sec9::round(uint dec) +{ + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + if (Sec6::add_nanoseconds(m_nsec + msec_round_add[dec])) + m_sec++; + m_nsec= 0; + Sec6::trunc(dec); + return *this; +} + + +void Timestamp::round_or_set_max(uint dec, int *warn) +{ + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + if (add_nanoseconds_usec(msec_round_add[dec]) && + tv_sec++ >= TIMESTAMP_MAX_VALUE) + { + tv_sec= TIMESTAMP_MAX_VALUE; + tv_usec= TIME_MAX_SECOND_PART; + *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE; + } + my_timeval_trunc(this, dec); +} + + +bool Temporal::add_nanoseconds_with_round(THD *thd, int *warn, + date_conv_mode_t mode, + ulong nsec) +{ + switch (time_type) { + case MYSQL_TIMESTAMP_TIME: + { + ulong max_hour= (mode & (TIME_INTERVAL_DAY | TIME_INTERVAL_hhmmssff)) ? + TIME_MAX_INTERVAL_HOUR : TIME_MAX_HOUR; + time_round_or_set_max(6, warn, max_hour, nsec); + return false; + } + case MYSQL_TIMESTAMP_DATETIME: + return datetime_round_or_invalidate(thd, 6, warn, nsec); + case MYSQL_TIMESTAMP_DATE: + return false; + case MYSQL_TIMESTAMP_NONE: + return false; + case MYSQL_TIMESTAMP_ERROR: + break; + } + DBUG_ASSERT(0); + return false; +} + + +void Temporal::time_round_or_set_max(uint dec, int *warn, + ulong max_hour, ulong nsec) +{ + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + if (add_nanoseconds_mmssff(nsec) && ++hour > max_hour) + { + time_hhmmssff_set_max(max_hour); + *warn|= MYSQL_TIME_WARN_OUT_OF_RANGE; + } + my_time_trunc(this, dec); +} + + +void Time::round_or_set_max(uint dec, int *warn, ulong nsec) +{ + Temporal::time_round_or_set_max(dec, warn, TIME_MAX_HOUR, nsec); + DBUG_ASSERT(is_valid_time_slow()); +} + + +void Time::round_or_set_max(uint dec, int *warn) +{ + round_or_set_max(dec, warn, msec_round_add[dec]); +} + /** Create from a DATETIME by subtracting a given number of days, implementing an optimized version of calc_time_diff(). @@ -586,9 +693,10 @@ Time::Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec6 &second) } -void Temporal_with_date::make_from_item(THD *thd, Item *item, date_mode_t flags) +void Temporal_with_date::make_from_item(THD *thd, Item *item, + date_mode_t fuzzydate) { - flags&= ~TIME_TIME_ONLY; + date_conv_mode_t flags= date_conv_mode_t(fuzzydate) & ~TIME_TIME_ONLY; /* Some TIME type items return error when trying to do get_date() without TIME_TIME_ONLY set (e.g. Item_field for Field_time). @@ -596,10 +704,11 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, date_mode_t flags) In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY and leave it to get_date() to check date. */ - date_mode_t time_flag= (item->field_type() == MYSQL_TYPE_TIME && + date_conv_mode_t time_flag= (item->field_type() == MYSQL_TYPE_TIME && !(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? - TIME_TIME_ONLY : date_mode_t(0); - if (item->get_date(thd, this, flags | time_flag)) + TIME_TIME_ONLY : TIME_CONV_NONE; + Options opt(flags | time_flag, time_round_mode_t(fuzzydate)); + if (item->get_date(thd, this, opt)) time_type= MYSQL_TIMESTAMP_NONE; else if (time_type == MYSQL_TIMESTAMP_TIME) { @@ -612,22 +721,17 @@ void Temporal_with_date::make_from_item(THD *thd, Item *item, date_mode_t flags) } -void Temporal_with_date::make_from_item(THD *thd, Item *item) +void Temporal_with_date::check_date_or_invalidate(int *warn, + date_conv_mode_t flags) { - return make_from_item(thd, item, sql_mode_for_dates(thd)); -} - - -void Temporal_with_date::check_date_or_invalidate(int *warn, date_mode_t flags) -{ - if (check_date(this, pack_time(this) != 0, - ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warn)) + if (::check_date(this, pack_time(this) != 0, + ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warn)) time_type= MYSQL_TIMESTAMP_NONE; } void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, - date_mode_t flags) + date_conv_mode_t flags) { DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); if (time_to_datetime(thd, from, this)) @@ -641,7 +745,7 @@ void Datetime::make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, void Datetime::make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, - date_mode_t flags) + date_conv_mode_t flags) { DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_DATE || from->time_type == MYSQL_TIMESTAMP_DATETIME); @@ -667,7 +771,7 @@ Datetime::Datetime(THD *thd, const timeval &tv) Datetime::Datetime(THD *thd, int *warn, const MYSQL_TIME *from, - date_mode_t flags) + date_conv_mode_t flags) { DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); switch (from->time_type) { @@ -687,6 +791,82 @@ Datetime::Datetime(THD *thd, int *warn, const MYSQL_TIME *from, } +bool Temporal::datetime_add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec) +{ + if (!add_nanoseconds_mmssff(nsec)) + return false; + /* + Overflow happened on minutes. Now we need to add 1 hour to the value. + Catch a special case for the maximum possible date and hour==23, to + truncate '9999-12-31 23:59:59.9999999' (with 7 fractional digits) + to '9999-12-31 23:59:59.999999' (with 6 fractional digits), + with a warning, instead of returning an error, so this statement: + INSERT INTO (datetime_column) VALUES ('9999-12-31 23:59:59.9999999'); + inserts a value truncated to 6 fractional digits, instead of zero + date '0000-00-00 00:00:00.000000'. + */ + if (year == 9999 && month == 12 && day == 31 && hour == 23) + { + minute= 59; + second= 59; + second_part= 999999; + *warn= MYSQL_TIME_WARN_OUT_OF_RANGE; + return false; + } + INTERVAL interval; + memset(&interval, 0, sizeof(interval)); + interval.hour= 1; + /* date_add_interval cannot handle bad dates */ + if (check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE, warn) || + date_add_interval(thd, this, INTERVAL_HOUR, interval)) + { + make_from_out_of_range(warn); + return true; + } + return false; +} + + +bool Temporal::datetime_round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec) +{ + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + if (datetime_add_nanoseconds_or_invalidate(thd, warn, nsec)) + return true; + my_time_trunc(this, dec); + return false; + +} + + +bool Datetime::round_or_invalidate(THD *thd, uint dec, int *warn) +{ + return round_or_invalidate(thd, dec, warn, msec_round_add[dec]); +} + + +Datetime_from_temporal::Datetime_from_temporal(THD *thd, Item *temporal, + date_conv_mode_t fuzzydate) + :Datetime(thd, temporal, Options(fuzzydate, TIME_FRAC_NONE)) +{ + // Exact rounding mode does not matter + DBUG_ASSERT(temporal->cmp_type() == TIME_RESULT); +} + + +Datetime_truncation_not_needed::Datetime_truncation_not_needed(THD *thd, Item *item, + date_conv_mode_t mode) + :Datetime(thd, item, Options(mode, TIME_FRAC_NONE)) +{ + /* + The called Datetime() constructor only would truncate nanoseconds if they + existed (but we know there were no nanoseconds). Here we assert that there + are also no microsecond digits outside of the scale specified in "dec". + */ + DBUG_ASSERT(!is_valid_datetime() || fraction_remainder(item->decimals) == 0); +} + +/********************************************************************/ + uint Type_std_attributes::count_max_decimals(Item **item, uint nitems) { uint res= 0; @@ -3151,14 +3331,16 @@ void Type_handler_row::Item_update_null_value(Item *item) const void Type_handler_time_common::Item_update_null_value(Item *item) const { MYSQL_TIME ltime; - (void) item->get_date(current_thd, <ime, TIME_TIME_ONLY); + THD *thd= current_thd; + (void) item->get_date(thd, <ime, Time::Options(TIME_TIME_ONLY, thd)); } void Type_handler_temporal_with_date::Item_update_null_value(Item *item) const { MYSQL_TIME ltime; - (void) item->get_date(current_thd, <ime, sql_mode_for_dates(current_thd)); + THD *thd= current_thd; + (void) item->get_date(thd, <ime, Datetime::Options(thd)); } @@ -5013,7 +5195,7 @@ bool Type_handler_temporal_result:: */ return func->get_date_native(thd, ltime, fuzzydate & TIME_TIME_ONLY ? - sql_mode_for_dates(thd) : + Datetime::Options(thd) : fuzzydate); } @@ -5847,14 +6029,15 @@ uint Type_handler_string_result::Item_temporal_precision(THD *thd, Item *item, String *tmp; MYSQL_TIME_STATUS status; DBUG_ASSERT(item->is_fixed()); + // Nanosecond rounding is not needed here, for performance purposes if ((tmp= item->val_str(&buf)) && (is_time ? Time(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(), - Time::Options(TIME_TIME_ONLY, + Time::Options(TIME_TIME_ONLY, TIME_FRAC_TRUNCATE, Time::DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)). is_valid_time() : - Datetime(&status, tmp->ptr(), tmp->length(), tmp->charset(), - TIME_FUZZY_DATES). + Datetime(thd, &status, tmp->ptr(), tmp->length(), tmp->charset(), + Datetime::Options(TIME_FUZZY_DATES, TIME_FRAC_TRUNCATE)). is_valid_datetime())) return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); @@ -6104,7 +6287,8 @@ bool Type_handler_temporal_with_date:: Item_save_in_value(THD *thd, Item *item, st_value *value) const { value->m_type= DYN_COL_DATETIME; - item->get_date(thd, &value->value.m_time, sql_mode_for_dates(thd)); + item->get_date(thd, &value->value.m_time, + Datetime::Options(thd, TIME_FRAC_NONE)); return check_null(item, value); } @@ -6298,7 +6482,7 @@ bool Type_handler:: Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const { item->get_date(protocol->thd, &buf->value.m_time, - sql_mode_for_dates(protocol->thd)); + Datetime::Options(protocol->thd)); if (!item->null_value) return protocol->store(&buf->value.m_time, item->decimals); return protocol->store_null(); @@ -6309,7 +6493,7 @@ bool Type_handler:: Item_send_date(Item *item, Protocol *protocol, st_value *buf) const { item->get_date(protocol->thd, &buf->value.m_time, - sql_mode_for_dates(protocol->thd)); + Date::Options(protocol->thd)); if (!item->null_value) return protocol->store_date(&buf->value.m_time); return protocol->store_null(); @@ -7502,8 +7686,8 @@ int Type_handler_temporal_with_date::stored_field_cmp_to_item(THD *thd, Item *item) const { MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time; - field->get_date(&field_time, TIME_INVALID_DATES); - item->get_date(thd, &item_time, TIME_INVALID_DATES); + field->get_date(&field_time, Datetime::Options(TIME_INVALID_DATES, thd)); + item->get_date(thd, &item_time, Datetime::Options(TIME_INVALID_DATES, thd)); if (item_time.time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(thd, &item_time, item_time_cmp= &item_time2)) return 1; @@ -7516,8 +7700,8 @@ int Type_handler_time_common::stored_field_cmp_to_item(THD *thd, Item *item) const { MYSQL_TIME field_time, item_time; - field->get_time(&field_time); - item->get_time(thd, &item_time); + field->get_date(&field_time, Time::Options(thd)); + item->get_date(thd, &item_time, Time::Options(thd)); return my_time_compare(&field_time, &item_time); } @@ -7612,7 +7796,7 @@ Type_handler_date_common::create_literal_item(THD *thd, { Temporal::Warn st; Item_literal *item= NULL; - Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd)); + Temporal_hybrid tmp(thd, &st, str, length, cs, Temporal_hybrid::Options(thd)); if (tmp.is_valid_temporal() && tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE && !have_important_literal_warnings(&st)) @@ -7632,7 +7816,7 @@ Type_handler_temporal_with_date::create_literal_item(THD *thd, { Temporal::Warn st; Item_literal *item= NULL; - Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd)); + Temporal_hybrid tmp(thd, &st, str, length, cs, Temporal_hybrid::Options(thd)); if (tmp.is_valid_temporal() && tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME && !have_important_literal_warnings(&st)) @@ -7653,7 +7837,7 @@ Type_handler_time_common::create_literal_item(THD *thd, { MYSQL_TIME_STATUS st; Item_literal *item= NULL; - Time::Options opt(TIME_TIME_ONLY, Time::DATETIME_TO_TIME_DISALLOW); + Time::Options opt(TIME_TIME_ONLY, thd, Time::DATETIME_TO_TIME_DISALLOW); Time tmp(thd, &st, str, length, cs, opt); if (tmp.is_valid_time() && !have_important_literal_warnings(&st)) diff --git a/sql/sql_type.h b/sql/sql_type.h index 273542c201c..a5d8f4ae6df 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -216,8 +216,8 @@ protected: ulong m_usec; // The fractional part, between 0 and 999999 bool m_neg; // false if positive, true of negative bool m_truncated; // Indicates if the constructor truncated the value - void make_from_decimal(const my_decimal *d); - void make_from_double(double d); + void make_from_decimal(const my_decimal *d, ulong *nanoseconds); + void make_from_double(double d, ulong *nanoseconds); void make_from_int(const Longlong_hybrid &nr) { m_neg= nr.neg(); @@ -230,14 +230,27 @@ protected: m_sec= m_usec= m_neg= m_truncated= 0; } Sec6() { } + bool add_nanoseconds(uint nanoseconds) + { + DBUG_ASSERT(nanoseconds <= 1000000000); + if (nanoseconds < 500) + return false; + m_usec+= (nanoseconds + 500) / 1000; + if (m_usec < 1000000) + return false; + m_usec%= 1000000; + return true; + } public: explicit Sec6(double nr) { - make_from_double(nr); + ulong nanoseconds; + make_from_double(nr, &nanoseconds); } explicit Sec6(const my_decimal *d) { - make_from_decimal(d); + ulong nanoseconds; + make_from_decimal(d, &nanoseconds); } explicit Sec6(const Longlong_hybrid &nr) { @@ -303,7 +316,8 @@ protected: } public: // [-][DD]hhhmmss.ff, YYMMDDhhmmss.ff, YYYYMMDDhhmmss.ff - bool to_datetime_or_time(MYSQL_TIME *to, int *warn, date_mode_t mode) const + bool to_datetime_or_time(MYSQL_TIME *to, int *warn, + date_conv_mode_t mode) const { bool rc= m_sec > 9999999 && m_sec <= 99991231235959ULL && !m_neg ? ::number_to_datetime_or_date(m_sec, m_usec, to, @@ -316,7 +330,8 @@ public: Convert a number in formats YYYYMMDDhhmmss.ff or YYMMDDhhmmss.ff to TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff' */ - bool to_datetime_or_date(MYSQL_TIME *to, int *warn, date_mode_t flags) const + bool to_datetime_or_date(MYSQL_TIME *to, int *warn, + date_conv_mode_t flags) const { if (m_neg) { @@ -349,6 +364,11 @@ public: ltime->second_part= m_usec; return false; } + Sec6 &trunc(uint dec) + { + m_usec-= my_time_fraction_remainder(m_usec, dec); + return *this; + } size_t to_string(char *to, size_t nbytes) const { return m_usec ? @@ -360,11 +380,45 @@ public: }; -class VSec6: public Sec6 +class Sec9: public Sec6 +{ +protected: + ulong m_nsec; // Nanoseconds 0..999 + void make_from_int(const Longlong_hybrid &nr) + { + Sec6::make_from_int(nr); + m_nsec= 0; + } + Sec9() { } +public: + Sec9(const my_decimal *d) + { + Sec6::make_from_decimal(d, &m_nsec); + } + Sec9(double d) + { + Sec6::make_from_double(d, &m_nsec); + } + ulong nsec() const { return m_nsec; } + Sec9 &trunc(uint dec) + { + m_nsec= 0; + Sec6::trunc(dec); + return *this; + } + Sec9 &round(uint dec); + Sec9 &round(uint dec, time_round_mode_t mode) + { + return mode == TIME_FRAC_TRUNCATE ? trunc(dec) : round(dec); + } +}; + + +class VSec9: public Sec9 { bool m_is_null; public: - VSec6(THD *thd, Item *item, const char *type_str, ulonglong limit); + VSec9(THD *thd, Item *item, const char *type_str, ulonglong limit); bool is_null() const { return m_is_null; } }; @@ -524,7 +578,24 @@ public: }; public: - static date_mode_t sql_mode_for_dates(THD *thd); + static date_conv_mode_t sql_mode_for_dates(THD *thd); + static time_round_mode_t default_round_mode(THD *thd); + class Options: public date_mode_t + { + public: + explicit Options(date_mode_t flags) + :date_mode_t(flags) + { } + Options(date_conv_mode_t flags, time_round_mode_t round_mode) + :date_mode_t(flags | round_mode) + { + DBUG_ASSERT(ulonglong(flags) <= UINT_MAX32); + } + Options(date_conv_mode_t flags, THD *thd) + :Options(flags, default_round_mode(thd)) + { } + }; + bool is_valid_temporal() const { DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); @@ -550,7 +621,7 @@ public: TIME/DATE/DATETIME failed. We return a zero date if allowed, otherwise - null. */ - void make_fuzzy_date(int *warn, date_mode_t fuzzydate) + void make_fuzzy_date(int *warn, date_conv_mode_t fuzzydate) { /* In the following scenario: @@ -590,14 +661,21 @@ protected: const Sec6 &nr, date_mode_t mode) { if (nr.convert_to_mysql_time(thd, &st->warnings, this, mode)) - make_fuzzy_date(&st->warnings, mode); + make_fuzzy_date(&st->warnings, date_conv_mode_t(mode)); + } + void make_from_sec9(THD *thd, MYSQL_TIME_STATUS *st, + const Sec9 &nr, date_mode_t mode) + { + if (nr.convert_to_mysql_time(thd, &st->warnings, this, mode) || + add_nanoseconds(thd, &st->warnings, mode, nr.nsec())) + make_fuzzy_date(&st->warnings, date_conv_mode_t(mode)); } void make_from_str(THD *thd, Warn *warn, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t fuzzydate); void make_from_double(THD *thd, Warn *warn, double nr, date_mode_t mode) { - make_from_sec6(thd, warn, Sec6(nr), mode); + make_from_sec9(thd, warn, Sec9(nr), mode); if (warn->warnings) warn->set_double(nr); } @@ -615,7 +693,7 @@ protected: void make_from_decimal(THD *thd, Warn *warn, const my_decimal *nr, date_mode_t mode) { - make_from_sec6(thd, warn, Sec6(nr), mode); + make_from_sec9(thd, warn, Sec9(nr), mode); if (warn->warnings) warn->set_decimal(nr); } @@ -670,15 +748,15 @@ protected: return rc; } // Character set aware versions for string conversion routines - bool str_to_temporal(MYSQL_TIME_STATUS *st, + bool str_to_temporal(THD *thd, MYSQL_TIME_STATUS *st, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t fuzzydate); - bool str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *st, + bool str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st, const char *str, size_t length, - CHARSET_INFO *cs, date_mode_t fuzzydate); - bool str_to_datetime_or_date(MYSQL_TIME_STATUS *st, + CHARSET_INFO *cs, date_mode_t mode); + bool str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *st, const char *str, size_t length, - CHARSET_INFO *cs, date_mode_t fuzzydate); + CHARSET_INFO *cs, date_mode_t mode); bool has_valid_mmssff() const { @@ -694,6 +772,67 @@ protected: { return year == 0 && month == 0 && day == 0; } + bool check_date(date_conv_mode_t flags, int *warn) const + { + return ::check_date(this, flags, warn); + } + void time_hhmmssff_set_max(ulong max_hour) + { + hour= max_hour; + minute= TIME_MAX_MINUTE; + second= TIME_MAX_SECOND; + second_part= TIME_MAX_SECOND_PART; + } + /* + Add nanoseconds to ssff + retval true if seconds overflowed (the caller should increment minutes) + false if no overflow happened + */ + bool add_nanoseconds_ssff(uint nanoseconds) + { + DBUG_ASSERT(nanoseconds <= 1000000000); + if (nanoseconds < 500) + return false; + second_part+= (nanoseconds + 500) / 1000; + if (second_part < 1000000) + return false; + second_part%= 1000000; + if (second < 59) + { + second++; + return false; + } + second= 0; + return true; + } + /* + Add nanoseconds to mmssff + retval true if hours overflowed (the caller should increment hours) + false if no overflow happened + */ + bool add_nanoseconds_mmssff(uint nanoseconds) + { + if (!add_nanoseconds_ssff(nanoseconds)) + return false; + if (minute < 59) + { + minute++; + return false; + } + minute= 0; + return true; + } + void time_round_or_set_max(uint dec, int *warn, ulong max_hour, ulong nsec); + bool datetime_add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec); + bool datetime_round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec); + bool add_nanoseconds_with_round(THD *thd, int *warn, + date_conv_mode_t mode, ulong nsec); + bool add_nanoseconds(THD *thd, int *warn, date_mode_t mode, ulong nsec) + { + date_conv_mode_t cmode= date_conv_mode_t(mode); + return time_round_mode_t(mode) == TIME_FRAC_ROUND ? + add_nanoseconds_with_round(thd, warn, cmode, nsec) : false; + } public: static void *operator new(size_t size, MYSQL_TIME *ltime) throw() { @@ -717,10 +856,28 @@ public: class Temporal_hybrid: public Temporal { public: + class Options: public Temporal::Options + { + public: + Options(THD *thd) + :Temporal::Options(sql_mode_for_dates(thd), default_round_mode(thd)) + { } + Options(date_conv_mode_t flags, time_round_mode_t round_mode) + :Temporal::Options(flags, round_mode) + { } + explicit Options(const Temporal::Options &opt) + :Temporal::Options(opt) + { } + explicit Options(date_mode_t fuzzydate) + :Temporal::Options(fuzzydate) + { } + }; + +public: // Contructors for Item Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate); Temporal_hybrid(THD *thd, Item *item) - :Temporal_hybrid(thd, item, sql_mode_for_dates(thd)) + :Temporal_hybrid(thd, item, Options(thd)) { } Temporal_hybrid(Item *item) :Temporal_hybrid(current_thd, item) @@ -964,14 +1121,20 @@ public: DBUG_ASSERT(fsp <= TIME_SECOND_PART_DIGITS); return max_int_part_char_length() + (fsp ? 1 : 0) + fsp; } + public: Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings, - Item *item, ulong max_hour); - Interval_DDhhmmssff(THD *thd, Item *item) + Item *item, ulong max_hour, + time_round_mode_t mode, uint dec); + Interval_DDhhmmssff(THD *thd, Item *item, uint dec) { Status st; - new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour()); + new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour(), + default_round_mode(thd), dec); } + Interval_DDhhmmssff(THD *thd, Item *item) + :Interval_DDhhmmssff(thd, item, TIME_SECOND_PART_DIGITS) + { } const MYSQL_TIME *get_mysql_time() const { DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow()); @@ -1028,27 +1191,35 @@ public: DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY, DATETIME_TO_TIME_MINUS_CURRENT_DATE }; - class Options + class Options: public Temporal::Options { - date_mode_t m_get_date_flags; datetime_to_time_mode_t m_datetime_to_time_mode; public: - Options() - :m_get_date_flags(flags_for_get_date()), - m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) + Options(THD *thd) + :Temporal::Options(default_flags_for_get_date(), default_round_mode(thd)), + m_datetime_to_time_mode(default_datetime_to_time_mode()) { } - Options(date_mode_t flags) - :m_get_date_flags(flags), - m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) + Options(date_conv_mode_t flags, THD *thd) + :Temporal::Options(flags, default_round_mode(thd)), + m_datetime_to_time_mode(default_datetime_to_time_mode()) { } - Options(date_mode_t flags, datetime_to_time_mode_t dtmode) - :m_get_date_flags(flags), + Options(date_conv_mode_t flags, THD *thd, datetime_to_time_mode_t dtmode) + :Temporal::Options(flags, default_round_mode(thd)), m_datetime_to_time_mode(dtmode) { } - date_mode_t get_date_flags() const - { return m_get_date_flags; } + Options(date_conv_mode_t fuzzydate, time_round_mode_t round_mode, + datetime_to_time_mode_t datetime_to_time_mode) + :Temporal::Options(fuzzydate, round_mode), + m_datetime_to_time_mode(datetime_to_time_mode) + { } + datetime_to_time_mode_t datetime_to_time_mode() const { return m_datetime_to_time_mode; } + + static datetime_to_time_mode_t default_datetime_to_time_mode() + { + return DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS; + } }; /* CAST(AS TIME) historically does not mix days to hours. @@ -1058,14 +1229,28 @@ public: class Options_for_cast: public Options { public: - Options_for_cast() - :Options(flags_for_get_date(), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE) + Options_for_cast(THD *thd) + :Options(default_flags_for_get_date(), default_round_mode(thd), + DATETIME_TO_TIME_YYYYMMDD_TRUNCATE) { } - Options_for_cast(date_mode_t mode) - :Options(flags_for_get_date() | (mode & TIME_FUZZY_DATES), + Options_for_cast(date_mode_t mode, THD *thd) + :Options(default_flags_for_get_date() | (mode & TIME_FUZZY_DATES), + default_round_mode(thd), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE) { } }; + + class Options_cmp: public Options + { + public: + Options_cmp(THD *thd) + :Options(comparison_flags_for_get_date(), thd) + { } + Options_cmp(THD *thd, datetime_to_time_mode_t dtmode) + :Options(comparison_flags_for_get_date(), + default_round_mode(thd), dtmode) + { } + }; private: bool is_valid_value_slow() const { @@ -1199,6 +1384,15 @@ private: time_type= MYSQL_TIMESTAMP_NONE; DBUG_ASSERT(is_valid_value_slow()); } +public: + void round_or_set_max(uint dec, int *warn, ulong nsec); +private: + void round_or_set_max(uint dec, int *warn); + + /* + All make_from_xxx() methods initialize *warn. + The old value gets lost. + */ void make_from_datetime_move_day_to_hour(int *warn, const MYSQL_TIME *from); void make_from_datetime_with_days_diff(int *warn, const MYSQL_TIME *from, long curdays); @@ -1213,7 +1407,7 @@ public: Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec6 &second); Time() { time_type= MYSQL_TIMESTAMP_NONE; } Time(Item *item) - :Time(current_thd, item, Options()) + :Time(current_thd, item) { } Time(THD *thd, Item *item, const Options opt) { @@ -1221,18 +1415,18 @@ public: make_from_item(thd, &warn, item, opt); } Time(THD *thd, Item *item) - :Time(thd, item, Options()) + :Time(thd, item, Options(thd)) { } Time(int *warn, const MYSQL_TIME *from, long curdays); Time(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t len, CHARSET_INFO *cs, const Options opt) { - if (str_to_datetime_or_date_or_time(status, str, len, cs, - opt.get_date_flags())) + if (str_to_datetime_or_date_or_time(thd, status, str, len, cs, opt)) time_type= MYSQL_TIMESTAMP_NONE; // The below call will optionally add notes to already collected warnings: - xxx_to_time_result_to_valid_value(thd, &status->warnings, opt); + else + xxx_to_time_result_to_valid_value(thd, &status->warnings, opt); } protected: @@ -1242,34 +1436,50 @@ protected: time_type= MYSQL_TIMESTAMP_NONE; xxx_to_time_result_to_valid_value(thd, warn, opt); } + Time(THD *thd, int *warn, const Sec9 &nr, const Options &opt) + :Time(thd, warn, static_cast<Sec6>(nr), opt) + { + if (is_valid_time() && time_round_mode_t(opt) == TIME_FRAC_ROUND) + round_or_set_max(6, warn, nr.nsec()); + } public: Time(THD *thd, int *warn, const Longlong_hybrid &nr, const Options &opt) :Time(thd, warn, Sec6(nr), opt) { } Time(THD *thd, int *warn, double nr, const Options &opt) - :Time(thd, warn, Sec6(nr), opt) + :Time(thd, warn, Sec9(nr), opt) { } Time(THD *thd, int *warn, const my_decimal *d, const Options &opt) - :Time(thd, warn, Sec6(d), opt) + :Time(thd, warn, Sec9(d), opt) { } Time(THD *thd, Item *item, const Options opt, uint dec) :Time(thd, item, opt) { - trunc(dec); + round(dec, time_round_mode_t(opt)); } - Time(int *warn, const MYSQL_TIME *from, long curdays, uint dec) + Time(int *warn, const MYSQL_TIME *from, long curdays, + const Time::Options &opt, uint dec) :Time(warn, from, curdays) { - trunc(dec); + round(dec, time_round_mode_t(opt), warn); + } + Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec9 &second, + time_round_mode_t mode, uint dec) + :Time(warn, neg, hour, minute, second) + { + DBUG_ASSERT(is_valid_time()); + if ((ulonglong) mode == (ulonglong) TIME_FRAC_ROUND) + round_or_set_max(6, warn, second.nsec()); + round(dec, mode, warn); } Time(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t len, CHARSET_INFO *cs, const Options &opt, uint dec) :Time(thd, status, str, len, cs, opt) { - trunc(dec); + round(dec, time_round_mode_t(opt), &status->warnings); } Time(THD *thd, int *warn, const Longlong_hybrid &nr, const Options &opt, uint dec) @@ -1278,23 +1488,24 @@ public: /* Decimal digit truncation is needed here in case if nr was out of the supported TIME range, so "this" was set to '838:59:59.999999'. + We always do truncation (not rounding) here, independently from "opt". */ trunc(dec); } Time(THD *thd, int *warn, double nr, const Options &opt, uint dec) :Time(thd, warn, nr, opt) { - trunc(dec); + round(dec, time_round_mode_t(opt), warn); } Time(THD *thd, int *warn, const my_decimal *d, const Options &opt, uint dec) :Time(thd, warn, d, opt) { - trunc(dec); + round(dec, time_round_mode_t(opt), warn); } - static date_mode_t flags_for_get_date() + static date_conv_mode_t default_flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES; } - static date_mode_t comparison_flags_for_get_date() + static date_conv_mode_t comparison_flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES | TIME_FUZZY_DATES; } bool is_valid_time() const { @@ -1367,6 +1578,12 @@ public: { return is_valid_time() ? Temporal::to_packed() : 0; } + long fraction_remainder(uint dec) const + { + DBUG_ASSERT(is_valid_time()); + return Temporal::fraction_remainder(dec); + } + Time &trunc(uint dec) { if (is_valid_time()) @@ -1374,6 +1591,32 @@ public: DBUG_ASSERT(is_valid_value_slow()); return *this; } + Time &round(uint dec, int *warn) + { + if (is_valid_time()) + round_or_set_max(dec, warn); + DBUG_ASSERT(is_valid_value_slow()); + return *this; + } + Time &round(uint dec, time_round_mode_t mode, int *warn) + { + switch (mode.mode()) { + case time_round_mode_t::FRAC_NONE: + DBUG_ASSERT(fraction_remainder(dec) == 0); + return trunc(dec); + case time_round_mode_t::FRAC_TRUNCATE: + return trunc(dec); + case time_round_mode_t::FRAC_ROUND: + return round(dec, warn); + } + return *this; + } + Time &round(uint dec, time_round_mode_t mode) + { + int warn= 0; + return round(dec, mode, &warn); + } + }; @@ -1402,10 +1645,23 @@ public: class Temporal_with_date: public Temporal { +public: + class Options: public Temporal::Options + { + public: + Options(date_conv_mode_t fuzzydate, time_round_mode_t mode): + Temporal::Options(fuzzydate, mode) + {} + explicit Options(const Temporal::Options &opt) + :Temporal::Options(opt) + { } + explicit Options(date_mode_t mode) + :Temporal::Options(mode) + { } + }; protected: - void check_date_or_invalidate(int *warn, date_mode_t flags); + void check_date_or_invalidate(int *warn, date_conv_mode_t flags); void make_from_item(THD *thd, Item *item, date_mode_t flags); - void make_from_item(THD *thd, Item *item); ulong daynr() const { @@ -1430,39 +1686,35 @@ protected: uint week= calc_week(this, week_behaviour, &year); return week + year * 100; } - +public: Temporal_with_date() { time_type= MYSQL_TIMESTAMP_NONE; } - Temporal_with_date(THD *thd, Item *item, date_mode_t flags) - { - make_from_item(thd, item, flags); - } - Temporal_with_date(THD *thd, Item *item) + Temporal_with_date(THD *thd, Item *item, date_mode_t fuzzydate) { - make_from_item(thd, item); + make_from_item(thd, item, fuzzydate); } Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags) { DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); - if (nr.to_datetime_or_date(this, warn, flags)) + if (nr.to_datetime_or_date(this, warn, date_conv_mode_t(flags))) time_type= MYSQL_TIMESTAMP_NONE; } - Temporal_with_date(MYSQL_TIME_STATUS *status, + Temporal_with_date(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t len, CHARSET_INFO *cs, date_mode_t flags) { DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false); - if (str_to_datetime_or_date(status, str, len, cs, flags)) + if (str_to_datetime_or_date(thd, status, str, len, cs, flags)) time_type= MYSQL_TIMESTAMP_NONE; } public: - bool check_date_with_warn(THD *thd, date_mode_t flags) + bool check_date_with_warn(THD *thd, date_conv_mode_t flags) { return ::check_date_with_warn(thd, this, flags, MYSQL_TIMESTAMP_ERROR); } - static date_mode_t comparison_flags_for_get_date() + static date_conv_mode_t comparison_flags_for_get_date() { return TIME_INVALID_DATES | TIME_FUZZY_DATES; } }; @@ -1488,22 +1740,41 @@ class Date: public Temporal_with_date return !check_datetime_range(this); } public: - Date(THD *thd, Item *item, date_mode_t flags) - :Temporal_with_date(thd, item, flags) + class Options: public Temporal_with_date::Options { - if (time_type == MYSQL_TIMESTAMP_DATETIME) - datetime_to_date(this); - DBUG_ASSERT(is_valid_value_slow()); - } - Date(THD *thd, Item *item) - :Temporal_with_date(thd, item) + public: + explicit Options(date_conv_mode_t fuzzydate) + :Temporal_with_date::Options(fuzzydate, TIME_FRAC_TRUNCATE) + { } + Options(THD *thd, time_round_mode_t mode) + :Temporal_with_date::Options(sql_mode_for_dates(thd), mode) + { } + explicit Options(THD *thd) + :Temporal_with_date::Options(sql_mode_for_dates(thd), TIME_FRAC_TRUNCATE) + { } + explicit Options(date_mode_t fuzzydate) + :Temporal_with_date::Options(fuzzydate) + { } + }; +public: + Date(Item *item, date_mode_t fuzzydate) + :Date(current_thd, item, fuzzydate) + { } + Date(THD *thd, Item *item, date_mode_t fuzzydate) + :Temporal_with_date(thd, item, fuzzydate) { if (time_type == MYSQL_TIMESTAMP_DATETIME) datetime_to_date(this); DBUG_ASSERT(is_valid_value_slow()); } + Date(THD *thd, Item *item, date_conv_mode_t fuzzydate) + :Date(thd, item, Options(fuzzydate)) + { } + Date(THD *thd, Item *item) + :Temporal_with_date(Date(thd, item, Options(thd, TIME_FRAC_TRUNCATE))) + { } Date(Item *item) - :Date(current_thd, item) + :Temporal_with_date(Date(current_thd, item)) { } Date(const Temporal_with_date *d) :Temporal_with_date(*d) @@ -1603,98 +1874,141 @@ class Datetime: public Temporal_with_date DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME); return !check_datetime_range(this); } + bool add_nanoseconds_or_invalidate(THD *thd, int *warn, ulong nsec) + { + DBUG_ASSERT(is_valid_datetime_slow()); + bool rc= Temporal::datetime_add_nanoseconds_or_invalidate(thd, warn, nsec); + DBUG_ASSERT(is_valid_value_slow()); + return rc; + } void date_to_datetime_if_needed() { if (time_type == MYSQL_TIMESTAMP_DATE) date_to_datetime(this); } void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, - date_mode_t flags); + date_conv_mode_t flags); void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, - date_mode_t flags); -public: - Datetime(THD *thd, Item *item, date_mode_t flags) - :Temporal_with_date(thd, item, flags) + date_conv_mode_t flags); + bool round_or_invalidate(THD *thd, uint dec, int *warn); + bool round_or_invalidate(THD *thd, uint dec, int *warn, ulong nsec) { - date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_datetime_slow()); + bool rc= Temporal::datetime_round_or_invalidate(thd, dec, warn, nsec); DBUG_ASSERT(is_valid_value_slow()); + return rc; } - Datetime(THD *thd, Item *item) - :Temporal_with_date(thd, item) +public: + + class Options: public Temporal_with_date::Options + { + public: + Options(date_conv_mode_t fuzzydate, time_round_mode_t nanosecond_rounding) + :Temporal_with_date::Options(fuzzydate, nanosecond_rounding) + { } + Options(THD *thd) + :Temporal_with_date::Options(sql_mode_for_dates(thd), default_round_mode(thd)) + { } + Options(THD *thd, time_round_mode_t rounding_mode) + :Temporal_with_date::Options(sql_mode_for_dates(thd), rounding_mode) + { } + Options(date_conv_mode_t fuzzydate, THD *thd) + :Temporal_with_date::Options(fuzzydate, default_round_mode(thd)) + { } + }; + + class Options_cmp: public Options + { + public: + Options_cmp(THD *thd) + :Options(comparison_flags_for_get_date(), thd) + { } + }; + +public: + Datetime(THD *thd, Item *item, date_mode_t fuzzydate) + :Temporal_with_date(thd, item, fuzzydate) { date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } + Datetime(THD *thd, Item *item) + :Temporal_with_date(Datetime(thd, item, Options(thd))) + { } Datetime(Item *item) :Datetime(current_thd, item) { } - Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_mode_t flags); + + Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_conv_mode_t flags); Datetime() { set_zero_time(this, MYSQL_TIMESTAMP_DATETIME); } - Datetime(MYSQL_TIME_STATUS *status, + Datetime(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t len, CHARSET_INFO *cs, - date_mode_t flags) - :Temporal_with_date(status, str, len, cs, flags) + const date_mode_t fuzzydate) + :Temporal_with_date(thd, status, str, len, cs, fuzzydate) { date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } protected: - Datetime(int *warn, const Sec6 &nr, date_mode_t flags) + Datetime(THD *thd, int *warn, const Sec6 &nr, date_mode_t flags) :Temporal_with_date(warn, nr, flags) { date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } + Datetime(THD *thd, int *warn, const Sec9 &nr, date_mode_t fuzzydate) + :Datetime(thd, warn, static_cast<const Sec6>(nr), fuzzydate) + { + if (is_valid_datetime() && + time_round_mode_t(fuzzydate) == TIME_FRAC_ROUND) + round_or_invalidate(thd, 6, warn, nr.nsec()); + DBUG_ASSERT(is_valid_value_slow()); + } public: - Datetime(int *warn, const Longlong_hybrid &nr, date_mode_t mode) - :Datetime(warn, Sec6(nr), mode) + Datetime(THD *thd, int *warn, const Longlong_hybrid &nr, date_mode_t mode) + :Datetime(thd, warn, Sec6(nr), mode) { } - Datetime(int *warn, double nr, date_mode_t fuzzydate) - :Datetime(warn, Sec6(nr), fuzzydate) + Datetime(THD *thd, int *warn, double nr, date_mode_t fuzzydate) + :Datetime(thd, warn, Sec9(nr), fuzzydate) { } - Datetime(int *warn, const my_decimal *d, date_mode_t fuzzydate) - :Datetime(warn, Sec6(d), fuzzydate) + Datetime(THD *thd, int *warn, const my_decimal *d, date_mode_t fuzzydate) + :Datetime(thd, warn, Sec9(d), fuzzydate) { } Datetime(THD *thd, const timeval &tv); - Datetime(THD *thd, Item *item, date_mode_t flags, uint dec) - :Datetime(thd, item, flags) + Datetime(THD *thd, Item *item, date_mode_t fuzzydate, uint dec) + :Datetime(thd, item, fuzzydate) { - trunc(dec); + int warn= 0; + round(thd, dec, time_round_mode_t(fuzzydate), &warn); } - Datetime(MYSQL_TIME_STATUS *status, + Datetime(THD *thd, MYSQL_TIME_STATUS *status, const char *str, size_t len, CHARSET_INFO *cs, date_mode_t fuzzydate, uint dec) - :Datetime(status, str, len, cs, fuzzydate) + :Datetime(thd, status, str, len, cs, fuzzydate) { - trunc(dec); + round(thd, dec, time_round_mode_t(fuzzydate), &status->warnings); } - Datetime(int *warn, double nr, date_mode_t fuzzydate, uint dec) - :Datetime(warn, nr, fuzzydate) + Datetime(THD *thd, int *warn, double nr, date_mode_t fuzzydate, uint dec) + :Datetime(thd, warn, nr, fuzzydate) { - trunc(dec); + round(thd, dec, time_round_mode_t(fuzzydate), warn); } - Datetime(int *warn, const my_decimal *d, date_mode_t fuzzydate, uint dec) - :Datetime(warn, d, fuzzydate) + Datetime(THD *thd, int *warn, const my_decimal *d, date_mode_t fuzzydate, uint dec) + :Datetime(thd, warn, d, fuzzydate) { - trunc(dec); + round(thd, dec, time_round_mode_t(fuzzydate), warn); } Datetime(THD *thd, int *warn, const MYSQL_TIME *from, date_mode_t fuzzydate, uint dec) - :Datetime(thd, warn, from, fuzzydate) - { - trunc(dec); - } - Datetime(THD *thd, const timeval &tv, uint dec) - :Datetime(thd, tv) + :Datetime(thd, warn, from, date_conv_mode_t(fuzzydate) & ~TIME_TIME_ONLY) { - DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); - trunc(dec); + round(thd, dec, time_round_mode_t(fuzzydate), warn); } bool is_valid_datetime() const @@ -1706,14 +2020,14 @@ public: DBUG_ASSERT(is_valid_value_slow()); return time_type == MYSQL_TIMESTAMP_DATETIME; } - bool check_date(date_mode_t flags, int *warnings) const + bool check_date(date_conv_mode_t flags, int *warnings) const { DBUG_ASSERT(is_valid_datetime_slow()); return ::check_date(this, (year || month || day), ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), warnings); } - bool check_date(date_mode_t flags) const + bool check_date(date_conv_mode_t flags) const { int dummy; /* unused */ return check_date(flags, &dummy); @@ -1728,6 +2042,27 @@ public: DBUG_ASSERT(is_valid_datetime_slow()); return Temporal_with_date::daynr(); } + ulong dayofyear() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::dayofyear(); + } + uint quarter() const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::quarter(); + } + uint week(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::week(week_behaviour); + } + uint yearweek(uint week_behaviour) const + { + DBUG_ASSERT(is_valid_datetime_slow()); + return Temporal_with_date::yearweek(week_behaviour); + } + longlong hhmmss_to_seconds_abs() const { DBUG_ASSERT(is_valid_datetime_slow()); @@ -1800,6 +2135,12 @@ public: { return is_valid_datetime() ? Temporal::to_packed() : 0; } + long fraction_remainder(uint dec) const + { + DBUG_ASSERT(is_valid_datetime()); + return Temporal::fraction_remainder(dec); + } + Datetime &trunc(uint dec) { if (is_valid_datetime()) @@ -1807,21 +2148,129 @@ public: DBUG_ASSERT(is_valid_value_slow()); return *this; } + Datetime &round(THD *thd, uint dec, int *warn) + { + if (is_valid_datetime()) + round_or_invalidate(thd, dec, warn); + DBUG_ASSERT(is_valid_value_slow()); + return *this; + } + Datetime &round(THD *thd, uint dec, time_round_mode_t mode, int *warn) + { + switch (mode.mode()) { + case time_round_mode_t::FRAC_NONE: + DBUG_ASSERT(fraction_remainder(dec) == 0); + return trunc(dec); + case time_round_mode_t::FRAC_TRUNCATE: + return trunc(dec); + case time_round_mode_t::FRAC_ROUND: + return round(thd, dec, warn); + } + return *this; + } + Datetime &round(THD *thd, uint dec, time_round_mode_t mode) + { + int warn= 0; + return round(thd, dec, mode, &warn); + } + +}; + + +/* + Datetime to be created from an Item who is known to be of a temporal + data type. For temporal data types we don't need nanosecond rounding + or truncation, as their precision is limited. +*/ +class Datetime_from_temporal: public Datetime +{ +public: + // The constructor DBUG_ASSERTs on a proper Item data type. + Datetime_from_temporal(THD *thd, Item *temporal, date_conv_mode_t flags); +}; + + +/* + Datetime to be created from an Item who is known not to have digits outside + of the specified scale. So it's not important which rounding method to use. + TRUNCATE should work. + Typically, Item is of a temporal data type, but this is not strictly required. +*/ +class Datetime_truncation_not_needed: public Datetime +{ +public: + Datetime_truncation_not_needed(THD *thd, Item *item, date_conv_mode_t mode); + Datetime_truncation_not_needed(THD *thd, Item *item, date_mode_t mode) + :Datetime_truncation_not_needed(thd, item, date_conv_mode_t(mode)) + { } }; class Timestamp: protected Timeval { +protected: + void round_or_set_max(uint dec, int *warn); + bool add_nanoseconds_usec(uint nanoseconds) + { + DBUG_ASSERT(nanoseconds <= 1000000000); + if (nanoseconds < 500) + return false; + tv_usec+= (nanoseconds + 500) / 1000; + if (tv_usec < 1000000) + return false; + tv_usec%= 1000000; + return true; + } +public: + static date_conv_mode_t sql_mode_for_timestamp(THD *thd); + static time_round_mode_t default_round_mode(THD *thd); + class DatetimeOptions: public date_mode_t + { + public: + DatetimeOptions(date_conv_mode_t fuzzydate, time_round_mode_t round_mode) + :date_mode_t(fuzzydate | round_mode) + { } + DatetimeOptions(THD *thd) + :DatetimeOptions(sql_mode_for_timestamp(thd), default_round_mode(thd)) + { } + }; public: Timestamp(my_time_t timestamp, ulong sec_part) :Timeval(timestamp, sec_part) { } const struct timeval &tv() const { return *this; } + long fraction_remainder(uint dec) const + { + return my_time_fraction_remainder(tv_usec, dec); + } Timestamp &trunc(uint dec) { my_timeval_trunc(this, dec); return *this; } + Timestamp &round(uint dec, int *warn) + { + round_or_set_max(dec, warn); + return *this; + } + Timestamp &round(uint dec, time_round_mode_t mode, int *warn) + { + switch (mode.mode()) { + case time_round_mode_t::FRAC_NONE: + DBUG_ASSERT(fraction_remainder(dec) == 0); + return trunc(dec); + case time_round_mode_t::FRAC_TRUNCATE: + return trunc(dec); + case time_round_mode_t::FRAC_ROUND: + return round(dec, warn); + } + return *this; + } + Timestamp &round(uint dec, time_round_mode_t mode) + { + int warn= 0; + return round(dec, mode, &warn); + } }; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 92c7d329bb9..3a4871875e5 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3353,6 +3353,7 @@ static const char *sql_mode_names[]= "ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL", "NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION", "PAD_CHAR_TO_FULL_LENGTH", "EMPTY_STRING_IS_NULL", "SIMULTANEOUS_ASSIGNMENT", + "TIME_ROUND_FRACTIONAL", 0 }; diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index dbc3565e202..1c41cc22411 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -2659,7 +2659,8 @@ public: if (!Sys_var_enum::do_check(thd, var)) return false; MYSQL_TIME ltime; - bool res= var->value->get_date(thd, <ime, date_mode_t(0)); + Datetime::Options opt(TIME_CONV_NONE, thd); + bool res= var->value->get_date(thd, <ime, opt); if (!res) { var->save_result.ulonglong_value= SYSTEM_TIME_AS_OF; @@ -2676,7 +2677,9 @@ private: { if (var->value) { - res= var->value->get_date(current_thd, &out.ltime, date_mode_t(0)); + THD *thd= current_thd; + Datetime::Options opt(TIME_CONV_NONE, thd); + res= var->value->get_date(thd, &out.ltime, opt); } else // set DEFAULT from global var { diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index 4af8811800e..7a41442d4ba 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -10576,7 +10576,7 @@ int ha_mroonga::generic_store_bulk_time(Field *field, grn_obj *buf) bool truncated = false; Field_time *time_field = (Field_time *)field; MYSQL_TIME mysql_time; - time_field->get_time(&mysql_time); + time_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); @@ -10596,7 +10596,7 @@ int ha_mroonga::generic_store_bulk_datetime(Field *field, grn_obj *buf) bool truncated = false; Field_datetime *datetime_field = (Field_datetime *)field; MYSQL_TIME mysql_time; - datetime_field->get_time(&mysql_time); + datetime_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); @@ -10657,7 +10657,7 @@ int ha_mroonga::generic_store_bulk_datetime2(Field *field, grn_obj *buf) bool truncated = false; Field_datetimef *datetimef_field = (Field_datetimef *)field; MYSQL_TIME mysql_time; - datetimef_field->get_time(&mysql_time); + datetimef_field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); @@ -10682,7 +10682,7 @@ int ha_mroonga::generic_store_bulk_time2(Field *field, grn_obj *buf) int error = 0; bool truncated = false; MYSQL_TIME mysql_time; - field->get_time(&mysql_time); + field->get_date(&mysql_time, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated); @@ -10707,7 +10707,7 @@ int ha_mroonga::generic_store_bulk_new_date(Field *field, grn_obj *buf) bool truncated = false; Field_newdate *newdate_field = (Field_newdate *)field; MYSQL_TIME mysql_date; - newdate_field->get_time(&mysql_date); + newdate_field->get_date(&mysql_date, Time::Options(current_thd)); mrn::TimeConverter time_converter; long long int time = time_converter.mysql_time_to_grn_time(&mysql_date, &truncated); @@ -11617,14 +11617,14 @@ int ha_mroonga::storage_encode_key_timestamp(Field *field, const uchar *key, } else { Field_timestamp_hires *timestamp_hires_field = (Field_timestamp_hires *)field; - uint fuzzy_date = 0; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; TABLE *table_backup = field->table; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); field->table = table; - timestamp_hires_field->get_date(&mysql_time, date_mode_t(fuzzy_date)); + Temporal::Options opt(TIME_CONV_NONE, current_thd); + timestamp_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; field->table = table_backup; @@ -11675,12 +11675,12 @@ int ha_mroonga::storage_encode_key_time(Field *field, const uchar *key, mysql_time.time_type = MYSQL_TIMESTAMP_TIME; } else { Field_time_hires *time_hires_field = (Field_time_hires *)field; - uint fuzzy_date = 0; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); - time_hires_field->get_date(&mysql_time, date_mode_t(fuzzy_date)); + Temporal::Options opt(TIME_CONV_NONE, current_thd); + time_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; } @@ -11749,12 +11749,12 @@ int ha_mroonga::storage_encode_key_datetime(Field *field, const uchar *key, if (field->decimals() > 0) { Field_datetime_hires *datetime_hires_field = (Field_datetime_hires *)field; MYSQL_TIME mysql_time; - uint fuzzy_date = 0; uchar *ptr_backup = field->ptr; uchar *null_ptr_backup = field->null_ptr; field->ptr = (uchar *)key; field->null_ptr = (uchar *)(key - 1); - datetime_hires_field->get_date(&mysql_time, date_mode_t(fuzzy_date)); + Temporal::Options opt(TIME_CONV_NONE, current_thd); + datetime_hires_field->get_date(&mysql_time, opt); field->ptr = ptr_backup; field->null_ptr = null_ptr_backup; mrn::TimeConverter time_converter; diff --git a/storage/mroonga/lib/mrn_condition_converter.cpp b/storage/mroonga/lib/mrn_condition_converter.cpp index 49f8e10753a..68ffa073f4f 100644 --- a/storage/mroonga/lib/mrn_condition_converter.cpp +++ b/storage/mroonga/lib/mrn_condition_converter.cpp @@ -258,8 +258,11 @@ namespace mrn { Item *real_value_item = value_item->real_item(); switch (field_item->field->type()) { case MYSQL_TYPE_TIME: - error = real_value_item->get_time(current_thd, mysql_time); + { + THD *thd= current_thd; + error= real_value_item->get_date(thd, mysql_time, Time::Options(thd)); break; + } case MYSQL_TYPE_YEAR: mysql_time->year = static_cast<int>(value_item->val_int()); mysql_time->month = 1; @@ -273,9 +276,13 @@ namespace mrn { error = false; break; default: - error = real_value_item->get_date(current_thd, mysql_time, TIME_FUZZY_DATES); + { + THD *thd= current_thd; + Datetime::Options opt(TIME_FUZZY_DATES, thd); + error = real_value_item->get_date(thd, mysql_time, opt); break; } + } DBUG_RETURN(error); } |