Commit d2ba9edd authored by Alexander Barkov's avatar Alexander Barkov

MDEV-17740 Extend EXTRACT(x AS DAY*) to understand long time intervals

parent f92d223f
......@@ -79,6 +79,16 @@ extern uchar days_in_month[];
/* Useful constants */
#define SECONDS_IN_24H 86400L
/* Limits for the INTERVAL data type */
/* Number of hours between '0001-01-01 00h' and '9999-12-31 23h' */
#define TIME_MAX_INTERVAL_HOUR 87649415
#define TIME_MAX_INTERVAL_HOUR_CHAR_LENGTH 8
/* Number of full days between '0001-01-01' and '9999-12-31'*/
#define TIME_MAX_INTERVAL_DAY 3652058 /*87649415/24*/
#define TIME_MAX_INTERVAL_DAY_CHAR_LENGTH 7
/* Limits for the TIME data type */
#define TIME_MAX_HOUR 838
#define TIME_MAX_MINUTE 59
......@@ -112,7 +122,21 @@ my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *l_time,
ulong max_hour, MYSQL_TIME_STATUS *status);
my_bool str_to_datetime_or_date_or_time(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status);
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool
str_to_datetime_or_date_or_interval_hhmmssff(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool
str_to_datetime_or_date_or_interval_day(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool str_to_datetime_or_date(const char *str, size_t length, MYSQL_TIME *to,
ulonglong flags, MYSQL_TIME_STATUS *status);
......@@ -120,7 +144,7 @@ longlong number_to_datetime_or_date(longlong nr, ulong sec_part,
MYSQL_TIME *time_res,
ulonglong flags, int *was_cut);
int number_to_time_only(my_bool neg, ulonglong nr, ulong sec_part,
MYSQL_TIME *ltime, int *was_cut);
ulong max_hour, MYSQL_TIME *to, int *was_cut);
ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *);
ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *);
......
......@@ -3215,7 +3215,8 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, size_t
{
MYSQL_TIME *tm= (MYSQL_TIME *)buffer;
MYSQL_TIME_STATUS status;
str_to_datetime_or_date_or_time(value, length, tm, 0, &status);
str_to_datetime_or_date_or_time(value, length, tm, 0, &status,
TIME_MAX_HOUR, UINT_MAX32);
err= status.warnings;
*param->error= MY_TEST(err);
break;
......
#
# MDEV-17385 MICROSECOND() returns confusing results with an out-of-range TIME-alike argument
#
CREATE TABLE t1 (v VARCHAR(64), ll BIGINT, t TIME, dt DATETIME, d DATE);
CREATE TABLE t2 AS SELECT
EXTRACT(DAY FROM t),
EXTRACT(DAY_HOUR FROM t),
EXTRACT(DAY_MINUTE FROM t),
EXTRACT(DAY_SECOND FROM t),
EXTRACT(DAY_MICROSECOND FROM t),
EXTRACT(DAY FROM d),
EXTRACT(DAY_HOUR FROM d),
EXTRACT(DAY_MINUTE FROM d),
EXTRACT(DAY_SECOND FROM d),
EXTRACT(DAY_MICROSECOND FROM d),
EXTRACT(DAY FROM v),
EXTRACT(DAY_HOUR FROM v),
EXTRACT(DAY_MINUTE FROM v),
EXTRACT(DAY_SECOND FROM v),
EXTRACT(DAY_MICROSECOND FROM v),
EXTRACT(DAY FROM ll),
EXTRACT(DAY_HOUR FROM ll),
EXTRACT(DAY_MINUTE FROM ll),
EXTRACT(DAY_SECOND FROM ll),
EXTRACT(DAY_MICROSECOND FROM ll)
FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`EXTRACT(DAY FROM t)` int(3) DEFAULT NULL,
`EXTRACT(DAY_HOUR FROM t)` int(5) DEFAULT NULL,
`EXTRACT(DAY_MINUTE FROM t)` int(7) DEFAULT NULL,
`EXTRACT(DAY_SECOND FROM t)` int(9) DEFAULT NULL,
`EXTRACT(DAY_MICROSECOND FROM t)` bigint(15) DEFAULT NULL,
`EXTRACT(DAY FROM d)` int(3) DEFAULT NULL,
`EXTRACT(DAY_HOUR FROM d)` int(5) DEFAULT NULL,
`EXTRACT(DAY_MINUTE FROM d)` int(7) DEFAULT NULL,
`EXTRACT(DAY_SECOND FROM d)` int(9) DEFAULT NULL,
`EXTRACT(DAY_MICROSECOND FROM d)` bigint(15) DEFAULT NULL,
`EXTRACT(DAY FROM v)` int(8) DEFAULT NULL,
`EXTRACT(DAY_HOUR FROM v)` int(10) DEFAULT NULL,
`EXTRACT(DAY_MINUTE FROM v)` bigint(12) DEFAULT NULL,
`EXTRACT(DAY_SECOND FROM v)` bigint(14) DEFAULT NULL,
`EXTRACT(DAY_MICROSECOND FROM v)` bigint(20) DEFAULT NULL,
`EXTRACT(DAY FROM ll)` int(8) DEFAULT NULL,
`EXTRACT(DAY_HOUR FROM ll)` int(10) DEFAULT NULL,
`EXTRACT(DAY_MINUTE FROM ll)` bigint(12) DEFAULT NULL,
`EXTRACT(DAY_SECOND FROM ll)` bigint(14) DEFAULT NULL,
`EXTRACT(DAY_MICROSECOND FROM ll)` bigint(20) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t2;
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(64), b DECIMAL(32,9));
INSERT INTO t1 VALUES
('9999-12-31 23:59:59.123456', 99991231235959.123456),
('2001-01-01 10:20:30.123456', 20010101102030.123456),
('4294967296:59:59.123456', 42949672965959.123456),
('4294967295:59:59.123456', 42949672955959.123456),
('87649416:59:59.123456', 876494165959.123456),
('87649415:59:59.123456', 876494155959.123456),
('87649414:59:59.123456', 876494145959.123456),
('9999:59:59.123456', 99995959.123456),
('9999:01:01.123456', 99990101.123456),
('9999:01:01', 99990101),
('0.999999', 0.999999),
('0.99999', 0.99999),
('0.9999', 0.9999),
('0.999', 0.999),
('0.99', 0.99),
('0.9', 0.9),
('000000',0);
# Summary:
# Check that FUNC(varchar) and FUNC(decimal) give equal results
# Expect empty sets
SELECT a, b, EXTRACT(DAY_HOUR FROM a), EXTRACT(DAY_HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(DAY_HOUR FROM a)<=>EXTRACT(DAY_HOUR FROM b));
a b EXTRACT(DAY_HOUR FROM a) EXTRACT(DAY_HOUR FROM b)
SELECT a, b, EXTRACT(DAY FROM a), EXTRACT(DAY FROM b) FROM t1 WHERE NOT (EXTRACT(DAY FROM a)<=>EXTRACT(DAY FROM b));
a b EXTRACT(DAY FROM a) EXTRACT(DAY FROM b)
SELECT a, b, EXTRACT(HOUR FROM a), EXTRACT(HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(HOUR FROM a)<=>EXTRACT(HOUR FROM b));
a b EXTRACT(HOUR FROM a) EXTRACT(HOUR FROM b)
SELECT a, b, EXTRACT(MINUTE FROM a), EXTRACT(MINUTE FROM b) FROM t1 WHERE NOT (EXTRACT(MINUTE FROM a)<=>EXTRACT(MINUTE FROM b));
a b EXTRACT(MINUTE FROM a) EXTRACT(MINUTE FROM b)
SELECT a, b, EXTRACT(SECOND FROM a), EXTRACT(SECOND FROM b) FROM t1 WHERE NOT (EXTRACT(SECOND FROM a)<=>EXTRACT(SECOND FROM b));
a b EXTRACT(SECOND FROM a) EXTRACT(SECOND FROM b)
SELECT a, b, EXTRACT(MICROSECOND FROM a), EXTRACT(MICROSECOND FROM b) FROM t1 WHERE NOT (EXTRACT(MICROSECOND FROM a)<=>EXTRACT(MICROSECOND FROM b));
a b EXTRACT(MICROSECOND FROM a) EXTRACT(MICROSECOND FROM b)
# Detailed results
SELECT
a,
EXTRACT(DAY FROM a) * 24 + EXTRACT(HOUR FROM a) AS dh,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
a dh EXTRACT(DAY_HOUR FROM a) EXTRACT(DAY FROM a) EXTRACT(HOUR FROM a) EXTRACT(MINUTE FROM a) EXTRACT(SECOND FROM a) EXTRACT(MICROSECOND FROM a)
9999-12-31 23:59:59.123456 767 3123 31 23 59 59 123456
2001-01-01 10:20:30.123456 34 110 1 10 20 30 123456
4294967296:59:59.123456 NULL NULL NULL NULL NULL NULL NULL
4294967295:59:59.123456 NULL NULL NULL NULL NULL NULL NULL
87649416:59:59.123456 NULL NULL NULL NULL NULL NULL NULL
87649415:59:59.123456 87649415 365205823 3652058 23 59 59 123456
87649414:59:59.123456 87649414 365205822 3652058 22 59 59 123456
9999:59:59.123456 9999 41615 416 15 59 59 123456
9999:01:01.123456 9999 41615 416 15 1 1 123456
9999:01:01 9999 41615 416 15 1 1 0
0.999999 0 0 0 0 0 0 999999
0.99999 0 0 0 0 0 0 999990
0.9999 0 0 0 0 0 0 999900
0.999 0 0 0 0 0 0 999000
0.99 0 0 0 0 0 0 990000
0.9 0 0 0 0 0 0 900000
000000 0 0 0 0 0 0 0
Warnings:
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967296:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '4294967295:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
Warning 1292 Incorrect interval value: '87649416:59:59.123456'
SELECT
b,
EXTRACT(DAY FROM b) * 24 + EXTRACT(HOUR FROM b) AS dh,
EXTRACT(DAY_HOUR FROM b),
EXTRACT(DAY FROM b),
EXTRACT(HOUR FROM b),
EXTRACT(MINUTE FROM b),
EXTRACT(SECOND FROM b),
EXTRACT(MICROSECOND FROM b)
FROM t1;
b dh EXTRACT(DAY_HOUR FROM b) EXTRACT(DAY FROM b) EXTRACT(HOUR FROM b) EXTRACT(MINUTE FROM b) EXTRACT(SECOND FROM b) EXTRACT(MICROSECOND FROM b)
99991231235959.123456000 767 3123 31 23 59 59 123456
20010101102030.123456000 34 110 1 10 20 30 123456
42949672965959.123456000 NULL NULL NULL NULL NULL NULL NULL
42949672955959.123456000 NULL NULL NULL NULL NULL NULL NULL
876494165959.123456000 NULL NULL NULL NULL NULL NULL NULL
876494155959.123456000 87649415 365205823 3652058 23 59 59 123456
876494145959.123456000 87649414 365205822 3652058 22 59 59 123456
99995959.123456000 9999 41615 416 15 59 59 123456
99990101.123456000 9999 41615 416 15 1 1 123456
99990101.000000000 9999 41615 416 15 1 1 0
0.999999000 0 0 0 0 0 0 999999
0.999990000 0 0 0 0 0 0 999990
0.999900000 0 0 0 0 0 0 999900
0.999000000 0 0 0 0 0 0 999000
0.990000000 0 0 0 0 0 0 990000
0.900000000 0 0 0 0 0 0 900000
0.000000000 0 0 0 0 0 0 0
Warnings:
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672965959.123456000' for column 'b' at row 3
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '42949672955959.123456000' for column 'b' at row 4
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
Warning 1292 Incorrect interval value: '876494165959.123456000' for column 'b' at row 5
DROP TABLE t1;
# Special case: DAY + TIME
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('9999-01-01');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
a EXTRACT(DAY_HOUR FROM a) EXTRACT(DAY_MINUTE FROM a) EXTRACT(DAY_SECOND FROM a) EXTRACT(DAY_MICROSECOND FROM a) EXTRACT(DAY FROM a) EXTRACT(HOUR FROM a) EXTRACT(MINUTE FROM a) EXTRACT(SECOND FROM a) EXTRACT(MICROSECOND FROM a)
9999-01-01 100 10000 1000000 1000000000000 1 0 0 0 0
DROP TABLE t1;
# Bad values
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
a EXTRACT(DAY_HOUR FROM a) EXTRACT(DAY_MINUTE FROM a) EXTRACT(DAY_SECOND FROM a) EXTRACT(DAY_MICROSECOND FROM a) EXTRACT(DAY FROM a) EXTRACT(HOUR FROM a) EXTRACT(MINUTE FROM a) EXTRACT(SECOND FROM a) EXTRACT(MICROSECOND FROM a)
NULL NULL NULL NULL NULL NULL NULL NULL NULL
Warnings:
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
Warning 1292 Incorrect interval value: ''
DROP TABLE t1;
# Backward compatibility
# This still parses as DATETIME
SELECT EXTRACT(YEAR FROM '2001/02/03 10:20:30');
EXTRACT(YEAR FROM '2001/02/03 10:20:30')
2001
SELECT EXTRACT(MONTH FROM '2001/02/03 10:20:30');
EXTRACT(MONTH FROM '2001/02/03 10:20:30')
2
SELECT EXTRACT(DAY FROM '2001/02/03 10:20:30');
EXTRACT(DAY FROM '2001/02/03 10:20:30')
3
SELECT EXTRACT(YEAR FROM '01/02/03 10:20:30');
EXTRACT(YEAR FROM '01/02/03 10:20:30')
2001
SELECT EXTRACT(MONTH FROM '01/02/03 10:20:30');
EXTRACT(MONTH FROM '01/02/03 10:20:30')
2
SELECT EXTRACT(DAY FROM '01/02/03 10:20:30');
EXTRACT(DAY FROM '01/02/03 10:20:30')
3
SELECT EXTRACT(YEAR FROM '01:02:03 10:20:30');
EXTRACT(YEAR FROM '01:02:03 10:20:30')
2001
SELECT EXTRACT(MONTH FROM '01:02:03 10:20:30');
EXTRACT(MONTH FROM '01:02:03 10:20:30')
2
SELECT EXTRACT(DAY FROM '01:02:03 10:20:30');
EXTRACT(DAY FROM '01:02:03 10:20:30')
3
# This still parses as DATETIME and returns NULL
SELECT EXTRACT(YEAR FROM "2011-02-32 8:46:06.23434");
EXTRACT(YEAR FROM "2011-02-32 8:46:06.23434")
NULL
Warnings:
Warning 1292 Incorrect datetime value: '2011-02-32 8:46:06.23434'
SELECT EXTRACT(MONTH FROM "2011-02-32 8:46:06.23434");
EXTRACT(MONTH FROM "2011-02-32 8:46:06.23434")
NULL
Warnings:
Warning 1292 Incorrect datetime value: '2011-02-32 8:46:06.23434'
SELECT EXTRACT(DAY FROM "2011-02-32 8:46:06.23434");
EXTRACT(DAY FROM "2011-02-32 8:46:06.23434")
NULL
Warnings:
Warning 1292 Incorrect interval value: '2011-02-32 8:46:06.23434'
SELECT EXTRACT(HOUR FROM "2011-02-32 8:46:06.23434");
EXTRACT(HOUR FROM "2011-02-32 8:46:06.23434")
NULL
Warnings:
Warning 1292 Incorrect interval value: '2011-02-32 8:46:06.23434'
# This still parses as DATE
SELECT EXTRACT(YEAR FROM '2001/02/03');
EXTRACT(YEAR FROM '2001/02/03')
2001
SELECT EXTRACT(MONTH FROM '2001/02/03');
EXTRACT(MONTH FROM '2001/02/03')
2
SELECT EXTRACT(DAY FROM '2001/02/03');
EXTRACT(DAY FROM '2001/02/03')
3
SELECT EXTRACT(YEAR FROM '01/02/03');
EXTRACT(YEAR FROM '01/02/03')
2001
SELECT EXTRACT(MONTH FROM '01/02/03');
EXTRACT(MONTH FROM '01/02/03')
2
SELECT EXTRACT(DAY FROM '01/02/03');
EXTRACT(DAY FROM '01/02/03')
3
SELECT EXTRACT(YEAR FROM '01-02-03');
EXTRACT(YEAR FROM '01-02-03')
2001
SELECT EXTRACT(MONTH FROM '01-02-03');
EXTRACT(MONTH FROM '01-02-03')
2
SELECT EXTRACT(DAY FROM '01-02-03');
EXTRACT(DAY FROM '01-02-03')
3
SELECT EXTRACT(YEAR FROM '1-2-3');
EXTRACT(YEAR FROM '1-2-3')
1
SELECT EXTRACT(MONTH FROM '1-2-3');
EXTRACT(MONTH FROM '1-2-3')
2
SELECT EXTRACT(DAY FROM '1-2-3');
EXTRACT(DAY FROM '1-2-3')
3
SELECT EXTRACT(HOUR FROM '1-2-3');
EXTRACT(HOUR FROM '1-2-3')
0
SELECT EXTRACT(DAY FROM '2024-01-03 garbage /////');
EXTRACT(DAY FROM '2024-01-03 garbage /////')
3
Warnings:
Warning 1292 Truncated incorrect date value: '2024-01-03 garbage /////'
SELECT EXTRACT(DAY FROM '24-01-03 garbage /////');
EXTRACT(DAY FROM '24-01-03 garbage /////')
3
Warnings:
Warning 1292 Truncated incorrect date value: '24-01-03 garbage /////'
SELECT EXTRACT(DAY FROM '01-02-03');
EXTRACT(DAY FROM '01-02-03')
3
SELECT EXTRACT(DAY FROM '24:02:03T');
EXTRACT(DAY FROM '24:02:03T')
3
SELECT EXTRACT(DAY FROM '24-02-03');
EXTRACT(DAY FROM '24-02-03')
3
SELECT EXTRACT(DAY FROM '24/02/03');
EXTRACT(DAY FROM '24/02/03')
3
SELECT EXTRACT(DAY FROM '11111');
EXTRACT(DAY FROM '11111')
1
SELECT TIME('2001-01-01T'), TIME('2001-01-01T ');
TIME('2001-01-01T') TIME('2001-01-01T ')
00:00:00 00:00:00
SELECT TIME('2001/01/01T'), TIME('2001/01/01T ');
TIME('2001/01/01T') TIME('2001/01/01T ')
00:00:00 00:00:00
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
TIME('2001:01:01T') TIME('2001:01:01T ')
00:00:00 00:00:00
SELECT EXTRACT(DAY FROM '2001-01-01T'), EXTRACT(DAY FROM '2001-01-01T ');
EXTRACT(DAY FROM '2001-01-01T') EXTRACT(DAY FROM '2001-01-01T ')
1 1
SELECT EXTRACT(DAY FROM '2001/01/01T'), EXTRACT(DAY FROM '2001/01/01T ');
EXTRACT(DAY FROM '2001/01/01T') EXTRACT(DAY FROM '2001/01/01T ')
1 1
SELECT EXTRACT(DAY FROM '2001:01:01T'), EXTRACT(DAY FROM '2001:01:01T ');
EXTRACT(DAY FROM '2001:01:01T') EXTRACT(DAY FROM '2001:01:01T ')
1 1
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
TIME('2001:01:01T') TIME('2001:01:01T ')
00:00:00 00:00:00
SELECT EXTRACT(HOUR FROM '2001-01-01T'), EXTRACT(HOUR FROM '2001-01-01T ');
EXTRACT(HOUR FROM '2001-01-01T') EXTRACT(HOUR FROM '2001-01-01T ')
0 0
SELECT EXTRACT(HOUR FROM '2001/01/01T'), EXTRACT(HOUR FROM '2001/01/01T ');
EXTRACT(HOUR FROM '2001/01/01T') EXTRACT(HOUR FROM '2001/01/01T ')
0 0
SELECT EXTRACT(HOUR FROM '2001:01:01T'), EXTRACT(HOUR FROM '2001:01:01T ');
EXTRACT(HOUR FROM '2001:01:01T') EXTRACT(HOUR FROM '2001:01:01T ')
0 0
# This still parses as DATE and returns NULL (without trying TIME)
SELECT EXTRACT(DAY FROM '100000:02:03T');
EXTRACT(DAY FROM '100000:02:03T')
NULL
Warnings:
Warning 1292 Incorrect interval value: '100000:02:03T'
SELECT EXTRACT(DAY FROM '100000/02/03');
EXTRACT(DAY FROM '100000/02/03')
NULL
Warnings:
Warning 1292 Incorrect interval value: '100000/02/03'
SELECT EXTRACT(DAY FROM '100000-02-03');
EXTRACT(DAY FROM '100000-02-03')
NULL
Warnings:
Warning 1292 Incorrect interval value: '100000-02-03'
SELECT EXTRACT(DAY FROM '1111');
EXTRACT(DAY FROM '1111')
NULL
Warnings:
Warning 1292 Incorrect interval value: '1111'
SELECT EXTRACT(DAY FROM '111');
EXTRACT(DAY FROM '111')
NULL
Warnings:
Warning 1292 Incorrect interval value: '111'
SELECT EXTRACT(DAY FROM '11');
EXTRACT(DAY FROM '11')
NULL
Warnings:
Warning 1292 Incorrect interval value: '11'
SELECT EXTRACT(DAY FROM '1');
EXTRACT(DAY FROM '1')
NULL
Warnings:
Warning 1292 Incorrect interval value: '1'
# This still parses as TIME
SELECT EXTRACT(HOUR FROM '11111');
EXTRACT(HOUR FROM '11111')
1
SELECT EXTRACT(HOUR FROM '1111');
EXTRACT(HOUR FROM '1111')
0
SELECT EXTRACT(HOUR FROM '111');
EXTRACT(HOUR FROM '111')
0
SELECT EXTRACT(HOUR FROM '11');
EXTRACT(HOUR FROM '11')
0
SELECT EXTRACT(HOUR FROM '1');
EXTRACT(HOUR FROM '1')
0
SELECT TIME('01:02:03:');
TIME('01:02:03:')
01:02:03
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03:'
SELECT TIME('01:02:03-');
TIME('01:02:03-')
01:02:03
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03-'
SELECT TIME('01:02:03;');
TIME('01:02:03;')
01:02:03
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03;'
SELECT TIME('01:02:03/');
TIME('01:02:03/')
01:02:03
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03/'
SELECT EXTRACT(HOUR FROM '01:02:03:');
EXTRACT(HOUR FROM '01:02:03:')
1
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03:'
SELECT EXTRACT(HOUR FROM '01:02:03-');
EXTRACT(HOUR FROM '01:02:03-')
1
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03-'
SELECT EXTRACT(HOUR FROM '01:02:03;');
EXTRACT(HOUR FROM '01:02:03;')
1
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03;'
SELECT EXTRACT(HOUR FROM '01:02:03/');
EXTRACT(HOUR FROM '01:02:03/')
1
Warnings:
Warning 1292 Truncated incorrect time value: '01:02:03/'
# Backward compatibility preserved for YEAR and MONTH only
# (behavior has changed for DAY, see below)
SELECT EXTRACT(YEAR FROM '01:02:03');
EXTRACT(YEAR FROM '01:02:03')
2001
SELECT EXTRACT(MONTH FROM '01:02:03');
EXTRACT(MONTH FROM '01:02:03')
2
SELECT EXTRACT(YEAR FROM '24:01:03 garbage /////');
EXTRACT(YEAR FROM '24:01:03 garbage /////')
2024
Warnings:
Warning 1292 Truncated incorrect date value: '24:01:03 garbage /////'
SELECT EXTRACT(MONTH FROM '24:01:03 garbage /////');
EXTRACT(MONTH FROM '24:01:03 garbage /////')
1
Warnings:
Warning 1292 Truncated incorrect date value: '24:01:03 garbage /////'
# This still parses as TIME 00:20:01
SELECT TIME('2001/01/01');
TIME('2001/01/01')
00:20:01
Warnings:
Warning 1292 Truncated incorrect time value: '2001/01/01'
SELECT TIME('2001-01-01');
TIME('2001-01-01')
00:20:01
Warnings:
Warning 1292 Truncated incorrect time value: '2001-01-01'
# This still parses as TIME and overflows to '838:59:59'
SELECT TIME('2001:01:01');
TIME('2001:01:01')
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '2001:01:01'
# This used to parse as DATE, now parses as TIME interval
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES
('2024:01:03 garbage /////'),
('24:01:03 garbage /////'),
('01:01:03 garbage /////'),
('2024:02:03'),
('100000:02:03'),
('24:02:03'),
('01:02:03'),
('01:02:03:'),
('01:02:03-'),
('01:02:03;'),
('01:02:03/'),
('20 10:20:30');
SELECT EXTRACT(DAY FROM a), EXTRACT(DAY_SECOND FROM a), a FROM t1;
EXTRACT(DAY FROM a) EXTRACT(DAY_SECOND FROM a) a
84 84080103 2024:01:03 garbage /////
1 1000103 24:01:03 garbage /////
0 10103 01:01:03 garbage /////
84 84080203 2024:02:03
4166 4166160203 100000:02:03
1 1000203 24:02:03
0 10203 01:02:03
0 10203 01:02:03:
0 10203 01:02:03-
0 10203 01:02:03;
0 10203 01:02:03/
20 20102030 20 10:20:30
Warnings:
Warning 1292 Truncated incorrect time value: '2024:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '2024:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '24:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '24:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '01:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '01:01:03 garbage /////'
Warning 1292 Truncated incorrect time value: '01:02:03:'
Warning 1292 Truncated incorrect time value: '01:02:03:'
Warning 1292 Truncated incorrect time value: '01:02:03-'
Warning 1292 Truncated incorrect time value: '01:02:03-'
Warning 1292 Truncated incorrect time value: '01:02:03;'
Warning 1292 Truncated incorrect time value: '01:02:03;'
Warning 1292 Truncated incorrect time value: '01:02:03/'
Warning 1292 Truncated incorrect time value: '01:02:03/'
DROP TABLE t1;
--echo #
--echo # MDEV-17385 MICROSECOND() returns confusing results with an out-of-range TIME-alike argument
--echo #
CREATE TABLE t1 (v VARCHAR(64), ll BIGINT, t TIME, dt DATETIME, d DATE);
CREATE TABLE t2 AS SELECT
EXTRACT(DAY FROM t),
EXTRACT(DAY_HOUR FROM t),
EXTRACT(DAY_MINUTE FROM t),
EXTRACT(DAY_SECOND FROM t),
EXTRACT(DAY_MICROSECOND FROM t),
EXTRACT(DAY FROM d),
EXTRACT(DAY_HOUR FROM d),
EXTRACT(DAY_MINUTE FROM d),
EXTRACT(DAY_SECOND FROM d),
EXTRACT(DAY_MICROSECOND FROM d),
EXTRACT(DAY FROM v),
EXTRACT(DAY_HOUR FROM v),
EXTRACT(DAY_MINUTE FROM v),
EXTRACT(DAY_SECOND FROM v),
EXTRACT(DAY_MICROSECOND FROM v),
EXTRACT(DAY FROM ll),
EXTRACT(DAY_HOUR FROM ll),
EXTRACT(DAY_MINUTE FROM ll),
EXTRACT(DAY_SECOND FROM ll),
EXTRACT(DAY_MICROSECOND FROM ll)
FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(64), b DECIMAL(32,9));
INSERT INTO t1 VALUES
('9999-12-31 23:59:59.123456', 99991231235959.123456),
('2001-01-01 10:20:30.123456', 20010101102030.123456),
('4294967296:59:59.123456', 42949672965959.123456),
('4294967295:59:59.123456', 42949672955959.123456),
('87649416:59:59.123456', 876494165959.123456),
('87649415:59:59.123456', 876494155959.123456),
('87649414:59:59.123456', 876494145959.123456),
('9999:59:59.123456', 99995959.123456),
('9999:01:01.123456', 99990101.123456),
('9999:01:01', 99990101),
('0.999999', 0.999999),
('0.99999', 0.99999),
('0.9999', 0.9999),
('0.999', 0.999),
('0.99', 0.99),
('0.9', 0.9),
('000000',0);
--echo # Summary:
--echo # Check that FUNC(varchar) and FUNC(decimal) give equal results
--echo # Expect empty sets
--disable_warnings
SELECT a, b, EXTRACT(DAY_HOUR FROM a), EXTRACT(DAY_HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(DAY_HOUR FROM a)<=>EXTRACT(DAY_HOUR FROM b));
SELECT a, b, EXTRACT(DAY FROM a), EXTRACT(DAY FROM b) FROM t1 WHERE NOT (EXTRACT(DAY FROM a)<=>EXTRACT(DAY FROM b));
SELECT a, b, EXTRACT(HOUR FROM a), EXTRACT(HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(HOUR FROM a)<=>EXTRACT(HOUR FROM b));
SELECT a, b, EXTRACT(MINUTE FROM a), EXTRACT(MINUTE FROM b) FROM t1 WHERE NOT (EXTRACT(MINUTE FROM a)<=>EXTRACT(MINUTE FROM b));
SELECT a, b, EXTRACT(SECOND FROM a), EXTRACT(SECOND FROM b) FROM t1 WHERE NOT (EXTRACT(SECOND FROM a)<=>EXTRACT(SECOND FROM b));
SELECT a, b, EXTRACT(MICROSECOND FROM a), EXTRACT(MICROSECOND FROM b) FROM t1 WHERE NOT (EXTRACT(MICROSECOND FROM a)<=>EXTRACT(MICROSECOND FROM b));
--enable_warnings
--echo # Detailed results
SELECT
a,
EXTRACT(DAY FROM a) * 24 + EXTRACT(HOUR FROM a) AS dh,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
SELECT
b,
EXTRACT(DAY FROM b) * 24 + EXTRACT(HOUR FROM b) AS dh,
EXTRACT(DAY_HOUR FROM b),
EXTRACT(DAY FROM b),
EXTRACT(HOUR FROM b),
EXTRACT(MINUTE FROM b),
EXTRACT(SECOND FROM b),
EXTRACT(MICROSECOND FROM b)
FROM t1;
DROP TABLE t1;
--echo # Special case: DAY + TIME
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('9999-01-01');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
DROP TABLE t1;
--echo # Bad values
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
DROP TABLE t1;
--echo # Backward compatibility
--echo # This still parses as DATETIME
SELECT EXTRACT(YEAR FROM '2001/02/03 10:20:30');
SELECT EXTRACT(MONTH FROM '2001/02/03 10:20:30');
SELECT EXTRACT(DAY FROM '2001/02/03 10:20:30');
SELECT EXTRACT(YEAR FROM '01/02/03 10:20:30');
SELECT EXTRACT(MONTH FROM '01/02/03 10:20:30');
SELECT EXTRACT(DAY FROM '01/02/03 10:20:30');
SELECT EXTRACT(YEAR FROM '01:02:03 10:20:30');
SELECT EXTRACT(MONTH FROM '01:02:03 10:20:30');
SELECT EXTRACT(DAY FROM '01:02:03 10:20:30');
--echo # This still parses as DATETIME and returns NULL
SELECT EXTRACT(YEAR FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(MONTH FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(DAY FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(HOUR FROM "2011-02-32 8:46:06.23434");
--echo # This still parses as DATE
SELECT EXTRACT(YEAR FROM '2001/02/03');
SELECT EXTRACT(MONTH FROM '2001/02/03');
SELECT EXTRACT(DAY FROM '2001/02/03');
SELECT EXTRACT(YEAR FROM '01/02/03');
SELECT EXTRACT(MONTH FROM '01/02/03');
SELECT EXTRACT(DAY FROM '01/02/03');
SELECT EXTRACT(YEAR FROM '01-02-03');
SELECT EXTRACT(MONTH FROM '01-02-03');
SELECT EXTRACT(DAY FROM '01-02-03');
SELECT EXTRACT(YEAR FROM '1-2-3');
SELECT EXTRACT(MONTH FROM '1-2-3');
SELECT EXTRACT(DAY FROM '1-2-3');
SELECT EXTRACT(HOUR FROM '1-2-3');
SELECT EXTRACT(DAY FROM '2024-01-03 garbage /////');
SELECT EXTRACT(DAY FROM '24-01-03 garbage /////');
SELECT EXTRACT(DAY FROM '01-02-03');
SELECT EXTRACT(DAY FROM '24:02:03T');
SELECT EXTRACT(DAY FROM '24-02-03');
SELECT EXTRACT(DAY FROM '24/02/03');
SELECT EXTRACT(DAY FROM '11111');
SELECT TIME('2001-01-01T'), TIME('2001-01-01T ');
SELECT TIME('2001/01/01T'), TIME('2001/01/01T ');
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
SELECT EXTRACT(DAY FROM '2001-01-01T'), EXTRACT(DAY FROM '2001-01-01T ');
SELECT EXTRACT(DAY FROM '2001/01/01T'), EXTRACT(DAY FROM '2001/01/01T ');
SELECT EXTRACT(DAY FROM '2001:01:01T'), EXTRACT(DAY FROM '2001:01:01T ');
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
SELECT EXTRACT(HOUR FROM '2001-01-01T'), EXTRACT(HOUR FROM '2001-01-01T ');
SELECT EXTRACT(HOUR FROM '2001/01/01T'), EXTRACT(HOUR FROM '2001/01/01T ');
SELECT EXTRACT(HOUR FROM '2001:01:01T'), EXTRACT(HOUR FROM '2001:01:01T ');
--echo # This still parses as DATE and returns NULL (without trying TIME)
SELECT EXTRACT(DAY FROM '100000:02:03T');
SELECT EXTRACT(DAY FROM '100000/02/03');
SELECT EXTRACT(DAY FROM '100000-02-03');
SELECT EXTRACT(DAY FROM '1111');
SELECT EXTRACT(DAY FROM '111');
SELECT EXTRACT(DAY FROM '11');
SELECT EXTRACT(DAY FROM '1');
--echo # This still parses as TIME
SELECT EXTRACT(HOUR FROM '11111');
SELECT EXTRACT(HOUR FROM '1111');
SELECT EXTRACT(HOUR FROM '111');
SELECT EXTRACT(HOUR FROM '11');
SELECT EXTRACT(HOUR FROM '1');
SELECT TIME('01:02:03:');
SELECT TIME('01:02:03-');
SELECT TIME('01:02:03;');
SELECT TIME('01:02:03/');
SELECT EXTRACT(HOUR FROM '01:02:03:');
SELECT EXTRACT(HOUR FROM '01:02:03-');
SELECT EXTRACT(HOUR FROM '01:02:03;');
SELECT EXTRACT(HOUR FROM '01:02:03/');
--echo # Backward compatibility preserved for YEAR and MONTH only
--echo # (behavior has changed for DAY, see below)
SELECT EXTRACT(YEAR FROM '01:02:03');
SELECT EXTRACT(MONTH FROM '01:02:03');
SELECT EXTRACT(YEAR FROM '24:01:03 garbage /////');
SELECT EXTRACT(MONTH FROM '24:01:03 garbage /////');
--echo # This still parses as TIME 00:20:01
SELECT TIME('2001/01/01');
SELECT TIME('2001-01-01');
--echo # This still parses as TIME and overflows to '838:59:59'
SELECT TIME('2001:01:01');
--echo # This used to parse as DATE, now parses as TIME interval
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES
('2024:01:03 garbage /////'),
('24:01:03 garbage /////'),
('01:01:03 garbage /////'),
('2024:02:03'),
('100000:02:03'),
('24:02:03'),
('01:02:03'),
('01:02:03:'),
('01:02:03-'),
('01:02:03;'),
('01:02:03/'),
('20 10:20:30');
SELECT EXTRACT(DAY FROM a), EXTRACT(DAY_SECOND FROM a), a FROM t1;
DROP TABLE t1;
......@@ -367,9 +367,7 @@ extract(DAY_MINUTE FROM "02 10:11:12")
21011
select extract(DAY_SECOND FROM "225 10:11:12");
extract(DAY_SECOND FROM "225 10:11:12")
34225959
Warnings:
Warning 1292 Truncated incorrect time value: '225 10:11:12'
225101112
select extract(HOUR FROM "1999-01-02 10:11:12");
extract(HOUR FROM "1999-01-02 10:11:12")
10
......@@ -1033,9 +1031,7 @@ Note 1105 Cast to unsigned converted negative integer to it's positive complemen
Warning 1292 Truncated incorrect time value: '18446744073709551615:00:00'
SELECT EXTRACT(HOUR FROM '10000:02:03');
EXTRACT(HOUR FROM '10000:02:03')
22
Warnings:
Warning 1292 Truncated incorrect time value: '10000:02:03'
16
CREATE TABLE t1(f1 TIME);
INSERT IGNORE INTO t1 VALUES('916:00:00 a');
Warnings:
......
......@@ -17,10 +17,7 @@ Warnings:
Warning 1292 Incorrect datetime value: '0'
select extract(hour from '100000:02:03'), extract(hour from '100000:02:03 ');
extract(hour from '100000:02:03') extract(hour from '100000:02:03 ')
NULL NULL
Warnings:
Warning 1292 Incorrect time value: '100000:02:03'
Warning 1292 Incorrect time value: '100000:02:03 '
16 16
#
# backward compatibility craziness
#
......
......@@ -262,12 +262,25 @@ static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status,
static int check_time_range_internal(MYSQL_TIME *ltime,
ulong max_hour, uint dec,
int *warning);
ulong max_hour, ulong err_hour,
uint dec, int *warning);
int check_time_range(MYSQL_TIME *ltime, uint dec, int *warning)
{
return check_time_range_internal(ltime, TIME_MAX_HOUR, dec, warning);
return check_time_range_internal(ltime, TIME_MAX_HOUR, UINT_MAX32,
dec, warning);
}
static my_bool
set_neg(my_bool neg, MYSQL_TIME_STATUS *st, MYSQL_TIME *ltime)
{
if ((ltime->neg= neg) && ltime->time_type != MYSQL_TIMESTAMP_TIME)
{
st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
return TRUE;
}
return FALSE;
}
......@@ -351,20 +364,51 @@ static my_bool find_body(my_bool *neg, const char *str, size_t length,
}
typedef struct
{
uint count_punct;
uint count_colon;
uint count_iso_date_time_separator;
} MYSQL_TIME_USED_CHAR_STATISTICS;
static void
mysql_time_used_char_statistics_init(MYSQL_TIME_USED_CHAR_STATISTICS *to,
const char *str, const char *end)
{
const char *s;
bzero((void *) to, sizeof(MYSQL_TIME_USED_CHAR_STATISTICS));
for (s= str; s < end; s++)
{
if (my_ispunct(&my_charset_latin1, *s))
to->count_punct++;
if (*s == ':')
to->count_colon++;
if (*s == 'T')
to->count_iso_date_time_separator++;
}
}
static my_bool
is_datetime_body_candidate(const char *str, size_t length)
is_datetime_body_candidate(const char *str, size_t length,
my_bool allow_dates_delimited,
my_bool allow_dates_numeric)
{
static uint min_date_length= 5; /* '1-1-1' -> '0001-01-01' */
uint pos;
uint pos, count_punct= 0;
uint date_time_separator_length= MY_TEST(!allow_dates_delimited);
if (length >= 12)
return TRUE;
/*
The shortest possible DATE is '1-1-1', which is 5 characters.
To make a full datetime it should be at least followed by a space or a 'T'.
To make a date it should be just not less that 5 characters.
*/
if (length < min_date_length + 1/* DATE/TIME separator */)
if (length < min_date_length + date_time_separator_length &&
!allow_dates_numeric)
return FALSE;
for (pos= min_date_length; pos < length; pos++)
for (pos= 0; pos < length; pos++)
{
if (str[pos] == 'T') /* Date/time separator */
return TRUE;
......@@ -381,21 +425,23 @@ is_datetime_body_candidate(const char *str, size_t length)
TIME('111 11') -> 838:59:59 = 111 days 11 hours with overflow
TIME('1111 11') -> 838:59:59 = 1111 days 11 hours with overflow
*/
for (pos= 0 ; pos < min_date_length; pos++)
{
if (my_ispunct(&my_charset_latin1, str[pos])) /* Can be a DATE */
return TRUE;
}
return FALSE;
return count_punct > 0; /* Can be a DATE if already had separators*/
}
if (my_ispunct(&my_charset_latin1, str[pos]))
{
if (allow_dates_delimited && str[pos] != ':')
return TRUE;
count_punct++;
}
}
return FALSE;
return allow_dates_numeric && count_punct == 0;
}
static my_bool
str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
MYSQL_TIME *l_time, ulong max_hour,
MYSQL_TIME *l_time,
ulong max_hour, ulong err_hour,
MYSQL_TIME_STATUS *status,
const char **endptr);
......@@ -452,14 +498,18 @@ static my_bool
str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
ulonglong flags,
my_bool punct_is_date_time_separator,
MYSQL_TIME_STATUS *status)
MYSQL_TIME_STATUS *status,
uint *number_of_fields,
const char **endptr)
{
const char *end=str+length, *pos;
uint number_of_fields= 0, digits, year_length, not_zero_date;
uint digits, year_length, not_zero_date;
int warn= 0;
DBUG_ENTER("str_to_datetime_or_date_body");
DBUG_ASSERT(C_FLAGS_OK(flags));
bzero(l_time, sizeof(*l_time));
*number_of_fields= 0;
*endptr= str;
/*
Calculate number of digits in first part.
......@@ -487,40 +537,41 @@ str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
(only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]])
*/
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
if (get_digits(&l_time->year, &number_of_fields, &str, end, year_length)
|| get_digits(&l_time->month, &number_of_fields, &str, end, 2)
|| get_digits(&l_time->day, &number_of_fields, &str, end, 2)
if (get_digits(&l_time->year, number_of_fields, &str, end, year_length)
|| get_digits(&l_time->month, number_of_fields, &str, end, 2)
|| get_digits(&l_time->day, number_of_fields, &str, end, 2)
|| get_maybe_T(&str, end)
|| get_digits(&l_time->hour, &number_of_fields, &str, end, 2)
|| get_digits(&l_time->minute, &number_of_fields, &str, end, 2)
|| get_digits(&l_time->second, &number_of_fields, &str, end, 2))
|| get_digits(&l_time->hour, number_of_fields, &str, end, 2)
|| get_digits(&l_time->minute, number_of_fields, &str, end, 2)
|| get_digits(&l_time->second, number_of_fields, &str, end, 2))
warn|= MYSQL_TIME_WARN_TRUNCATED;
}
else
{
const char *start= str;
if (get_number(&l_time->year, &number_of_fields, &str, end))
if (get_number(&l_time->year, number_of_fields, &str, end))
warn|= MYSQL_TIME_WARN_TRUNCATED;
year_length= (uint)(str - start);
if (!warn &&
(get_punct(&str, end)
|| get_number(&l_time->month, &number_of_fields, &str, end)
|| get_number(&l_time->month, number_of_fields, &str, end)
|| get_punct(&str, end)
|| get_number(&l_time->day, &number_of_fields, &str, end)
|| get_date_time_separator(&number_of_fields,
|| get_number(&l_time->day, number_of_fields, &str, end)
|| get_date_time_separator(number_of_fields,
punct_is_date_time_separator, &str, end)
|| get_number(&l_time->hour, &number_of_fields, &str, end)
|| get_number(&l_time->hour, number_of_fields, &str, end)
|| get_punct(&str, end)
|| get_number(&l_time->minute, &number_of_fields, &str, end)
|| get_number(&l_time->minute, number_of_fields, &str, end)
|| get_punct(&str, end)
|| get_number(&l_time->second, &number_of_fields, &str, end)))
|| get_number(&l_time->second, number_of_fields, &str, end)))
warn|= MYSQL_TIME_WARN_TRUNCATED;
}
status->warnings|= warn;
*endptr= str;
/* we're ok if date part is correct. even if the rest is truncated */
if (number_of_fields < 3)
if (*number_of_fields < 3)
{
l_time->time_type= MYSQL_TIMESTAMP_NONE;
status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
......@@ -531,7 +582,8 @@ str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
{
str++;
get_microseconds(&l_time->second_part, status,
&number_of_fields, &str, end);
number_of_fields, &str, end);
*endptr= str;
}
not_zero_date = l_time->year || l_time->month || l_time->day ||
......@@ -551,7 +603,7 @@ str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
if (check_date(l_time, not_zero_date, flags, &status->warnings))
goto err;
l_time->time_type= (number_of_fields <= 3 ?
l_time->time_type= (*number_of_fields <= 3 ?
MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
if (str != end)
......@@ -594,27 +646,104 @@ str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
TRUE on error
*/
static
my_bool str_to_datetime_or_date_or_time_body(const char *str, size_t length,
MYSQL_TIME *l_time,
ulonglong fuzzydate,
MYSQL_TIME_STATUS *status)
static my_bool
str_to_datetime_or_date_or_time_body(const char *str, size_t length,
MYSQL_TIME *l_time,
ulonglong fuzzydate,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour,
my_bool allow_dates_delimited,
my_bool allow_dates_numeric)
{
const char *endptr;
DBUG_ASSERT(C_FLAGS_OK(fuzzydate));
/* Check first if this is a full TIMESTAMP */
if (is_datetime_body_candidate(str, length))
if (is_datetime_body_candidate(str, length,
allow_dates_delimited,
allow_dates_numeric))
{ /* Probably full timestamp */
int warn_copy= status->warnings; /* could already be set by find_body() */
(void) str_to_datetime_or_date_body(str, length, l_time,
fuzzydate, FALSE, status);
if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
uint number_of_fields;
(void) str_to_datetime_or_date_body(str, length, l_time, fuzzydate,
FALSE, status,
&number_of_fields, &endptr);
DBUG_ASSERT(endptr >= str);
DBUG_ASSERT(endptr <= str + length);
switch (l_time->time_type) {
case MYSQL_TIMESTAMP_DATETIME:
return FALSE;
case MYSQL_TIMESTAMP_DATE:
{
/*
Successfully parsed as DATE, but it can also be a TIME:
'24:02:03' - continue and parse as TIME
'24:02:03 garbage /////' - continue and parse as TIME
'24:02:03T' - return DATE
'24-02-03' - return DATE
'24/02/03' - return DATE
'11111' - return DATE
*/
MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
mysql_time_used_char_statistics_init(&used_chars, str, endptr);
if (used_chars.count_iso_date_time_separator || !used_chars.count_colon)
return FALSE;
}
break;
case MYSQL_TIMESTAMP_ERROR:
{
MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
/*
Check if it parsed as DATETIME but then failed as out of range:
'2011-02-32 8:46:06.23434' - return error
*/
if (number_of_fields > 3)
return TRUE;
/*
Check if it parsed as DATE but then failed as out of range:
'100000:02:03' - continue and parse as TIME
'100000:02:03T' - return error
'100000/02/03' - return error
'100000-02-03' - return error
*/
mysql_time_used_char_statistics_init(&used_chars, str, endptr);
if (used_chars.count_iso_date_time_separator || !used_chars.count_colon)
return TRUE;
}
break;
case MYSQL_TIMESTAMP_NONE:
{
if (allow_dates_numeric && endptr >= str + length)
{
/*
For backward compatibility this parses as DATE and fails:
EXTRACT(DAY FROM '1111') -- return error
EXTRACT(DAY FROM '1') -- return error
*/
MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
mysql_time_used_char_statistics_init(&used_chars, str, endptr);
if (!used_chars.count_iso_date_time_separator &&
!used_chars.count_colon &&
!used_chars.count_punct)
return TRUE;
}
/*
- '256 10:30:30' - continue and parse as TIME
- '4294967296:59:59.123456456' - continue and parse as TIME
*/
}
break;
case MYSQL_TIMESTAMP_TIME:
DBUG_ASSERT(0);
break;
}
my_time_status_init(status);
status->warnings= warn_copy;
}
if (!str_to_DDhhmmssff_internal(FALSE, str, length, l_time, TIME_MAX_HOUR,
if (!str_to_DDhhmmssff_internal(FALSE, str, length, l_time,
time_max_hour, time_err_hour,
status, &endptr))
return FALSE;
......@@ -640,10 +769,11 @@ my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *ltime,
return TRUE;
/* Reject anything that might be parsed as a full TIMESTAMP */
if (is_datetime_body_candidate(str, length))
if (is_datetime_body_candidate(str, length, FALSE, FALSE))
{
(void) str_to_datetime_or_date_body(str, length, ltime,
0, FALSE, status);
uint number_of_fields;
(void) str_to_datetime_or_date_body(str, length, ltime, 0, FALSE,
status, &number_of_fields, &endptr);
if (ltime->time_type > MYSQL_TIMESTAMP_ERROR)
{
status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
......@@ -659,7 +789,7 @@ my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *ltime,
will scan only '2001'.
*/
if (str_to_DDhhmmssff_internal(neg, str, length, ltime, max_hour,
status, &endptr) ||
UINT_MAX32, status, &endptr) ||
(endptr < str + length && endptr[0] == '-'))
return TRUE;
return FALSE;
......@@ -668,29 +798,63 @@ my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *ltime,
my_bool
str_to_datetime_or_date_or_time(const char *str, size_t length,
MYSQL_TIME *l_time,
ulonglong fuzzydate,
MYSQL_TIME_STATUS *status)
MYSQL_TIME *to, ulonglong mode,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour)
{
my_bool neg, rc;
DBUG_ASSERT(C_FLAGS_OK(fuzzydate));
my_bool neg;
DBUG_ASSERT(C_FLAGS_OK(mode));
my_time_status_init(status);
return
find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
time_max_hour, time_err_hour,
FALSE, FALSE) ||
set_neg(neg, status, to);
}
my_bool
str_to_datetime_or_date_or_interval_hhmmssff(const char *str, size_t length,
MYSQL_TIME *to, ulonglong mode,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour)
{
my_bool neg;
DBUG_ASSERT(C_FLAGS_OK(mode));
my_time_status_init(status);
return
find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
time_max_hour, time_err_hour,
TRUE, FALSE) ||
set_neg(neg, status, to);
}
my_bool
str_to_datetime_or_date_or_interval_day(const char *str, size_t length,
MYSQL_TIME *to, ulonglong mode,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour)
{
my_bool neg;
DBUG_ASSERT(C_FLAGS_OK(mode));
my_time_status_init(status);
if (find_body(&neg, str, length, l_time, &status->warnings, &str, &length))
return TRUE;
/*
QQ: Perhaps we should modify xxx_body() to return endptr.
If endptr points to '-', return an error.
For backward compatibility we allow to parse non-delimited
values as DATE rather than as TIME:
EXTRACT(DAY FROM '11111')
*/
rc= str_to_datetime_or_date_or_time_body(str, length, l_time,
fuzzydate, status);
if (rc)
return rc;
if ((l_time->neg= neg) && l_time->time_type != MYSQL_TIMESTAMP_TIME)
{
status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
return TRUE;
}
return FALSE;
return
find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
time_max_hour, time_err_hour,
TRUE, TRUE) ||
set_neg(neg, status, to);
}
......@@ -698,20 +862,16 @@ my_bool
str_to_datetime_or_date(const char *str, size_t length, MYSQL_TIME *l_time,
ulonglong flags, MYSQL_TIME_STATUS *status)
{
my_bool neg, rc;
my_bool neg;
uint number_of_fields;
const char *endptr;
DBUG_ASSERT(C_FLAGS_OK(flags));
my_time_status_init(status);
if (find_body(&neg, str, length, l_time, &status->warnings, &str, &length))
return TRUE;
rc= str_to_datetime_or_date_body(str, length, l_time, flags, TRUE, status);
if (rc)
return rc;
if ((l_time->neg= neg))
{
status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
return TRUE;
}
return FALSE;
return
find_body(&neg, str, length, l_time, &status->warnings, &str, &length) ||
str_to_datetime_or_date_body(str, length, l_time, flags, TRUE,
status, &number_of_fields, &endptr) ||
set_neg(neg, status, l_time);
}
......@@ -738,7 +898,8 @@ str_to_datetime_or_date(const char *str, size_t length, MYSQL_TIME *l_time,
*/
static my_bool
str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
MYSQL_TIME *l_time, ulong max_hour,
MYSQL_TIME *l_time,
ulong max_hour, ulong err_hour,
MYSQL_TIME_STATUS *status, const char **endptr)
{
ulong date[5];
......@@ -891,7 +1052,8 @@ str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
*endptr= str;
/* Check if the value is valid and fits into MYSQL_TIME range */
if (check_time_range_internal(l_time, max_hour, 6, &status->warnings))
if (check_time_range_internal(l_time, max_hour, err_hour,
6, &status->warnings))
return TRUE;
/* Check if there is garbage at end of the MYSQL_TIME specification */
......@@ -911,7 +1073,9 @@ str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
SYNOPSIS:
check_time_range_internal()
time pointer to MYSQL_TIME value
ulong max_hour - maximum allowed hour value
ulong max_hour - maximum allowed hour value. if the hour is greater,
cut the time value to 'max_hour:59:59.999999'
ulong err_hour - if hour is greater than this value, return an error
uint dec
warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
......@@ -926,13 +1090,15 @@ str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
*/
int check_time_range_internal(struct st_mysql_time *my_time,
ulong max_hour, uint dec, int *warning)
ulong max_hour, ulong err_hour,
uint dec, int *warning)
{
ulonglong hour;
static ulong max_sec_part[TIME_SECOND_PART_DIGITS+1]= {000000, 900000, 990000,
999000, 999900, 999990, 999999};
if (my_time->minute >= 60 || my_time->second >= 60)
if (my_time->minute >= 60 || my_time->second >= 60 ||
my_time->hour > err_hour)
{
*warning|= MYSQL_TIME_WARN_TRUNCATED;
return 1;
......@@ -1581,17 +1747,19 @@ longlong number_to_datetime_or_date(longlong nr, ulong sec_part,
-1 time value is invalid
*/
int number_to_time_only(my_bool neg, ulonglong nr, ulong sec_part,
MYSQL_TIME *ltime, int *was_cut)
ulong max_hour, MYSQL_TIME *ltime, int *was_cut)
{
static const ulonglong TIME_MAX_mmss= TIME_MAX_MINUTE*100 + TIME_MAX_SECOND;
ulonglong time_max_value= max_hour * 10000ULL + TIME_MAX_mmss;
*was_cut= 0;
ltime->year= ltime->month= ltime->day= 0;
ltime->time_type= MYSQL_TIMESTAMP_TIME;
ltime->neg= neg;
if (nr > TIME_MAX_VALUE)
if (nr > time_max_value)
{
nr= TIME_MAX_VALUE;
nr= time_max_value;
sec_part= TIME_MAX_SECOND_PART;
*was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
}
......
......@@ -2092,16 +2092,18 @@ void Item_extract::print(String *str, enum_query_type query_type)
bool Item_extract::fix_length_and_dec()
{
maybe_null=1; // If wrong date
uint32 daylen= args[0]->cmp_type() == TIME_RESULT ? 2 :
TIME_MAX_INTERVAL_DAY_CHAR_LENGTH;
switch (int_type) {
case INTERVAL_YEAR: set_date_length(4); break; // YYYY
case INTERVAL_YEAR_MONTH: set_date_length(6); break; // YYYYMM
case INTERVAL_QUARTER: set_date_length(2); break; // 1..4
case INTERVAL_MONTH: set_date_length(2); break; // MM
case INTERVAL_WEEK: set_date_length(2); break; // 0..52
case INTERVAL_DAY: set_day_length(2); break; // DD
case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh
case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm
case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss
case INTERVAL_DAY: set_day_length(daylen); break; // DD
case INTERVAL_DAY_HOUR: set_day_length(daylen+2); break; // DDhh
case INTERVAL_DAY_MINUTE: set_day_length(daylen+4); break; // DDhhmm
case INTERVAL_DAY_SECOND: set_day_length(daylen+6); break; // DDhhmmss
case INTERVAL_HOUR: set_time_length(2); break; // hh
case INTERVAL_HOUR_MINUTE: set_time_length(4); break; // hhmm
case INTERVAL_HOUR_SECOND: set_time_length(6); break; // hhmmss
......@@ -2109,7 +2111,7 @@ bool Item_extract::fix_length_and_dec()
case INTERVAL_MINUTE_SECOND: set_time_length(4); break; // mmss
case INTERVAL_SECOND: set_time_length(2); break; // ss
case INTERVAL_MICROSECOND: set_time_length(6); break; // ffffff
case INTERVAL_DAY_MICROSECOND: set_time_length(14); break; // DDhhmmssffffff
case INTERVAL_DAY_MICROSECOND: set_time_length(daylen+12); break; // DDhhmmssffffff
case INTERVAL_HOUR_MICROSECOND: set_time_length(12); break; // hhmmssffffff
case INTERVAL_MINUTE_MICROSECOND: set_time_length(10); break; // mmssffffff
case INTERVAL_SECOND_MICROSECOND: set_time_length(8); break; // ssffffff
......
......@@ -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= date_mode_t(0);
m_date_mode= TIME_INTERVAL_DAY;
}
void set_time_length(uint32 length)
{
set_handler(handler_by_length(max_length= length + 1/*sign*/, 11));
m_date_mode= TIME_TIME_ONLY;
m_date_mode= TIME_INTERVAL_hhmmssff;
}
public:
const interval_type int_type; // keep it public
......
......@@ -37,11 +37,13 @@ class date_mode_t
FUZZY_DATES is used for the result will only be used for comparison
purposes. Conversion is as relaxed as possible.
*/
FUZZY_DATES= 1U,
TIME_ONLY= 4U,
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
FUZZY_DATES= 1U,
TIME_ONLY= 4U,
INTERVAL_hhmmssff= 8U,
INTERVAL_DAY= 16U,
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
};
private:
......@@ -98,6 +100,8 @@ class date_mode_t
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);
......
......@@ -371,18 +371,14 @@ class TemporalAsciiBuffer: public LEX_CSTRING
};
/* Character set-aware version of str_to_datetime_or_date_or_time() */
bool Temporal::str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *status,
/* 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,
const char *str, size_t length,
CHARSET_INFO *cs,
date_mode_t fuzzydate)
{
TemporalAsciiBuffer tmp(str, length, cs);
bool rc= ::str_to_datetime_or_date_or_time(tmp.str, tmp.length, this,
ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(status->warnings || !rc);
return rc;
return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate);
}
......@@ -393,11 +389,17 @@ bool Temporal::str_to_datetime_or_date(MYSQL_TIME_STATUS *status,
date_mode_t flags)
{
TemporalAsciiBuffer tmp(str, length, cs);
bool rc= ::str_to_datetime_or_date(tmp.str, tmp.length, this,
ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(status->warnings || !rc);
return rc;
return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags);
}
/* Character set-aware version of ascii_to_temporal() */
bool Temporal::str_to_temporal(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);
}
......
......@@ -181,9 +181,7 @@ 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 (fuzzydate & TIME_TIME_ONLY ?
str_to_datetime_or_date_or_time(warn, str, length, cs, fuzzydate) :
str_to_datetime_or_date(warn, str, length, cs, fuzzydate))
if (str_to_temporal(warn, str, length, cs, fuzzydate))
make_fuzzy_date(&warn->warnings, fuzzydate);
if (warn->warnings)
warn->set_str(str, length, &my_charset_bin);
......@@ -232,8 +230,11 @@ void Sec6::make_truncated_warning(THD *thd, const char *type_str) const
bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime,
date_mode_t fuzzydate) const
{
bool is_time= bool(fuzzydate & TIME_TIME_ONLY);
bool rc= is_time ? to_time(ltime, warn) : to_datetime(ltime, fuzzydate, warn);
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);
DBUG_ASSERT(*warn || !rc);
if (truncated())
*warn|= MYSQL_TIME_WARN_TRUNCATED;
......
......@@ -264,21 +264,59 @@ class Sec6
MYSQL_TIME *ltime,
date_mode_t fuzzydate) const;
// Convert a number in format hhhmmss.ff to TIME'hhh:mm:ss.ff'
bool to_time(MYSQL_TIME *to, int *warn) const
protected:
bool to_interval_hhmmssff_only(MYSQL_TIME *to, int *warn) const
{
return number_to_time_only(m_neg, m_sec, m_usec,
TIME_MAX_INTERVAL_HOUR, to, warn);
}
bool to_datetime_or_to_interval_hhmmssff(MYSQL_TIME *to, int *warn) const
{
bool rc= (m_sec > 9999999 && m_sec <= 99991231235959ULL && !neg()) ?
number_to_datetime_or_date(m_sec, m_usec, to,
C_TIME_INVALID_DATES, warn) < 0 :
number_to_time_only(m_neg, m_sec, m_usec, to, warn);
/*
Convert a number to a time interval.
The following formats are understood:
- 0 <= x <= 999999995959 - parse as hhhhmmss
- 999999995959 < x <= 99991231235959 - parse as YYYYMMDDhhmmss
(YYMMDDhhmmss) (YYYYMMDDhhmmss)
Note, these formats are NOT understood:
- YYMMDD - overlaps with INTERVAL range
- YYYYMMDD - overlaps with INTERVAL range
- YYMMDDhhmmss - overlaps with INTERVAL range, partially
(see TIME_MAX_INTERVAL_HOUR)
If we ever need wider intervals, this code switching between
full datetime and interval-only should be rewised.
*/
DBUG_ASSERT(TIME_MAX_INTERVAL_HOUR <= 999999995959);
/* (YYMMDDhhmmss) */
if (m_sec > 999999995959ULL &&
m_sec <= 99991231235959ULL && m_neg == 0)
return to_datetime_or_date(to, warn, TIME_INVALID_DATES);
if (m_sec / 10000 > TIME_MAX_INTERVAL_HOUR)
{
*warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
return true;
}
return to_interval_hhmmssff_only(to, warn);
}
public:
// [-][DD]hhhmmss.ff, YYMMDDhhmmss.ff, YYYYMMDDhhmmss.ff
bool to_datetime_or_time(MYSQL_TIME *to, int *warn, date_mode_t mode) const
{
bool rc= m_sec > 9999999 && m_sec <= 99991231235959ULL && !m_neg ?
::number_to_datetime_or_date(m_sec, m_usec, to,
ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE), warn) < 0 :
::number_to_time_only(m_neg, m_sec, m_usec, TIME_MAX_HOUR, to, warn);
DBUG_ASSERT(*warn || !rc);
return rc;
}
/*
Convert a number in format YYYYMMDDhhmmss.ff to
Convert a number in formats YYYYMMDDhhmmss.ff or YYMMDDhhmmss.ff to
TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff'
*/
bool to_datetime(MYSQL_TIME *to, date_mode_t flags, int *warn) const
bool to_datetime_or_date(MYSQL_TIME *to, int *warn, date_mode_t flags) const
{
if (m_neg)
{
......@@ -458,6 +496,8 @@ class Temporal: protected MYSQL_TIME
timestamp_type tstype, const char *name)
{
const char *typestr= tstype >= 0 ? type_name_by_timestamp_type(tstype) :
mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ?
"interval" :
mode & TIME_TIME_ONLY ? "time" : "datetime";
Temporal::push_conversion_warnings(thd, totally_useless_value, warnings, typestr,
name, ptr());
......@@ -579,6 +619,60 @@ class Temporal: protected MYSQL_TIME
if (warn->warnings)
warn->set_decimal(nr);
}
bool ascii_to_temporal(MYSQL_TIME_STATUS *st,
const char *str, size_t length,
date_mode_t mode)
{
if (mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY))
return ascii_to_datetime_or_date_or_interval_DDhhmmssff(st, str, length,
mode);
if (mode & TIME_TIME_ONLY)
return ascii_to_datetime_or_date_or_time(st, str, length, mode);
return ascii_to_datetime_or_date(st, str, length, mode);
}
bool ascii_to_datetime_or_date_or_interval_DDhhmmssff(MYSQL_TIME_STATUS *st,
const char *str,
size_t length,
date_mode_t mode)
{
longlong cflags= ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE);
bool rc= mode & TIME_INTERVAL_DAY ?
::str_to_datetime_or_date_or_interval_day(str, length, this, cflags, st,
TIME_MAX_INTERVAL_HOUR,
TIME_MAX_INTERVAL_HOUR) :
::str_to_datetime_or_date_or_interval_hhmmssff(str, length, this,
cflags, st,
TIME_MAX_INTERVAL_HOUR,
TIME_MAX_INTERVAL_HOUR);
DBUG_ASSERT(!rc || st->warnings);
return rc;
}
bool ascii_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *status,
const char *str, size_t length,
date_mode_t fuzzydate)
{
ulonglong cflags= ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE);
bool rc= ::str_to_datetime_or_date_or_time(str, length, this,
cflags, status,
TIME_MAX_HOUR, UINT_MAX32);
DBUG_ASSERT(!rc || status->warnings);
return rc;
}
bool ascii_to_datetime_or_date(MYSQL_TIME_STATUS *status,
const char *str, size_t length,
date_mode_t fuzzydate)
{
DBUG_ASSERT(bool(fuzzydate & TIME_TIME_ONLY) == false);
bool rc= ::str_to_datetime_or_date(str, length, this,
ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(!rc || status->warnings);
return rc;
}
// Character set aware versions for string conversion routines
bool str_to_temporal(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,
const char *str, size_t length,
CHARSET_INFO *cs, date_mode_t fuzzydate);
......@@ -858,7 +952,7 @@ class Interval_DDhhmmssff: public Temporal
*/
static uint max_useful_hour()
{
return 87649415;
return TIME_MAX_INTERVAL_HOUR;
}
public:
Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings,
......@@ -1120,7 +1214,7 @@ class Time: public Temporal
}
Time(THD *thd, int *warn, const Sec6 &nr, const Options opt)
{
if (nr.to_time(this, warn))
if (nr.to_datetime_or_time(this, warn, TIME_INVALID_DATES))
time_type= MYSQL_TIMESTAMP_NONE;
xxx_to_time_result_to_valid_value(thd, warn, opt);
}
......@@ -1305,7 +1399,7 @@ class Temporal_with_date: public Temporal
Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags)
{
DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false);
if (nr.to_datetime(this, flags, warn))
if (nr.to_datetime_or_date(this, warn, flags))
time_type= MYSQL_TIMESTAMP_NONE;
}
Temporal_with_date(MYSQL_TIME_STATUS *status,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment