diff options
-rw-r--r-- | client/mysqlbinlog.cc | 7 | ||||
-rw-r--r-- | include/my_time.h | 27 | ||||
-rw-r--r-- | mysql-test/r/cast.result | 20 | ||||
-rw-r--r-- | mysql-test/r/partition_innodb.result | 4 | ||||
-rw-r--r-- | mysql-test/r/temporal_literal.result | 418 | ||||
-rw-r--r-- | mysql-test/t/cast.test | 10 | ||||
-rw-r--r-- | mysql-test/t/partition_innodb.test | 4 | ||||
-rw-r--r-- | mysql-test/t/temporal_literal.test | 217 | ||||
-rw-r--r-- | sql-common/my_time.c | 225 | ||||
-rw-r--r-- | sql/field.cc | 43 | ||||
-rw-r--r-- | sql/item.cc | 93 | ||||
-rw-r--r-- | sql/item.h | 113 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 22 | ||||
-rw-r--r-- | sql/item_create.cc | 65 | ||||
-rw-r--r-- | sql/item_create.h | 5 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 2 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 13 | ||||
-rw-r--r-- | sql/item_timefunc.h | 1 | ||||
-rw-r--r-- | sql/sql_select.cc | 1 | ||||
-rw-r--r-- | sql/sql_time.cc | 48 | ||||
-rw-r--r-- | sql/sql_time.h | 36 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 79 | ||||
-rw-r--r-- | storage/spider/spd_db_mysql.cc | 20 |
23 files changed, 1253 insertions, 220 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 58785c48781..9ad284646e0 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1542,13 +1542,14 @@ the mysql command line client.\n\n"); static my_time_t convert_str_to_timestamp(const char* str) { - int was_cut; + MYSQL_TIME_STATUS status; MYSQL_TIME l_time; long dummy_my_timezone; uint dummy_in_dst_time_gap; + /* We require a total specification (date AND time) */ - if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &was_cut) != - MYSQL_TIMESTAMP_DATETIME || was_cut) + if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &status) || + l_time.time_type != MYSQL_TIMESTAMP_DATETIME || status.warnings) { error("Incorrect date and time argument: %s", str); exit(1); diff --git a/include/my_time.h b/include/my_time.h index d8a8d0b5ed3..b0539939d8d 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -78,14 +78,27 @@ extern uchar days_in_month[]; #define TIME_MAX_VALUE_SECONDS (TIME_MAX_HOUR * 3600L + \ TIME_MAX_MINUTE * 60L + TIME_MAX_SECOND) +/* + Structure to return status from + str_to_datetime(), str_to_time(). +*/ +typedef struct st_mysql_time_status +{ + int warnings; + uint precision; +} MYSQL_TIME_STATUS; + +static inline void my_time_status_init(MYSQL_TIME_STATUS *status) +{ + status->warnings= status->precision= 0; +} + my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, ulonglong flags, int *was_cut); -enum enum_mysql_timestamp_type -str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - ulonglong flag, int *warning); -enum enum_mysql_timestamp_type -str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, - ulonglong flags, int *was_cut); +my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, + ulonglong flag, MYSQL_TIME_STATUS *status); +my_bool str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, + ulonglong flags, MYSQL_TIME_STATUS *status); longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res, ulonglong flags, int *was_cut); @@ -107,7 +120,7 @@ ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *); ulonglong TIME_to_ulonglong(const MYSQL_TIME *); double TIME_to_double(const MYSQL_TIME *my_time); -longlong pack_time(MYSQL_TIME *my_time); +longlong pack_time(const MYSQL_TIME *my_time); MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time); int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning); diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 625791a7c4b..ac7be28d736 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -180,24 +180,24 @@ select 10.0+'10'; select 10E+0+'10'; 10E+0+'10' 20 -select CONVERT(DATE "2004-01-22 21:45:33" USING latin1); -CONVERT(DATE "2004-01-22 21:45:33" USING latin1) +select CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1); +CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1) 2004-01-22 21:45:33 -select CONVERT(DATE "2004-01-22 21:45:33",CHAR); -CONVERT(DATE "2004-01-22 21:45:33",CHAR) +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR); +CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR) 2004-01-22 21:45:33 -select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4)); -CONVERT(DATE "2004-01-22 21:45:33",CHAR(4)) +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4)); +CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4)) 2004 Warnings: Warning 1292 Truncated incorrect CHAR(4) value: '2004-01-22 21:45:33' -select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4)); -CONVERT(DATE "2004-01-22 21:45:33",BINARY(4)) +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4)); +CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4)) 2004 Warnings: Warning 1292 Truncated incorrect BINARY(4) value: '2004-01-22 21:45:33' -select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)); -CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)) +select CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4)); +CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4)) 2004 Warnings: Warning 1292 Truncated incorrect BINARY(4) value: '2004-01-22 21:45:33' diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index adfe3287459..a1accfa8e3d 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -51,8 +51,8 @@ p200912 0 p201103 1 p201912 0 SELECT count(*) FROM t1 p where c3 in -(select c3 from t1 t where t.c3 < date '2011-04-26 19:19:44' - and t.c3 > date '2011-04-26 19:18:44') ; +(select c3 from t1 t where t.c3 < timestamp '2011-04-26 19:19:44' + and t.c3 > timestamp '2011-04-26 19:18:44') ; count(*) 0 DROP TABLE t1; diff --git a/mysql-test/r/temporal_literal.result b/mysql-test/r/temporal_literal.result new file mode 100644 index 00000000000..c79ed311d16 --- /dev/null +++ b/mysql-test/r/temporal_literal.result @@ -0,0 +1,418 @@ +DROP TABLE IF EXISTS t1, t2; +SET NAMES latin1; +# +# Testing DATE literals +# +SELECT DATE'xxxx'; +ERROR HY000: Incorrect DATE value: 'xxxx' +SELECT DATE'01'; +ERROR HY000: Incorrect DATE value: '01' +SELECT DATE'01-01'; +ERROR HY000: Incorrect DATE value: '01-01' +SELECT DATE'2001'; +ERROR HY000: Incorrect DATE value: '2001' +SELECT DATE'2001-01'; +ERROR HY000: Incorrect DATE value: '2001-01' +SELECT DATE'2001-00-00'; +DATE'2001-00-00' +2001-00-00 +SELECT DATE'2001-01-00'; +DATE'2001-01-00' +2001-01-00 +SELECT DATE'0000-00-00'; +DATE'0000-00-00' +0000-00-00 +SELECT DATE'2001-01-01 00:00:00'; +ERROR HY000: Incorrect DATE value: '2001-01-01 00:00:00' +SELECT DATE'01:01:01'; +DATE'01:01:01' +2001-01-01 +SELECT DATE'01-01-01'; +DATE'01-01-01' +2001-01-01 +SELECT DATE'2010-01-01'; +DATE'2010-01-01' +2010-01-01 +SELECT DATE '2010-01-01'; +DATE '2010-01-01' +2010-01-01 +CREATE TABLE t1 AS SELECT DATE'2010-01-01'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `DATE'2010-01-01'` date NOT NULL DEFAULT '0000-00-00' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT +{d'2001-01-01'}, +{ d '2001-01-01' }, +{d'2001-01-01 10:10:10'}; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `{d'2001-01-01'}` date NOT NULL DEFAULT '0000-00-00', + `{ d '2001-01-01' }` date NOT NULL DEFAULT '0000-00-00', + `2001-01-01 10:10:10` varchar(19) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {d'2010-01-01'}; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select DATE'2010-01-01' AS `{d'2010-01-01'}` +EXPLAIN EXTENDED SELECT DATE'2010-01-01'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select DATE'2010-01-01' AS `DATE'2010-01-01'` +# +# Testing DATE literals in non-default sql_mode +# +SET sql_mode=no_zero_in_date; +SELECT DATE'2001-00-00'; +ERROR HY000: Incorrect DATE value: '2001-00-00' +SELECT DATE'2001-01-00'; +ERROR HY000: Incorrect DATE value: '2001-01-00' +SELECT DATE'0000-00-00'; +DATE'0000-00-00' +0000-00-00 +SET sql_mode=no_zero_date; +SELECT DATE'0000-00-00'; +ERROR HY000: Incorrect DATE value: '0000-00-00' +SET sql_mode=default; +# +# Testing TIME literals +# +SELECT TIME'xxxx'; +ERROR HY000: Incorrect TIME value: 'xxxx' +SELECT TIME'900:00:00'; +ERROR HY000: Incorrect TIME value: '900:00:00' +SELECT TIME'-900:00:00'; +ERROR HY000: Incorrect TIME value: '-900:00:00' +SELECT TIME'1 24:00:00'; +TIME'1 24:00:00' +48:00:00 +SELECT TIME'30 24:00:00'; +TIME'30 24:00:00' +744:00:00 +SELECT TIME'0000-00-00 00:00:00'; +ERROR HY000: Incorrect TIME value: '0000-00-00 00:00:00' +SELECT TIME'40 24:00:00'; +ERROR HY000: Incorrect TIME value: '40 24:00:00' +SELECT TIME'10'; +TIME'10' +00:00:10 +SELECT TIME'10:10'; +TIME'10:10' +10:10:00 +SELECT TIME'10:11.12'; +TIME'10:11.12' +10:11:00.12 +SELECT TIME'10:10:10'; +TIME'10:10:10' +10:10:10 +SELECT TIME'10:10:10.'; +TIME'10:10:10.' +10:10:10 +SELECT TIME'10:10:10.1'; +TIME'10:10:10.1' +10:10:10.1 +SELECT TIME'10:10:10.12'; +TIME'10:10:10.12' +10:10:10.12 +SELECT TIME'10:10:10.123'; +TIME'10:10:10.123' +10:10:10.123 +SELECT TIME'10:10:10.1234'; +TIME'10:10:10.1234' +10:10:10.1234 +SELECT TIME'10:10:10.12345'; +TIME'10:10:10.12345' +10:10:10.12345 +SELECT TIME'10:10:10.123456'; +TIME'10:10:10.123456' +10:10:10.123456 +SELECT TIME'-10:00:00'; +TIME'-10:00:00' +-10:00:00 +SELECT TIME '10:11:12'; +TIME '10:11:12' +10:11:12 +CREATE TABLE t1 AS SELECT +TIME'10:10:10', +TIME'10:10:10.', +TIME'10:10:10.1', +TIME'10:10:10.12', +TIME'10:10:10.123', +TIME'10:10:10.1234', +TIME'10:10:10.12345', +TIME'10:10:10.123456'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `TIME'10:10:10'` time NOT NULL DEFAULT '00:00:00', + `TIME'10:10:10.'` time NOT NULL DEFAULT '00:00:00', + `TIME'10:10:10.1'` time(1) NOT NULL DEFAULT '00:00:00.0', + `TIME'10:10:10.12'` time(2) NOT NULL DEFAULT '00:00:00.00', + `TIME'10:10:10.123'` time(3) NOT NULL DEFAULT '00:00:00.000', + `TIME'10:10:10.1234'` time(4) NOT NULL DEFAULT '00:00:00.0000', + `TIME'10:10:10.12345'` time(5) NOT NULL DEFAULT '00:00:00.00000', + `TIME'10:10:10.123456'` time(6) NOT NULL DEFAULT '00:00:00.000000' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT +{t'10:10:10'}, +{ t '10:10:10' }, +{t'10:10:10.'}, +{t'10:10:10.123456'}, +{t'2001-01-01'}; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `{t'10:10:10'}` time NOT NULL DEFAULT '00:00:00', + `{ t '10:10:10' }` time NOT NULL DEFAULT '00:00:00', + `{t'10:10:10.'}` time NOT NULL DEFAULT '00:00:00', + `{t'10:10:10.123456'}` time(6) NOT NULL DEFAULT '00:00:00.000000', + `2001-01-01` varchar(10) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {t'10:01:01'}; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select TIME'10:01:01' AS `{t'10:01:01'}` +EXPLAIN EXTENDED SELECT TIME'10:01:01'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select TIME'10:01:01' AS `TIME'10:01:01'` +# +# Testing TIMESTAMP literals +# +SELECT TIMESTAMP'xxxx'; +ERROR HY000: Incorrect DATETIME value: 'xxxx' +SELECT TIMESTAMP'2010'; +ERROR HY000: Incorrect DATETIME value: '2010' +SELECT TIMESTAMP'2010-01'; +ERROR HY000: Incorrect DATETIME value: '2010-01' +SELECT TIMESTAMP'2010-01-01'; +ERROR HY000: Incorrect DATETIME value: '2010-01-01' +SELECT TIMESTAMP'2010-01-01 00'; +TIMESTAMP'2010-01-01 00' +2010-01-01 00:00:00 +SELECT TIMESTAMP'2010-01-01 00:01'; +TIMESTAMP'2010-01-01 00:01' +2010-01-01 00:01:00 +SELECT TIMESTAMP'2010-01-01 10:10:10'; +TIMESTAMP'2010-01-01 10:10:10' +2010-01-01 10:10:10 +SELECT TIMESTAMP'2010-01-01 10:10:10.'; +TIMESTAMP'2010-01-01 10:10:10.' +2010-01-01 10:10:10 +SELECT TIMESTAMP'2010-01-01 10:10:10.1'; +TIMESTAMP'2010-01-01 10:10:10.1' +2010-01-01 10:10:10.1 +SELECT TIMESTAMP'2010-01-01 10:10:10.12'; +TIMESTAMP'2010-01-01 10:10:10.12' +2010-01-01 10:10:10.12 +SELECT TIMESTAMP'2010-01-01 10:10:10.123'; +TIMESTAMP'2010-01-01 10:10:10.123' +2010-01-01 10:10:10.123 +SELECT TIMESTAMP'2010-01-01 10:10:10.1234'; +TIMESTAMP'2010-01-01 10:10:10.1234' +2010-01-01 10:10:10.1234 +SELECT TIMESTAMP'2010-01-01 10:10:10.12345'; +TIMESTAMP'2010-01-01 10:10:10.12345' +2010-01-01 10:10:10.12345 +SELECT TIMESTAMP'2010-01-01 10:10:10.123456'; +TIMESTAMP'2010-01-01 10:10:10.123456' +2010-01-01 10:10:10.123456 +SELECT TIMESTAMP '2010-01-01 10:20:30'; +TIMESTAMP '2010-01-01 10:20:30' +2010-01-01 10:20:30 +CREATE TABLE t1 AS SELECT +TIMESTAMP'2010-01-01 10:10:10', +TIMESTAMP'2010-01-01 10:10:10.', +TIMESTAMP'2010-01-01 10:10:10.1', +TIMESTAMP'2010-01-01 10:10:10.12', +TIMESTAMP'2010-01-01 10:10:10.123', +TIMESTAMP'2010-01-01 10:10:10.1234', +TIMESTAMP'2010-01-01 10:10:10.12345', +TIMESTAMP'2010-01-01 10:10:10.123456'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `TIMESTAMP'2010-01-01 10:10:10'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `TIMESTAMP'2010-01-01 10:10:10.'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `TIMESTAMP'2010-01-01 10:10:10.1'` datetime(1) NOT NULL DEFAULT '0000-00-00 00:00:00.0', + `TIMESTAMP'2010-01-01 10:10:10.12'` datetime(2) NOT NULL DEFAULT '0000-00-00 00:00:00.00', + `TIMESTAMP'2010-01-01 10:10:10.123'` datetime(3) NOT NULL DEFAULT '0000-00-00 00:00:00.000', + `TIMESTAMP'2010-01-01 10:10:10.1234'` datetime(4) NOT NULL DEFAULT '0000-00-00 00:00:00.0000', + `TIMESTAMP'2010-01-01 10:10:10.12345'` datetime(5) NOT NULL DEFAULT '0000-00-00 00:00:00.00000', + `TIMESTAMP'2010-01-01 10:10:10.123456'` datetime(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT +{ts'2001-01-01 10:10:10'}, +{ ts '2001-01-01 10:10:10' }, +{ts'2001-01-01 10:10:10.'}, +{ts'2001-01-01 10:10:10.123456'}, +{ts'2001-01-01'}; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `{ts'2001-01-01 10:10:10'}` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `{ ts '2001-01-01 10:10:10' }` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `{ts'2001-01-01 10:10:10.'}` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `{ts'2001-01-01 10:10:10.123456'}` datetime(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000', + `2001-01-01` varchar(10) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {ts'2010-01-01 10:10:10'}; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select TIMESTAMP'2010-01-01 10:10:10' AS `{ts'2010-01-01 10:10:10'}` +EXPLAIN EXTENDED SELECT TIMESTAMP'2010-01-01 10:10:10'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select TIMESTAMP'2010-01-01 10:10:10' AS `TIMESTAMP'2010-01-01 10:10:10'` +# +# Testing nanosecond rounding for TIMESTAMP literals with bad dates +# +SELECT TIMESTAMP'2001-00-00 00:00:00.999999'; +TIMESTAMP'2001-00-00 00:00:00.999999' +2001-00-00 00:00:00.999999 +SELECT TIMESTAMP'2001-00-01 00:00:00.999999'; +TIMESTAMP'2001-00-01 00:00:00.999999' +2001-00-01 00:00:00.999999 +SELECT TIMESTAMP'2001-01-00 00:00:00.999999'; +TIMESTAMP'2001-01-00 00:00:00.999999' +2001-01-00 00:00:00.999999 +SELECT TIMESTAMP'2001-00-00 00:00:00.9999999'; +ERROR HY000: Incorrect DATETIME value: '2001-00-00 00:00:00.9999999' +SELECT TIMESTAMP'2001-00-01 00:00:00.9999999'; +ERROR HY000: Incorrect DATETIME value: '2001-00-01 00:00:00.9999999' +SELECT TIMESTAMP'2001-01-00 00:00:00.9999999'; +ERROR HY000: Incorrect DATETIME value: '2001-01-00 00:00:00.9999999' +# +# String literal with bad dates and nanoseconds to DATETIME(N) +# +CREATE TABLE t1 (a DATETIME(6)); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +SELECT * FROM t1; +a +2001-00-00 00:00:00.999999 +2001-00-01 00:00:00.999999 +2001-01-00 00:00:00.999999 +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME(5)); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +SELECT * FROM t1; +a +2001-00-00 00:00:00.99999 +2001-00-01 00:00:00.99999 +2001-01-00 00:00:00.99999 +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +Warnings: +Warning 1265 Data truncated for column 'a' at row 1 +SELECT * FROM t1; +a +2001-00-00 00:00:00 +2001-00-01 00:00:00 +2001-01-00 00:00:00 +DROP TABLE t1; +# +# Testing Item_date_literal::eq +# +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES ('2001-01-01'),('2003-01-01'); +SELECT * FROM t1 WHERE a BETWEEN DATE'2001-01-01' AND DATE'2002-01-01'; +a +2001-01-01 +SELECT DATE'2001-01-01' FROM t1 GROUP BY DATE'2001-01-01'; +DATE'2001-01-01' +2001-01-01 +DROP TABLE t1; +# +# TIME literals in no-zero date context +# +SELECT TO_DAYS(TIME'00:00:00'); +TO_DAYS(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT TO_SECONDS(TIME'00:00:00'); +TO_SECONDS(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT DAYOFYEAR(TIME'00:00:00'); +DAYOFYEAR(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT WEEK(TIME'00:00:00'); +WEEK(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT YEARWEEK(TIME'00:00:00'); +YEARWEEK(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT WEEKDAY(TIME'00:00:00'); +WEEKDAY(TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT CONVERT_TZ(TIME'00:00:00','+00:00','+01:00'); +CONVERT_TZ(TIME'00:00:00','+00:00','+01:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR); +DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR) +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +SELECT TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00'); +TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00') +NULL +Warnings: +Warning 1292 Incorrect datetime value: '00:00:00' +# +# Testing Item_func::fix_fields() +# +SELECT 'a' LIKE 'a' ESCAPE DATE'2001-01-01'; +ERROR HY000: Incorrect arguments to ESCAPE +SELECT 'a' LIKE 'a' ESCAPE TIMESTAMP'2001-01-01 00:00:00'; +ERROR HY000: Incorrect arguments to ESCAPE +SELECT 'a' LIKE 'a' ESCAPE TIME'00:00:00'; +ERROR HY000: Incorrect arguments to ESCAPE diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test index 64e8225530b..9cbc941402e 100644 --- a/mysql-test/t/cast.test +++ b/mysql-test/t/cast.test @@ -74,11 +74,11 @@ select 10E+0+'10'; # The following cast creates warnings -select CONVERT(DATE "2004-01-22 21:45:33" USING latin1); -select CONVERT(DATE "2004-01-22 21:45:33",CHAR); -select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4)); -select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4)); -select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4)); +select CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1); +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR); +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4)); +select CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4)); +select CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4)); select CAST(0xb3 as signed); select CAST(0x8fffffffffffffff as signed); select CAST(0xffffffffffffffff as unsigned); diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test index a743ad45568..ea8faec0d51 100644 --- a/mysql-test/t/partition_innodb.test +++ b/mysql-test/t/partition_innodb.test @@ -60,8 +60,8 @@ insert into t1(c2,c3) values ("Test row",'2010-01-01 00:00:00'); SELECT PARTITION_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't1' AND TABLE_SCHEMA = 'test'; SELECT count(*) FROM t1 p where c3 in -(select c3 from t1 t where t.c3 < date '2011-04-26 19:19:44' - and t.c3 > date '2011-04-26 19:18:44') ; +(select c3 from t1 t where t.c3 < timestamp '2011-04-26 19:19:44' + and t.c3 > timestamp '2011-04-26 19:18:44') ; DROP TABLE t1; diff --git a/mysql-test/t/temporal_literal.test b/mysql-test/t/temporal_literal.test new file mode 100644 index 00000000000..5d67f64a6f5 --- /dev/null +++ b/mysql-test/t/temporal_literal.test @@ -0,0 +1,217 @@ +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +SET NAMES latin1; + + +--echo # +--echo # Testing DATE literals +--echo # +--error ER_WRONG_VALUE +SELECT DATE'xxxx'; +--error ER_WRONG_VALUE +SELECT DATE'01'; +--error ER_WRONG_VALUE +SELECT DATE'01-01'; +--error ER_WRONG_VALUE +SELECT DATE'2001'; +--error ER_WRONG_VALUE +SELECT DATE'2001-01'; +SELECT DATE'2001-00-00'; +SELECT DATE'2001-01-00'; +SELECT DATE'0000-00-00'; +--error ER_WRONG_VALUE +SELECT DATE'2001-01-01 00:00:00'; +SELECT DATE'01:01:01'; +SELECT DATE'01-01-01'; +SELECT DATE'2010-01-01'; +SELECT DATE '2010-01-01'; +CREATE TABLE t1 AS SELECT DATE'2010-01-01'; +SHOW CREATE TABLE t1; +DROP TABLE t1; +CREATE TABLE t1 AS SELECT + {d'2001-01-01'}, + { d '2001-01-01' }, + {d'2001-01-01 10:10:10'}; +SHOW CREATE TABLE t1; +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {d'2010-01-01'}; +EXPLAIN EXTENDED SELECT DATE'2010-01-01'; + +--echo # +--echo # Testing DATE literals in non-default sql_mode +--echo # +SET sql_mode=no_zero_in_date; +--error ER_WRONG_VALUE +SELECT DATE'2001-00-00'; +--error ER_WRONG_VALUE +SELECT DATE'2001-01-00'; +SELECT DATE'0000-00-00'; + +SET sql_mode=no_zero_date; +--error ER_WRONG_VALUE +SELECT DATE'0000-00-00'; +SET sql_mode=default; + +--echo # +--echo # Testing TIME literals +--echo # +--error ER_WRONG_VALUE +SELECT TIME'xxxx'; +--error ER_WRONG_VALUE +SELECT TIME'900:00:00'; +--error ER_WRONG_VALUE +SELECT TIME'-900:00:00'; +SELECT TIME'1 24:00:00'; +SELECT TIME'30 24:00:00'; +--error ER_WRONG_VALUE +SELECT TIME'0000-00-00 00:00:00'; +--error ER_WRONG_VALUE +SELECT TIME'40 24:00:00'; +SELECT TIME'10'; +SELECT TIME'10:10'; +SELECT TIME'10:11.12'; +SELECT TIME'10:10:10'; +SELECT TIME'10:10:10.'; +SELECT TIME'10:10:10.1'; +SELECT TIME'10:10:10.12'; +SELECT TIME'10:10:10.123'; +SELECT TIME'10:10:10.1234'; +SELECT TIME'10:10:10.12345'; +SELECT TIME'10:10:10.123456'; +SELECT TIME'-10:00:00'; +SELECT TIME '10:11:12'; +CREATE TABLE t1 AS SELECT + TIME'10:10:10', + TIME'10:10:10.', + TIME'10:10:10.1', + TIME'10:10:10.12', + TIME'10:10:10.123', + TIME'10:10:10.1234', + TIME'10:10:10.12345', + TIME'10:10:10.123456'; +SHOW CREATE TABLE t1; +DROP TABLE t1; +CREATE TABLE t1 AS SELECT + {t'10:10:10'}, + { t '10:10:10' }, + {t'10:10:10.'}, + {t'10:10:10.123456'}, + {t'2001-01-01'}; +SHOW CREATE TABLE t1; +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {t'10:01:01'}; +EXPLAIN EXTENDED SELECT TIME'10:01:01'; + + +--echo # +--echo # Testing TIMESTAMP literals +--echo # +--error ER_WRONG_VALUE +SELECT TIMESTAMP'xxxx'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2010'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2010-01'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2010-01-01'; +SELECT TIMESTAMP'2010-01-01 00'; +SELECT TIMESTAMP'2010-01-01 00:01'; +SELECT TIMESTAMP'2010-01-01 10:10:10'; +SELECT TIMESTAMP'2010-01-01 10:10:10.'; +SELECT TIMESTAMP'2010-01-01 10:10:10.1'; +SELECT TIMESTAMP'2010-01-01 10:10:10.12'; +SELECT TIMESTAMP'2010-01-01 10:10:10.123'; +SELECT TIMESTAMP'2010-01-01 10:10:10.1234'; +SELECT TIMESTAMP'2010-01-01 10:10:10.12345'; +SELECT TIMESTAMP'2010-01-01 10:10:10.123456'; +SELECT TIMESTAMP '2010-01-01 10:20:30'; +CREATE TABLE t1 AS SELECT + TIMESTAMP'2010-01-01 10:10:10', + TIMESTAMP'2010-01-01 10:10:10.', + TIMESTAMP'2010-01-01 10:10:10.1', + TIMESTAMP'2010-01-01 10:10:10.12', + TIMESTAMP'2010-01-01 10:10:10.123', + TIMESTAMP'2010-01-01 10:10:10.1234', + TIMESTAMP'2010-01-01 10:10:10.12345', + TIMESTAMP'2010-01-01 10:10:10.123456'; +SHOW CREATE TABLE t1; +DROP TABLE t1; +CREATE TABLE t1 AS SELECT + {ts'2001-01-01 10:10:10'}, + { ts '2001-01-01 10:10:10' }, + {ts'2001-01-01 10:10:10.'}, + {ts'2001-01-01 10:10:10.123456'}, + {ts'2001-01-01'}; +SHOW CREATE TABLE t1; +DROP TABLE t1; +EXPLAIN EXTENDED SELECT {ts'2010-01-01 10:10:10'}; +EXPLAIN EXTENDED SELECT TIMESTAMP'2010-01-01 10:10:10'; + +--echo # +--echo # Testing nanosecond rounding for TIMESTAMP literals with bad dates +--echo # +SELECT TIMESTAMP'2001-00-00 00:00:00.999999'; +SELECT TIMESTAMP'2001-00-01 00:00:00.999999'; +SELECT TIMESTAMP'2001-01-00 00:00:00.999999'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2001-00-00 00:00:00.9999999'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2001-00-01 00:00:00.9999999'; +--error ER_WRONG_VALUE +SELECT TIMESTAMP'2001-01-00 00:00:00.9999999'; + +--echo # +--echo # String literal with bad dates and nanoseconds to DATETIME(N) +--echo # +CREATE TABLE t1 (a DATETIME(6)); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +SELECT * FROM t1; +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME(5)); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +SELECT * FROM t1; +DROP TABLE t1; +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999'); +INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999'); +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Testing Item_date_literal::eq +--echo # +CREATE TABLE t1 (a DATE); +INSERT INTO t1 VALUES ('2001-01-01'),('2003-01-01'); +SELECT * FROM t1 WHERE a BETWEEN DATE'2001-01-01' AND DATE'2002-01-01'; +SELECT DATE'2001-01-01' FROM t1 GROUP BY DATE'2001-01-01'; +DROP TABLE t1; + +--echo # +--echo # TIME literals in no-zero date context +--echo # +SELECT TO_DAYS(TIME'00:00:00'); +SELECT TO_SECONDS(TIME'00:00:00'); +SELECT DAYOFYEAR(TIME'00:00:00'); +SELECT WEEK(TIME'00:00:00'); +SELECT YEARWEEK(TIME'00:00:00'); +SELECT WEEKDAY(TIME'00:00:00'); +SELECT CONVERT_TZ(TIME'00:00:00','+00:00','+01:00'); +SELECT DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR); +SELECT TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00'); + +--echo # +--echo # Testing Item_func::fix_fields() +--echo # +--error ER_WRONG_ARGUMENTS +SELECT 'a' LIKE 'a' ESCAPE DATE'2001-01-01'; +--error ER_WRONG_ARGUMENTS +SELECT 'a' LIKE 'a' ESCAPE TIMESTAMP'2001-01-01 00:00:00'; +--error ER_WRONG_ARGUMENTS +SELECT 'a' LIKE 'a' ESCAPE TIME'00:00:00'; diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 343bb0ec614..b19f4d1d149 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -226,6 +226,23 @@ my_bool check_datetime_range(const MYSQL_TIME *ltime) } +static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status, + uint *number_of_fields, + const char **str, const char *end) +{ + const char *start= *str; + uint tmp= 0; /* For the case '10:10:10.' */ + if (get_digits(&tmp, number_of_fields, str, end, 6)) + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; + if ((status->precision= (*str - start)) < 6) + *val= tmp * log_10_int[6 - (*str - start)]; + else + *val= tmp; + if (skip_digits(str, end)) + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; +} + + /* Convert a timestamp string to a MYSQL_TIME value. @@ -240,9 +257,8 @@ my_bool check_datetime_range(const MYSQL_TIME *ltime) TIME_NO_ZERO_IN_DATE Don't allow partial dates TIME_NO_ZERO_DATE Don't allow 0000-00-00 date TIME_INVALID_DATES Allow 2000-02-31 - was_cut 0 Value OK - 1 If value was cut during conversion - 2 check_date(date,flags) considers date invalid + status Conversion status + DESCRIPTION At least the following formats are recogniced (based on number of digits) @@ -253,20 +269,29 @@ my_bool check_datetime_range(const MYSQL_TIME *ltime) The second part may have an optional .###### fraction part. - RETURN VALUES + status->warnings is set to: + 0 Value OK + MYSQL_TIME_WARN_TRUNCATED If value was cut during conversion + MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid + + l_time->time_type is set as follows: MYSQL_TIMESTAMP_NONE String wasn't a timestamp, like [DD [HH:[MM:[SS]]]].fraction. + l_time is not changed. MYSQL_TIMESTAMP_DATE DATE string (YY MM and DD parts ok) MYSQL_TIMESTAMP_DATETIME Full timestamp MYSQL_TIMESTAMP_ERROR Timestamp with wrong values. All elements in l_time is set to 0 + RETURN VALUES + 0 - Ok + 1 - Error */ #define MAX_DATE_PARTS 8 -enum enum_mysql_timestamp_type +my_bool str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, - ulonglong flags, int *was_cut) + ulonglong flags, MYSQL_TIME_STATUS *status) { const char *end=str+length, *pos; uint number_of_fields= 0, digits, year_length, not_zero_date; @@ -275,19 +300,20 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, if (flags & TIME_TIME_ONLY) { - enum enum_mysql_timestamp_type ret; - ret= str_to_time(str, length, l_time, flags, was_cut); + my_bool ret= str_to_time(str, length, l_time, flags, status); DBUG_RETURN(ret); } - *was_cut= 0; + + my_time_status_init(status); /* Skip space at start */ for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++) ; if (str == end || ! my_isdigit(&my_charset_latin1, *str)) { - *was_cut= 1; - DBUG_RETURN(MYSQL_TIMESTAMP_NONE); + status->warnings= MYSQL_TIME_WARN_TRUNCATED; + l_time->time_type= MYSQL_TIMESTAMP_NONE; + DBUG_RETURN(1); } /* @@ -316,55 +342,49 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, (only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]]) */ year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2; - *was_cut= 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); + 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)) + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; } else { const char *start= str; - *was_cut = get_number(&l_time->year, &number_of_fields, &str, end); + if (get_number(&l_time->year, &number_of_fields, &str, end)) + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; year_length= str - start; - if (!*was_cut) - *was_cut= get_punct(&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, flags, &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_punct(&str, end) - || get_number(&l_time->second, &number_of_fields, &str, end); + if (!status->warnings && + (get_punct(&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, flags, &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_punct(&str, end) + || get_number(&l_time->second, &number_of_fields, &str, end))) + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; } - if (number_of_fields < 3) - *was_cut= 1; - /* we're ok if date part is correct. even if the rest is truncated */ - if (*was_cut && number_of_fields < 3) - DBUG_RETURN(MYSQL_TIMESTAMP_NONE); + if (number_of_fields < 3) + { + l_time->time_type= MYSQL_TIMESTAMP_NONE; + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; + DBUG_RETURN(TRUE); + } - if (!*was_cut && str < end && *str == '.') + if (!status->warnings && str < end && *str == '.') { - uint second_part; - const char *start= ++str; - *was_cut= get_digits(&second_part, &number_of_fields, &str, end, 6); - if (number_of_fields == 7) - { - if (str - start < 6) - second_part*= log_10_int[6 - (str - start)]; - l_time->second_part= second_part; - } - else - l_time->second_part= 0; - if (skip_digits(&str, end)) - *was_cut= 1; + str++; + get_microseconds(&l_time->second_part, status, + &number_of_fields, &str, end); } not_zero_date = l_time->year || l_time->month || l_time->day || @@ -377,11 +397,11 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, if (l_time->year > 9999 || l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 || l_time->minute > 59 || l_time->second > 59) { - *was_cut= 1; + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; goto err; } - if (check_date(l_time, not_zero_date, flags, was_cut)) + if (check_date(l_time, not_zero_date, flags, &status->warnings)) goto err; l_time->time_type= (number_of_fields <= 3 ? @@ -391,16 +411,17 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, { if (!my_isspace(&my_charset_latin1,*str)) { - *was_cut= 1; + status->warnings= MYSQL_TIME_WARN_TRUNCATED; break; } } - DBUG_RETURN(l_time->time_type); + DBUG_RETURN(FALSE); err: bzero((char*) l_time, sizeof(*l_time)); - DBUG_RETURN(MYSQL_TIMESTAMP_ERROR); + l_time->time_type= MYSQL_TIMESTAMP_ERROR; + DBUG_RETURN(TRUE); } @@ -415,23 +436,26 @@ err: There may be an optional [.second_part] after seconds length Length of str l_time Store result here - warning Set MYSQL_TIME_WARN_TRUNCATED flag if the input string - was cut during conversion, and/or - MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is - out of range. + status Conversion status + NOTES + Because of the extra days argument, this function can only work with times where the time arguments are in the above order. + status->warnings is set as follows: + MYSQL_TIME_WARN_TRUNCATED if the input string was cut during conversion, + and/or + MYSQL_TIME_WARN_OUT_OF_RANGE flag is set if the value is out of range. + RETURN - MYSQL_TIMESTAMP_TIME - MYSQL_TIMESTAMP_ERROR + FALSE on success + TRUE on error */ -enum enum_mysql_timestamp_type -str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - ulonglong fuzzydate, int *warning) +my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, + ulonglong fuzzydate, MYSQL_TIME_STATUS *status) { ulong date[5]; ulonglong value; @@ -439,7 +463,7 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time, my_bool found_days,found_hours, neg= 0; uint UNINIT_VAR(state); - *warning= 0; + my_time_status_init(status); for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++) length--; if (str != end && *str == '-') @@ -449,22 +473,20 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time, length--; } if (str == end) - return MYSQL_TIMESTAMP_ERROR; + { + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; + goto err; + } /* Check first if this is a full TIMESTAMP */ if (length >= 12) { /* Probably full timestamp */ - int was_cut; - enum enum_mysql_timestamp_type - res= str_to_datetime(str, length, l_time, + (void) str_to_datetime(str, length, l_time, (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY, - &was_cut); - if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR) - { - if (was_cut) - *warning|= MYSQL_TIME_WARN_TRUNCATED; - return res; - } + status); + if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR) + return l_time->time_type == MYSQL_TIMESTAMP_ERROR; + my_time_status_init(status); } l_time->neg= neg; @@ -532,24 +554,15 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time, fractional: /* Get fractional second part */ - if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1])) + if (!status->warnings && str < end && *str == '.') { - int field_length= 5; - str++; value=(uint) (uchar) (*str - '0'); - while (++str != end && my_isdigit(&my_charset_latin1, *str)) - { - if (field_length-- > 0) - value= value*10 + (uint) (uchar) (*str - '0'); - } - if (field_length > 0) - value*= (long) log_10_int[field_length]; - else if (field_length < 0) - *warning|= MYSQL_TIME_WARN_TRUNCATED; - date[4]= (ulong) value; + uint number_of_fields= 0; + str++; + get_microseconds(&date[4], status, &number_of_fields, &str, end); } else - date[4]=0; - + date[4]= 0; + /* Check for exponent part: E<gigit> | E<sign><digit> */ /* (may occur as result of %g formatting of time value) */ if ((end - str) > 1 && @@ -558,7 +571,10 @@ fractional: ((str[1] == '-' || str[1] == '+') && (end - str) > 2 && my_isdigit(&my_charset_latin1, str[2])))) - return MYSQL_TIMESTAMP_ERROR; + { + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; + goto err; + } if (internal_format_positions[7] != 255) { @@ -581,8 +597,11 @@ fractional: if (date[0] > UINT_MAX || date[1] > UINT_MAX || date[2] > UINT_MAX || date[3] > UINT_MAX || date[4] > UINT_MAX) - return MYSQL_TIMESTAMP_ERROR; - + { + status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + goto err; + } + l_time->year= 0; /* For protocol::store_time */ l_time->month= 0; l_time->day= date[0]; @@ -593,9 +612,9 @@ fractional: l_time->time_type= MYSQL_TIMESTAMP_TIME; /* Check if the value is valid and fits into MYSQL_TIME range */ - if (check_time_range(l_time, 6, warning)) - return MYSQL_TIMESTAMP_ERROR; - + if (check_time_range(l_time, 6, &status->warnings)) + return TRUE; + /* Check if there is garbage at end of the MYSQL_TIME specification */ if (str != end) { @@ -603,12 +622,17 @@ fractional: { if (!my_isspace(&my_charset_latin1,*str)) { - *warning|= MYSQL_TIME_WARN_TRUNCATED; + status->warnings|= MYSQL_TIME_WARN_TRUNCATED; break; } } while (++str != end); } - return MYSQL_TIMESTAMP_TIME; + return FALSE; + +err: + bzero((char*) l_time, sizeof(*l_time)); + l_time->time_type= MYSQL_TIMESTAMP_ERROR; + return TRUE; } @@ -638,7 +662,10 @@ int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning) 999000, 999900, 999990, 999999}; if (my_time->minute >= 60 || my_time->second >= 60) + { + *warning|= MYSQL_TIME_WARN_TRUNCATED; return 1; + } hour= my_time->hour + (24*my_time->day); @@ -1406,7 +1433,7 @@ double TIME_to_double(const MYSQL_TIME *my_time) return my_time->neg ? -d : d; } -longlong pack_time(MYSQL_TIME *my_time) +longlong pack_time(const MYSQL_TIME *my_time) { return ((((((my_time->year * 13ULL + my_time->month) * 32ULL + diff --git a/sql/field.cc b/sql/field.cc index c51a04ba140..e6cab7184fd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1900,7 +1900,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + ltime, fuzzydate)) return 1; return 0; } @@ -4647,18 +4647,18 @@ int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { MYSQL_TIME l_time; - int error; - int have_smth_to_conv; + MYSQL_TIME_STATUS status; + bool have_smth_to_conv; ErrConvString str(from, len, cs); THD *thd= get_thd(); /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ - have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time, + have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | - MODE_NO_ZERO_IN_DATE, &error) > - MYSQL_TIMESTAMP_ERROR); - return store_TIME_with_warning(thd, &l_time, &str, error, have_smth_to_conv); + MODE_NO_ZERO_IN_DATE, &status); + return store_TIME_with_warning(thd, &l_time, &str, + status.warnings, have_smth_to_conv); } @@ -5154,18 +5154,16 @@ int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime, int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs) { MYSQL_TIME ltime; - int error; - enum enum_mysql_timestamp_type func_res; + MYSQL_TIME_STATUS status; THD *thd= get_thd(); ErrConvString str(from, len, cs); - - func_res= str_to_datetime(cs, from, len, <ime, - (TIME_FUZZY_DATE | - (thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error); - return store_TIME_with_warning(<ime, &str, error, func_res > MYSQL_TIMESTAMP_ERROR); + bool func_res= !str_to_datetime(cs, from, len, <ime, + (TIME_FUZZY_DATE | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), + &status); + return store_TIME_with_warning(<ime, &str, status.warnings, func_res); } @@ -5280,16 +5278,17 @@ void Field_time::store_TIME(MYSQL_TIME *ltime) int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) { MYSQL_TIME ltime; + MYSQL_TIME_STATUS status; ErrConvString str(from, len, cs); - int was_cut; - int have_smth_to_conv= - str_to_time(cs, from, len, <ime, + bool have_smth_to_conv= + !str_to_time(cs, from, len, <ime, get_thd()->variables.sql_mode & (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES), - &was_cut) > MYSQL_TIMESTAMP_ERROR; + &status); - return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv); + return store_TIME_with_warning(<ime, &str, + status.warnings, have_smth_to_conv); } diff --git a/sql/item.cc b/sql/item.cc index f9def19bf6a..21eacb38401 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -377,6 +377,27 @@ my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) } +longlong Item::val_int_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + longlong v= TIME_to_ulonglong(<ime); + return ltime.neg ? -v : v; +} + + +double Item::val_real_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return TIME_to_double(<ime); +} + + double Item::val_real_from_decimal() { /* Note that fix_fields may not be called for Item_avg_field items */ @@ -1272,7 +1293,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + ltime, fuzzydate)) goto err; break; } @@ -6283,6 +6304,76 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length) } +bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const +{ + return + item->basic_const_item() && type() == item->type() && + field_type() == ((Item_temporal_literal *) item)->field_type() && + !my_time_compare(&cached_time, + &((Item_temporal_literal *) item)->cached_time); +} + + +void Item_date_literal::print(String *str, enum_query_type query_type) +{ + str->append("DATE'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_date_to_str(&cached_time, buf); + str->append(buf); + str->append('\''); +} + + +bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + *ltime= cached_time; + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + +void Item_datetime_literal::print(String *str, enum_query_type query_type) +{ + str->append("TIMESTAMP'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_datetime_to_str(&cached_time, buf, decimals); + str->append(buf); + str->append('\''); +} + + +bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + *ltime= cached_time; + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + +void Item_time_literal::print(String *str, enum_query_type query_type) +{ + str->append("TIME'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_time_to_str(&cached_time, buf, decimals); + str->append(buf); + str->append('\''); +} + + +bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + *ltime= cached_time; + if (fuzzy_date & TIME_TIME_ONLY) + return (null_value= false); + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + + /** Pack data in buffer for sending. */ diff --git a/sql/item.h b/sql/item.h index 1a373da9ba5..a298f16a93b 100644 --- a/sql/item.h +++ b/sql/item.h @@ -599,7 +599,8 @@ public: SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM, XPATH_NODESET, XPATH_NODESET_CMP, - VIEW_FIXER_ITEM, EXPR_CACHE_ITEM}; + VIEW_FIXER_ITEM, EXPR_CACHE_ITEM, + DATE_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -956,7 +957,9 @@ public: my_decimal *val_decimal_from_date(my_decimal *decimal_value); my_decimal *val_decimal_from_time(my_decimal *decimal_value); longlong val_int_from_decimal(); + longlong val_int_from_date(); double val_real_from_decimal(); + double val_real_from_date(); int save_time_in_field(Field *field); int save_date_in_field(Field *field); @@ -1113,8 +1116,8 @@ public: */ virtual CHARSET_INFO *charset_for_protocol(void) const { - return result_type() == STRING_RESULT ? collation.collation : - &my_charset_bin; + return cmp_type() == STRING_RESULT ? collation.collation : + &my_charset_bin; }; virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg) @@ -2877,6 +2880,110 @@ public: Item_bin_string(const char *str,uint str_length); }; + +class Item_temporal_literal :public Item_basic_constant +{ + //sql_mode= current_thd->variables.sql_mode & + // (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); +protected: + MYSQL_TIME cached_time; +public: + /** + Constructor for Item_date_literal. + @param ltime DATE value. + */ + Item_temporal_literal(MYSQL_TIME *ltime) :Item_basic_constant() + { + collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); + decimals= 0; + cached_time= *ltime; + } + Item_temporal_literal(MYSQL_TIME *ltime, uint dec_arg) :Item_basic_constant() + { + collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); + decimals= dec_arg; + cached_time= *ltime; + } + bool basic_const_item() const { return true; } + bool const_item() const { return true; } + enum Type type() const { return DATE_ITEM; } + bool eq(const Item *item, bool binary_cmp) const; + enum Item_result result_type () const { return STRING_RESULT; } + Item_result cmp_type() const { return TIME_RESULT; } + + bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} + + String *val_str(String *str) + { return val_string_from_date(str); } + longlong val_int() + { return val_int_from_date(); } + double val_real() + { return val_real_from_date(); } + my_decimal *val_decimal(my_decimal *decimal_value) + { return val_decimal_from_date(decimal_value); } + Field *tmp_table_field(TABLE *table) + { return tmp_table_field_from_field_type(table, 0); } + int save_in_field(Field *field, bool no_conversions) + { return save_date_in_field(field); } +}; + + +/** + DATE'2010-01-01' +*/ +class Item_date_literal: public Item_temporal_literal +{ +public: + Item_date_literal(MYSQL_TIME *ltime) + :Item_temporal_literal(ltime) + { + max_length= MAX_DATE_WIDTH; + fixed= 1; + } + enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + void print(String *str, enum_query_type query_type); + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); +}; + + +/** + TIME'10:10:10' +*/ +class Item_time_literal: public Item_temporal_literal +{ +public: + Item_time_literal(MYSQL_TIME *ltime, uint dec_arg) + :Item_temporal_literal(ltime, dec_arg) + { + max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0); + fixed= 1; + } + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + void print(String *str, enum_query_type query_type); + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); +}; + + +/** + TIMESTAMP'2001-01-01 10:20:30' +*/ +class Item_datetime_literal: public Item_temporal_literal +{ +public: + Item_datetime_literal(MYSQL_TIME *ltime, uint dec_arg) + :Item_temporal_literal(ltime, dec_arg) + { + max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0); + fixed= 1; + } + enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + void print(String *str, enum_query_type query_type); + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); +}; + + + class Item_result_field :public Item /* Item with result field */ { public: diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5f62ee946a5..057d75676dc 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -721,31 +721,31 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, const char *warn_name, MYSQL_TIME *l_time) { bool value; - int error; - enum_mysql_timestamp_type timestamp_type; + MYSQL_TIME_STATUS status; int flags= TIME_FUZZY_DATE | MODE_INVALID_DATES; ErrConvString err(str); - if (warn_type == MYSQL_TIMESTAMP_TIME) - flags|= TIME_TIME_ONLY; - - timestamp_type= - str_to_datetime(str->charset(), str->ptr(), str->length(), - l_time, flags, &error); + DBUG_ASSERT(warn_type != MYSQL_TIMESTAMP_TIME); - if (timestamp_type > MYSQL_TIMESTAMP_ERROR) + if (!str_to_datetime(str->charset(), str->ptr(), str->length(), + l_time, flags, &status)) + { + DBUG_ASSERT(l_time->time_type == MYSQL_TIMESTAMP_DATETIME || + l_time->time_type == MYSQL_TIMESTAMP_DATE); /* Do not return yet, we may still want to throw a "trailing garbage" warning. */ value= FALSE; + } else { + DBUG_ASSERT(l_time->time_type != MYSQL_TIMESTAMP_TIME); + DBUG_ASSERT(status.warnings != 0); // Must be set by set_to_datetime() value= TRUE; - error= 1; /* force warning */ } - if (error > 0) + if (status.warnings > 0) make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, &err, warn_type, warn_name); diff --git a/sql/item_create.cc b/sql/item_create.cc index ce4dc7ced8f..c6d0f09907b 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -32,6 +32,7 @@ #include "set_var.h" #include "sp_head.h" #include "sp.h" +#include "sql_time.h" /* ============================================================================= @@ -5821,6 +5822,70 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, } +/** + Builder for datetime literals: + TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'. + @param thd The current thread + @param str Character literal + @param length Length of str + @param type Type of literal (TIME, DATE or DATETIME) + @param send_error Whether to generate an error on failure +*/ + +Item *create_temporal_literal(THD *thd, + const char *str, uint length, + CHARSET_INFO *cs, + enum_field_types type, + bool send_error) +{ + MYSQL_TIME_STATUS status; + MYSQL_TIME ltime; + Item *item= NULL; + ulonglong datetime_flags= thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | + MODE_NO_ZERO_DATE | + MODE_INVALID_DATES); + ulonglong flags= TIME_FUZZY_DATE | datetime_flags; + + switch(type) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + if (!str_to_datetime(cs, str, length, <ime, flags, &status) && + ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings) + item= new (thd->mem_root) Item_date_literal(<ime); + break; + case MYSQL_TYPE_DATETIME: + if (!str_to_datetime(cs, str, length, <ime, flags, &status) && + ltime.time_type == MYSQL_TIMESTAMP_DATETIME && !status.warnings) + item= new (thd->mem_root) Item_datetime_literal(<ime, + status.precision); + break; + case MYSQL_TYPE_TIME: + if (!str_to_time(cs, str, length, <ime, 0, &status) && + ltime.time_type == MYSQL_TIMESTAMP_TIME && !status.warnings) + item= new (thd->mem_root) Item_time_literal(<ime, + status.precision); + break; + default: + DBUG_ASSERT(0); + } + + if (item) + return item; + + if (send_error) + { + const char *typestr= + (type == MYSQL_TYPE_DATE) ? "DATE" : + (type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME"; + ErrConvString err(str, length, thd->variables.character_set_client); + my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr()); + } + return NULL; +} + + static List<Item> *create_func_dyncol_prepare(THD *thd, DYNCALL_CREATE_DEF **dfs, List<DYNCALL_CREATE_DEF> &list) diff --git a/sql/item_create.h b/sql/item_create.h index 5ecb45e9eae..5f1a8c6006d 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -168,6 +168,11 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, const char *len, const char *dec, CHARSET_INFO *cs); +Item *create_temporal_literal(THD *thd, + const char *str, uint length, + CHARSET_INFO *cs, + enum_field_types type, + bool send_error); int item_create_init(); void item_create_cleanup(); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 132515733d6..1495456bda4 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -4725,7 +4725,7 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (str_to_datetime_with_warn(&my_charset_numeric, val.x.string.value.str, val.x.string.value.length, - ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR) + ltime, fuzzy_date)) goto null; return 0; case DYN_COL_DATETIME: diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 84510e0f112..c0e623f499c 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2430,17 +2430,8 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; - - int unused; - if (check_date(ltime, ltime->year || ltime->month || ltime->day, - fuzzy_date, &unused)) - { - ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_DATE, 0); - return (null_value= 1); - } - return (null_value= 0); + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_DATE)); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 9b2db9e816e..3f58fe09af1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -489,7 +489,6 @@ public: Item_temporal_func(Item *a, Item *b) :Item_func(a,b) {} Item_temporal_func(Item *a, Item *b, Item *c) :Item_func(a,b,c) {} enum Item_result result_type () const { return STRING_RESULT; } - CHARSET_INFO *charset_for_protocol(void) const { return &my_charset_bin; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } Item_result cmp_type() const { return TIME_RESULT; } String *val_str(String *str); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 93f8711fb7e..737f568fd62 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -14696,6 +14696,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::REAL_ITEM: case Item::DECIMAL_ITEM: case Item::STRING_ITEM: + case Item::DATE_ITEM: case Item::REF_ITEM: case Item::NULL_ITEM: case Item::VARBIN_ITEM: diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 89c2e3b7086..ff2ec62f815 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -214,6 +214,22 @@ ulong convert_month_to_period(ulong month) } +bool +check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, + timestamp_type ts_type) +{ + int unused; + if (check_date(ltime, fuzzy_date, &unused)) + { + ErrConvTime str(ltime); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, ts_type, 0); + return true; + } + return false; +} + + /* Convert a string to 8-bit representation, for use in str_to_time/str_to_date/str_to_date. @@ -249,9 +265,9 @@ to_ascii(CHARSET_INFO *cs, /* Character set-aware version of str_to_time() */ -timestamp_type +bool str_to_time(CHARSET_INFO *cs, const char *str,uint length, - MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning) + MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) { char cnv[32]; if ((cs->state & MY_CS_NONASCII) != 0) @@ -259,14 +275,14 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length, length= to_ascii(cs, str, length, cnv, sizeof(cnv)); str= cnv; } - return str_to_time(str, length, l_time, fuzzydate, warning); + return str_to_time(str, length, l_time, fuzzydate, status); } /* Character set-aware version of str_to_datetime() */ -timestamp_type str_to_datetime(CHARSET_INFO *cs, - const char *str, uint length, - MYSQL_TIME *l_time, ulonglong flags, int *was_cut) +bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length, + MYSQL_TIME *l_time, ulonglong flags, + MYSQL_TIME_STATUS *status) { char cnv[32]; if ((cs->state & MY_CS_NONASCII) != 0) @@ -274,7 +290,7 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs, length= to_ascii(cs, str, length, cnv, sizeof(cnv)); str= cnv; } - return str_to_datetime(str, length, l_time, flags, was_cut); + return str_to_datetime(str, length, l_time, flags, status); } @@ -286,26 +302,24 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs, See description of str_to_datetime() for more information. */ -timestamp_type +bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, uint length, MYSQL_TIME *l_time, ulonglong flags) { - int was_cut; + MYSQL_TIME_STATUS status; THD *thd= current_thd; - timestamp_type ts_type; - - ts_type= str_to_datetime(cs, str, length, l_time, + bool ret_val= str_to_datetime(cs, str, length, l_time, (flags | (sql_mode_for_dates(thd))), - &was_cut); - if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) + &status); + if (ret_val || status.warnings) make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, str, length, flags & TIME_TIME_ONLY ? - MYSQL_TIMESTAMP_TIME : ts_type, NullS); + MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS); DBUG_EXECUTE_IF("str_to_datetime_warn", push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_YES, str);); - return ts_type; + return ret_val; } @@ -1055,7 +1069,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s */ -int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) +int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b) { ulonglong a_t= pack_time(a); ulonglong b_t= pack_time(b); diff --git a/sql/sql_time.h b/sql/sql_time.h index c1a75bb2ad3..cf029f143b3 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -35,11 +35,9 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code); -bool str_to_time_with_warn(CHARSET_INFO *cs, const char *str, uint length, - MYSQL_TIME *l_time, ulonglong fuzzydate); -timestamp_type str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, - uint length, MYSQL_TIME *l_time, - ulonglong flags); +bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, + uint length, MYSQL_TIME *l_time, + ulonglong flags); bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, ulonglong fuzzydate, const char *name); @@ -76,7 +74,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval); bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out, long *microseconds_out); -int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b); +int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b); void localtime_to_TIME(MYSQL_TIME *to, struct tm *from); void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds); uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year); @@ -86,12 +84,14 @@ bool parse_date_time_format(timestamp_type format_type, const char *format, uint format_length, DATE_TIME_FORMAT *date_time_format); /* Character set-aware version of str_to_time() */ -timestamp_type str_to_time(CHARSET_INFO *cs, const char *str,uint length, - MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning); +bool str_to_time(CHARSET_INFO *cs, const char *str,uint length, + MYSQL_TIME *l_time, ulonglong fuzzydate, + MYSQL_TIME_STATUS *status); /* Character set-aware version of str_to_datetime() */ -timestamp_type str_to_datetime(CHARSET_INFO *cs, - const char *str, uint length, - MYSQL_TIME *l_time, ulonglong flags, int *was_cut); +bool str_to_datetime(CHARSET_INFO *cs, + const char *str, uint length, + MYSQL_TIME *l_time, ulonglong flags, + MYSQL_TIME_STATUS *status); /* convenience wrapper */ inline bool parse_date_time_format(timestamp_type format_type, @@ -110,4 +110,18 @@ extern DATE_TIME_FORMAT global_time_format; extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[]; extern LEX_STRING interval_type_to_name[]; + +static inline bool +non_zero_date(const MYSQL_TIME *ltime) +{ + return ltime->year || ltime->month || ltime->day; +} +static inline bool +check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut) +{ + return check_date(ltime, non_zero_date(ltime), flags, was_cut); +} +bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, + timestamp_type ts_type); + #endif /* SQL_TIME_INCLUDED */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 83b5692d606..0dc8ed7037e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -32,6 +32,7 @@ #define YYTHD ((THD *)yythd) #define YYLIP (& YYTHD->m_parser_state->m_lip) #define YYPS (& YYTHD->m_parser_state->m_yacc) +#define YYCSCL YYTHD->variables.character_set_client #define MYSQL_YACC #define YYINITDEPTH 100 @@ -899,10 +900,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 170 shift/reduce conflicts. + Currently there are 167 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 170 +%expect 167 /* Comments for TOKENS. @@ -1628,7 +1629,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); replace_lock_option opt_low_priority insert_lock_option load_data_lock %type <item> - literal text_literal insert_ident order_ident + literal text_literal insert_ident order_ident temporal_literal simple_ident expr opt_expr opt_else sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr @@ -8741,7 +8742,48 @@ simple_expr: MYSQL_YYABORT; } | '{' ident expr '}' - { $$= $3; } + { + Item_string *item; + $$= NULL; + /* + If "expr" is reasonably short pure ASCII string literal, + try to parse known ODBC style date, time or timestamp literals, + e.g: + SELECT {d'2001-01-01'}; + SELECT {t'10:20:30'}; + SELECT {ts'2001-01-01 10:20:30'}; + */ + if ($3->type() == Item::STRING_ITEM && + (item= (Item_string *) $3) && + item->collation.repertoire == MY_REPERTOIRE_ASCII && + item->str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4) + { + enum_field_types type= MYSQL_TYPE_STRING; + LEX_STRING *ls= &$2; + if (ls->length == 1) + { + if (ls->str[0] == 'd') /* {d'2001-01-01'} */ + type= MYSQL_TYPE_DATE; + else if (ls->str[0] == 't') /* {t'10:20:30'} */ + type= MYSQL_TYPE_TIME; + } + else if (ls->length == 2) /* {ts'2001-01-01 10:20:30'} */ + { + if (ls->str[0] == 't' && ls->str[1] == 's') + type= MYSQL_TYPE_DATETIME; + } + if (type != MYSQL_TYPE_STRING) + { + $$= create_temporal_literal(YYTHD, + item->str_value.ptr(), + item->str_value.length(), + item->str_value.charset(), + type, false); + } + } + if ($$ == NULL) + $$= $3; + } | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' { $2->push_front($5); @@ -12730,6 +12772,7 @@ signed_literal: literal: text_literal { $$ = $1; } | NUM_literal { $$ = $1; } + | temporal_literal { $$= $1; } | NULL_SYM { $$ = new (YYTHD->mem_root) Item_null(); @@ -12824,9 +12867,6 @@ literal: $$= item_str; } - | DATE_SYM text_literal { $$ = $2; } - | TIME_SYM text_literal { $$ = $2; } - | TIMESTAMP text_literal { $$ = $2; } ; NUM_literal: @@ -12875,6 +12915,31 @@ NUM_literal: } ; + +temporal_literal: + DATE_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATE, true))) + MYSQL_YYABORT; + } + | TIME_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_TIME, true))) + MYSQL_YYABORT; + } + | TIMESTAMP TEXT_STRING + { + if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATETIME, true))) + MYSQL_YYABORT; + } + ; + + + + /********************************************************************** ** Creating different items. **********************************************************************/ diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index d17de6cd32e..48d127dac2f 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -567,7 +567,7 @@ int spider_db_mysql_result::fetch_table_status( #else
my_bool not_used_my_bool;
#endif
- int not_used_int;
+ MYSQL_TIME_STATUS time_status;
long not_used_long;
DBUG_ENTER("spider_db_mysql_result::fetch_table_status");
DBUG_PRINT("info",("spider this=%p", this));
@@ -628,8 +628,9 @@ int spider_db_mysql_result::fetch_table_status( ("spider auto_increment_value=%lld", auto_increment_value));
if (mysql_row[11])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[11], strlen(mysql_row[11]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
create_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
@@ -650,8 +651,9 @@ int spider_db_mysql_result::fetch_table_status( #endif
if (mysql_row[12])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[12], strlen(mysql_row[12]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
update_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
@@ -672,8 +674,9 @@ int spider_db_mysql_result::fetch_table_status( #endif
if (mysql_row[13])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[13], strlen(mysql_row[13]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
check_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
@@ -737,8 +740,9 @@ int spider_db_mysql_result::fetch_table_status( ("spider auto_increment_value=%lld", auto_increment_value));
if (mysql_row[6])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[6], strlen(mysql_row[6]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
create_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
@@ -759,8 +763,9 @@ int spider_db_mysql_result::fetch_table_status( #endif
if (mysql_row[7])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[7], strlen(mysql_row[7]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
update_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
@@ -781,8 +786,9 @@ int spider_db_mysql_result::fetch_table_status( #endif
if (mysql_row[8])
{
+ my_time_status_init(&time_status);
str_to_datetime(mysql_row[8], strlen(mysql_row[8]), &mysql_time, 0,
- ¬_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
check_time = (time_t) my_system_gmt_sec(&mysql_time,
¬_used_long, ¬_used_uint);
|