summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqlbinlog.cc7
-rw-r--r--include/my_time.h27
-rw-r--r--mysql-test/r/cast.result20
-rw-r--r--mysql-test/r/partition_innodb.result4
-rw-r--r--mysql-test/r/temporal_literal.result418
-rw-r--r--mysql-test/t/cast.test10
-rw-r--r--mysql-test/t/partition_innodb.test4
-rw-r--r--mysql-test/t/temporal_literal.test217
-rw-r--r--sql-common/my_time.c222
-rw-r--r--sql/field.cc43
-rw-r--r--sql/item.cc93
-rw-r--r--sql/item.h113
-rw-r--r--sql/item_cmpfunc.cc22
-rw-r--r--sql/item_create.cc65
-rw-r--r--sql/item_create.h5
-rw-r--r--sql/item_strfunc.cc2
-rw-r--r--sql/item_timefunc.cc13
-rw-r--r--sql/item_timefunc.h1
-rw-r--r--sql/sql_select.cc1
-rw-r--r--sql/sql_time.cc48
-rw-r--r--sql/sql_time.h36
-rw-r--r--sql/sql_yacc.yy79
-rw-r--r--storage/spider/spd_db_mysql.cc20
23 files changed, 1255 insertions, 215 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index c099459f61e..f38a88749db 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -1541,13 +1541,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 046b5c94923..f7227b168be 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 d4093bb4df9..03701f61ae9 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -130,6 +130,7 @@ static int get_digits(uint *val, uint *number_of_fields, const char **str,
return get_number(val, number_of_fields, str, min(end, *str + length));
}
+
static int get_punct(const char **str, const char *end)
{
if (*str >= end)
@@ -203,6 +204,24 @@ static uint skip_digits(const char **str, const char *end)
return s - start;
}
+
+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.
@@ -217,9 +236,8 @@ static uint skip_digits(const char **str, const char *end)
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)
@@ -230,20 +248,29 @@ static uint skip_digits(const char **str, const char *end)
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;
@@ -252,19 +279,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);
}
/*
@@ -293,50 +321,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 (str - start < 6)
- second_part*= log_10_int[6 - (str - start)];
- l_time->second_part= second_part;
- 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 ||
@@ -349,11 +376,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 ?
@@ -363,16 +390,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);
}
@@ -387,23 +415,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;
@@ -411,7 +442,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 == '-')
@@ -421,22 +452,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;
@@ -504,24 +533,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 &&
@@ -530,7 +550,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)
{
@@ -553,8 +576,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];
@@ -565,9 +591,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)
{
@@ -575,12 +601,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;
}
@@ -610,7 +641,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);
@@ -1357,7 +1391,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 e85903d76c6..8bf9561b586 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1822,7 +1822,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;
}
@@ -4570,18 +4570,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);
}
@@ -5060,18 +5060,16 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
int Field_temporal::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, &ltime,
- (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(&ltime, &str, error, func_res > MYSQL_TIMESTAMP_ERROR);
+ bool func_res= !str_to_datetime(cs, from, len, &ltime,
+ (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(&ltime, &str, status.warnings, func_res);
}
@@ -5157,16 +5155,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, &ltime,
+ bool have_smth_to_conv=
+ !str_to_time(cs, from, len, &ltime,
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(&ltime, &str, was_cut, have_smth_to_conv);
+ return store_TIME_with_warning(&ltime, &str,
+ status.warnings, have_smth_to_conv);
}
diff --git a/sql/item.cc b/sql/item.cc
index a5d5cf61496..d1fd2fc82ae 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(&ltime, TIME_FUZZY_DATE))
+ return 0;
+ longlong v= TIME_to_ulonglong(&ltime);
+ return ltime.neg ? -v : v;
+}
+
+
+double Item::val_real_from_date()
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, TIME_FUZZY_DATE))
+ return 0;
+ return TIME_to_double(&ltime);
+}
+
+
double Item::val_real_from_decimal()
{
/* Note that fix_fields may not be called for Item_avg_field items */
@@ -1269,7 +1290,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;
}
@@ -6286,6 +6307,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, &ltime, flags, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings)
+ item= new (thd->mem_root) Item_date_literal(&ltime);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_DATETIME && !status.warnings)
+ item= new (thd->mem_root) Item_datetime_literal(&ltime,
+ status.precision);
+ break;
+ case MYSQL_TYPE_TIME:
+ if (!str_to_time(cs, str, length, &ltime, 0, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_TIME && !status.warnings)
+ item= new (thd->mem_root) Item_time_literal(&ltime,
+ 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 5bf53784c26..9582c7940f1 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -4722,7 +4722,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 f43a4e79431..1f3d7bb6265 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 806017b9ae5..7c7239fd400 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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
create_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
update_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
check_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
create_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
update_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_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,
- &not_used_int);
+ &time_status);
#ifdef MARIADB_BASE_VERSION
check_time = (time_t) my_system_gmt_sec(&mysql_time,
&not_used_long, &not_used_uint);