summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <msvensson@shellback.(none)>2006-10-12 17:37:59 +0200
committerunknown <msvensson@shellback.(none)>2006-10-12 17:37:59 +0200
commit0e33031804e081e2b57ab35c08b1bd936e8d4f49 (patch)
tree04d4f7a81776f7c95c6652f1381485e70b3a4a4b
parenta4f01a94b4d6fdf3d1c79446c7f1a36f50903d1d (diff)
parent04502c3f21536304481567ff80766f911bca16cb (diff)
downloadmariadb-git-0e33031804e081e2b57ab35c08b1bd936e8d4f49.tar.gz
Merge shellback.(none):/home/msvensson/mysql/mysql-5.0-maint
into shellback.(none):/home/msvensson/mysql/mysql-5.0-maint-new mysql-test/r/mysql.result: Auto merged mysql-test/t/mysql.test: Auto merged sql/sql_parse.cc: Auto merged
-rw-r--r--BitKeeper/etc/collapsed1
-rw-r--r--client/mysql.cc15
-rw-r--r--include/my_time.h14
-rw-r--r--myisam/sort.c4
-rw-r--r--mysql-test/r/func_date_add.result14
-rw-r--r--mysql-test/r/func_sapdb.result15
-rw-r--r--mysql-test/r/func_time.result95
-rw-r--r--mysql-test/r/mysql.result13
-rw-r--r--mysql-test/r/show_check.result2
-rw-r--r--mysql-test/t/func_date_add.test15
-rw-r--r--mysql-test/t/func_time.test43
-rw-r--r--mysql-test/t/mysql.test17
-rw-r--r--mysql-test/t/show_check.test5
-rw-r--r--sql-common/my_time.c90
-rw-r--r--sql/field.cc35
-rw-r--r--sql/item_timefunc.cc236
-rw-r--r--sql/item_timefunc.h19
-rw-r--r--sql/time.cc6
18 files changed, 529 insertions, 110 deletions
diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed
index 1d220fdb31d..e6d64f4f6ee 100644
--- a/BitKeeper/etc/collapsed
+++ b/BitKeeper/etc/collapsed
@@ -9,3 +9,4 @@
451ab499rgdjXyOnUDqHu-wBDoS-OQ
45214442pBGT9KuZEGixBH71jTzbOA
45214a07hVsIGwvwa-WrO-jpeaSwVw
+452a92d0-31-8wSzSfZi165fcGcXPA
diff --git a/client/mysql.cc b/client/mysql.cc
index 1d7abbbc38a..f845038d6b6 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -388,6 +388,21 @@ int main(int argc,char *argv[])
else
status.add_to_history=1;
status.exit_status=1;
+
+ {
+ /*
+ The file descriptor-layer may be out-of-sync with the file-number layer,
+ so we make sure that "stdout" is really open. If its file is closed then
+ explicitly close the FD layer.
+ */
+ int stdout_fileno_copy;
+ stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
+ if (stdout_fileno_copy == -1)
+ fclose(stdout);
+ else
+ close(stdout_fileno_copy); /* Clean up dup(). */
+ }
+
load_defaults("my",load_default_groups,&argc,&argv);
defaults_argv=argv;
if (get_options(argc, (char **) argv))
diff --git a/include/my_time.h b/include/my_time.h
index e52ef69475d..adce58b08d7 100644
--- a/include/my_time.h
+++ b/include/my_time.h
@@ -49,6 +49,16 @@ typedef long my_time_t;
#define TIME_NO_ZERO_DATE (TIME_NO_ZERO_IN_DATE*2)
#define TIME_INVALID_DATES (TIME_NO_ZERO_DATE*2)
+#define MYSQL_TIME_WARN_TRUNCATED 1
+#define MYSQL_TIME_WARN_OUT_OF_RANGE 2
+
+/* Limits for the TIME data type */
+#define TIME_MAX_HOUR 838
+#define TIME_MAX_MINUTE 59
+#define TIME_MAX_SECOND 59
+#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + \
+ TIME_MAX_SECOND)
+
enum enum_mysql_timestamp_type
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
uint flags, int *was_cut);
@@ -61,7 +71,9 @@ ulonglong TIME_to_ulonglong(const MYSQL_TIME *time);
my_bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
- int *was_cut);
+ int *warning);
+
+int check_time_range(struct st_mysql_time *time, int *warning);
long calc_daynr(uint year,uint month,uint day);
uint calc_days_in_year(uint year);
diff --git a/myisam/sort.c b/myisam/sort.c
index 00fbfe768dc..74e0d973c9c 100644
--- a/myisam/sort.c
+++ b/myisam/sort.c
@@ -324,7 +324,7 @@ pthread_handler_t thr_find_all_keys(void *arg)
if (info->sort_info->got_error)
goto err;
- if (info->keyinfo->flag && HA_VAR_LENGTH_KEY)
+ if (info->keyinfo->flag & HA_VAR_LENGTH_KEY)
{
info->write_keys=write_keys_varlen;
info->read_to_buffer=read_to_buffer_varlen;
@@ -516,7 +516,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param)
{
if (got_error)
continue;
- if (sinfo->keyinfo->flag && HA_VAR_LENGTH_KEY)
+ if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY)
{
sinfo->write_keys=write_keys_varlen;
sinfo->read_to_buffer=read_to_buffer_varlen;
diff --git a/mysql-test/r/func_date_add.result b/mysql-test/r/func_date_add.result
index 841d13a6ea6..ac5709260fd 100644
--- a/mysql-test/r/func_date_add.result
+++ b/mysql-test/r/func_date_add.result
@@ -71,3 +71,17 @@ NULL
NULL
NULL
drop table t1;
+End of 4.1 tests
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 DAY;
+CAST('2006-09-26' AS DATE) + INTERVAL 1 DAY
+2006-09-27
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 MONTH;
+CAST('2006-09-26' AS DATE) + INTERVAL 1 MONTH
+2006-10-26
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 YEAR;
+CAST('2006-09-26' AS DATE) + INTERVAL 1 YEAR
+2007-09-26
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 WEEK;
+CAST('2006-09-26' AS DATE) + INTERVAL 1 WEEK
+2006-10-03
+End of 5.0 tests
diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result
index 64eb6eefd1a..18908c2c46e 100644
--- a/mysql-test/r/func_sapdb.result
+++ b/mysql-test/r/func_sapdb.result
@@ -107,7 +107,9 @@ subtime("02:01:01.999999", "01:01:01.999999")
01:00:00.000000
select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002");
timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002")
-8807:59:59.999999
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '8807:59:59.999999'
select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002");
timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002")
46:58:57.999999
@@ -219,13 +221,16 @@ SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF(t2, t3) As qqq,
TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
ttt qqq eee rrr
-744:00:00 NULL NULL NULL
-26305:01:02 22:58:58 -22:58:58 NULL
--26305:01:02 -22:58:58 22:58:58 NULL
+838:59:59 22:58:58 -22:58:58 NULL
+-838:59:59 -22:58:58 22:58:58 NULL
NULL 26:02:02 -26:02:02 NULL
00:00:00 -26:02:02 26:02:02 NULL
NULL NULL NULL NULL
NULL NULL NULL NULL
00:00:00 -24:00:00 24:00:00 NULL
+Warnings:
+Warning 1292 Truncated incorrect time value: '26305:01:02'
+Warning 1292 Truncated incorrect time value: '-26305:01:02'
drop table t1, test;
select addtime("-01:01:01.01", "-23:59:59.1") as a;
a
@@ -235,7 +240,9 @@ a
10000
select microsecond(19971231235959.01) as a;
a
-10000
+0
+Warnings:
+Warning 1292 Truncated incorrect time value: '19971231235959.01'
select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a;
a
1997-12-31 00:00:10.090000
diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result
index 25c910a711a..7388ca3c2b8 100644
--- a/mysql-test/r/func_time.result
+++ b/mysql-test/r/func_time.result
@@ -339,7 +339,9 @@ extract(DAY_MINUTE FROM "02 10:11:12")
21011
select extract(DAY_SECOND FROM "225 10:11:12");
extract(DAY_SECOND FROM "225 10:11:12")
-225101112
+8385959
+Warnings:
+Warning 1292 Truncated incorrect time value: '225 10:11:12'
select extract(HOUR FROM "1999-01-02 10:11:12");
extract(HOUR FROM "1999-01-02 10:11:12")
10
@@ -612,7 +614,7 @@ date_add(date,INTERVAL "1 1:1:1" DAY_SECOND)
2003-01-03 01:01:01
select date_add(date,INTERVAL "1" WEEK) from t1;
date_add(date,INTERVAL "1" WEEK)
-2003-01-09 00:00:00
+2003-01-09
select date_add(date,INTERVAL "1" QUARTER) from t1;
date_add(date,INTERVAL "1" QUARTER)
2003-04-02
@@ -621,7 +623,7 @@ timestampadd(MINUTE, 1, date)
2003-01-02 00:01:00
select timestampadd(WEEK, 1, date) from t1;
timestampadd(WEEK, 1, date)
-2003-01-09 00:00:00
+2003-01-09
select timestampadd(SQL_TSI_SECOND, 1, date) from t1;
timestampadd(SQL_TSI_SECOND, 1, date)
2003-01-02 00:00:01
@@ -890,6 +892,93 @@ t1 CREATE TABLE `t1` (
`from_unixtime(1) + 0` double(23,6) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
+SELECT SEC_TO_TIME(3300000);
+SEC_TO_TIME(3300000)
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '3300000'
+SELECT SEC_TO_TIME(3300000)+0;
+SEC_TO_TIME(3300000)+0
+8385959.000000
+Warnings:
+Warning 1292 Truncated incorrect time value: '3300000'
+SELECT SEC_TO_TIME(3600 * 4294967296);
+SEC_TO_TIME(3600 * 4294967296)
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '15461882265600'
+SELECT TIME_TO_SEC('916:40:00');
+TIME_TO_SEC('916:40:00')
+3020399
+Warnings:
+Warning 1292 Truncated incorrect time value: '916:40:00'
+SELECT ADDTIME('500:00:00', '416:40:00');
+ADDTIME('500:00:00', '416:40:00')
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '916:40:00'
+SELECT ADDTIME('916:40:00', '416:40:00');
+ADDTIME('916:40:00', '416:40:00')
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '916:40:00'
+Warning 1292 Truncated incorrect time value: '1255:39:59'
+SELECT SUBTIME('916:40:00', '416:40:00');
+SUBTIME('916:40:00', '416:40:00')
+422:19:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '916:40:00'
+SELECT SUBTIME('-916:40:00', '416:40:00');
+SUBTIME('-916:40:00', '416:40:00')
+-838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '-916:40:00'
+Warning 1292 Truncated incorrect time value: '-1255:39:59'
+SELECT MAKETIME(916,0,0);
+MAKETIME(916,0,0)
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '916:00:00'
+SELECT MAKETIME(4294967296, 0, 0);
+MAKETIME(4294967296, 0, 0)
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '4294967296:00:00'
+SELECT MAKETIME(-4294967296, 0, 0);
+MAKETIME(-4294967296, 0, 0)
+-838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '-4294967296:00:00'
+SELECT MAKETIME(0, 4294967296, 0);
+MAKETIME(0, 4294967296, 0)
+NULL
+SELECT MAKETIME(0, 0, 4294967296);
+MAKETIME(0, 0, 4294967296)
+NULL
+SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
+MAKETIME(CAST(-1 AS UNSIGNED), 0, 0)
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '18446744073709551615:00:00'
+SELECT EXTRACT(HOUR FROM '100000:02:03');
+EXTRACT(HOUR FROM '100000:02:03')
+838
+Warnings:
+Warning 1292 Truncated incorrect time value: '100000:02:03'
+CREATE TABLE t1(f1 TIME);
+INSERT INTO t1 VALUES('916:00:00 a');
+Warnings:
+Warning 1265 Data truncated for column 'f1' at row 1
+Warning 1264 Out of range value adjusted for column 'f1' at row 1
+SELECT * FROM t1;
+f1
+838:59:59
+DROP TABLE t1;
+SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED));
+SEC_TO_TIME(CAST(-1 AS UNSIGNED))
+838:59:59
+Warnings:
+Warning 1292 Truncated incorrect time value: '18446744073709551615'
SET NAMES latin1;
SET character_set_results = NULL;
SHOW VARIABLES LIKE 'character_set_results';
diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result
index 1053b1918fb..86ac7a8d72d 100644
--- a/mysql-test/r/mysql.result
+++ b/mysql-test/r/mysql.result
@@ -149,4 +149,17 @@ ERROR at line 1: USE must be followed by a database name
\\
';
';
+create table t17583 (a int);
+insert into t17583 (a) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+select count(*) from t17583;
+count(*)
+1280
+drop table t17583;
End of 5.0 tests
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 7bdfa78066c..762019be313 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -520,6 +520,7 @@ t1 NULL NULL NULL NULL # # # # NULL NULL NULL NULL NULL NULL NULL NULL Incorrect
show create table t1;
ERROR HY000: Incorrect information in file: './test/t1.frm'
drop table t1;
+End of 4.1 tests
CREATE TABLE txt1(a int);
CREATE TABLE tyt2(a int);
CREATE TABLE urkunde(a int);
@@ -629,3 +630,4 @@ SHOW TABLES FROM no_such_database;
ERROR 42000: Unknown database 'no_such_database'
SHOW COLUMNS FROM no_such_table;
ERROR 42S02: Table 'test.no_such_table' doesn't exist
+End of 5.0 tests
diff --git a/mysql-test/t/func_date_add.test b/mysql-test/t/func_date_add.test
index e01fce30577..b575eeececa 100644
--- a/mysql-test/t/func_date_add.test
+++ b/mysql-test/t/func_date_add.test
@@ -64,4 +64,17 @@ insert into t1 values (date_add('2000-01-04', INTERVAL NULL DAY));
select * from t1;
drop table t1;
-# End of 4.1 tests
+--echo End of 4.1 tests
+
+#
+# Bug#21811
+#
+# Make sure we end up with an appropriate
+# date format (DATE) after addition operation
+#
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 DAY;
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 MONTH;
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 YEAR;
+SELECT CAST('2006-09-26' AS DATE) + INTERVAL 1 WEEK;
+
+--echo End of 5.0 tests
diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test
index 2b3fb86829d..e76afebf778 100644
--- a/mysql-test/t/func_time.test
+++ b/mysql-test/t/func_time.test
@@ -447,6 +447,49 @@ show create table t1;
drop table t1;
#
+# Bug #11655: Wrong time is returning from nested selects - maximum time exists
+#
+# check if SEC_TO_TIME() handles out-of-range values correctly
+SELECT SEC_TO_TIME(3300000);
+SELECT SEC_TO_TIME(3300000)+0;
+SELECT SEC_TO_TIME(3600 * 4294967296);
+
+# check if TIME_TO_SEC() handles out-of-range values correctly
+SELECT TIME_TO_SEC('916:40:00');
+
+# check if ADDTIME() handles out-of-range values correctly
+SELECT ADDTIME('500:00:00', '416:40:00');
+SELECT ADDTIME('916:40:00', '416:40:00');
+
+# check if SUBTIME() handles out-of-range values correctly
+SELECT SUBTIME('916:40:00', '416:40:00');
+SELECT SUBTIME('-916:40:00', '416:40:00');
+
+# check if MAKETIME() handles out-of-range values correctly
+SELECT MAKETIME(916,0,0);
+SELECT MAKETIME(4294967296, 0, 0);
+SELECT MAKETIME(-4294967296, 0, 0);
+SELECT MAKETIME(0, 4294967296, 0);
+SELECT MAKETIME(0, 0, 4294967296);
+SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
+
+# check if EXTRACT() handles out-of-range values correctly
+SELECT EXTRACT(HOUR FROM '100000:02:03');
+
+# check if we get proper warnings if both input string truncation
+# and out-of-range value occur
+CREATE TABLE t1(f1 TIME);
+INSERT INTO t1 VALUES('916:00:00 a');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+#
+# Bug #20927: sec_to_time treats big unsigned as signed
+#
+# check if SEC_TO_TIME() handles BIGINT UNSIGNED values correctly
+SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED));
+
+#
# 21913: DATE_FORMAT() Crashes mysql server if I use it through
# mysql-connector-j driver.
#
diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test
index f3296e6f706..6b2c84f880a 100644
--- a/mysql-test/t/mysql.test
+++ b/mysql-test/t/mysql.test
@@ -153,4 +153,21 @@ drop table t1;
--exec echo "SELECT '\';';" >> $MYSQLTEST_VARDIR/tmp/bug20103.sql
--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/bug20103.sql 2>&1
+#
+# Bug#17583: mysql drops connection when stdout is not writable
+#
+create table t17583 (a int);
+insert into t17583 (a) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+insert into t17583 select a from t17583;
+# Close to the minimal data needed to exercise bug.
+select count(*) from t17583;
+--exec echo "select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; select count(*) from t17583; " |$MYSQL test >&-
+drop table t17583;
+
--echo End of 5.0 tests
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 65a81545c87..07426193dcf 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -400,7 +400,8 @@ show create table t1;
drop table t1;
-# End of 4.1 tests
+--echo End of 4.1 tests
+
#
# BUG 12183 - SHOW OPEN TABLES behavior doesn't match grammar
# First we close all open tables with FLUSH tables and then we open some.
@@ -506,4 +507,4 @@ SHOW TABLES FROM no_such_database;
SHOW COLUMNS FROM no_such_table;
-# End of 5.0 tests.
+--echo End of 5.0 tests
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 93bf23ed284..fb36e5f4e54 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -465,8 +465,10 @@ err:
There may be an optional [.second_part] after seconds
length Length of str
l_time Store result here
- was_cut Set to 1 if value was cut during conversion or to 0
- otherwise.
+ 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.
NOTES
Because of the extra days argument, this function can only
@@ -478,15 +480,16 @@ err:
*/
my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
- int *was_cut)
+ int *warning)
{
- long date[5],value;
+ ulong date[5];
+ ulonglong value;
const char *end=str+length, *end_of_days;
my_bool found_days,found_hours;
uint state;
l_time->neg=0;
- *was_cut= 0;
+ *warning= 0;
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
length--;
if (str != end && *str == '-')
@@ -501,13 +504,16 @@ my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
/* 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,
- (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut);
+ (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
+ {
+ if (was_cut)
+ *warning|= MYSQL_TIME_WARN_TRUNCATED;
return res == MYSQL_TIMESTAMP_ERROR;
- /* We need to restore was_cut flag since str_to_datetime can modify it */
- *was_cut= 0;
+ }
}
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
@@ -587,7 +593,7 @@ fractional:
if (field_length > 0)
value*= (long) log_10_int[field_length];
else if (field_length < 0)
- *was_cut= 1;
+ *warning|= MYSQL_TIME_WARN_TRUNCATED;
date[4]=value;
}
else
@@ -601,10 +607,7 @@ fractional:
((str[1] == '-' || str[1] == '+') &&
(end - str) > 2 &&
my_isdigit(&my_charset_latin1, str[2]))))
- {
- *was_cut= 1;
return 1;
- }
if (internal_format_positions[7] != 255)
{
@@ -623,12 +626,12 @@ fractional:
}
}
- /* Some simple checks */
- if (date[2] >= 60 || date[3] >= 60)
- {
- *was_cut= 1;
+ /* Integer overflow checks */
+ if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
+ date[2] > UINT_MAX || date[3] > UINT_MAX ||
+ date[4] > UINT_MAX)
return 1;
- }
+
l_time->year= 0; /* For protocol::store_time */
l_time->month= 0;
l_time->day= date[0];
@@ -638,6 +641,10 @@ fractional:
l_time->second_part= date[4];
l_time->time_type= MYSQL_TIMESTAMP_TIME;
+ /* Check if the value is valid and fits into TIME range */
+ if (check_time_range(l_time, warning))
+ return 1;
+
/* Check if there is garbage at end of the TIME specification */
if (str != end)
{
@@ -645,7 +652,7 @@ fractional:
{
if (!my_isspace(&my_charset_latin1,*str))
{
- *was_cut= 1;
+ *warning|= MYSQL_TIME_WARN_TRUNCATED;
break;
}
} while (++str != end);
@@ -655,6 +662,47 @@ fractional:
/*
+ Check 'time' value to lie in the TIME range
+
+ SYNOPSIS:
+ check_time_range()
+ time pointer to TIME value
+ warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
+
+ DESCRIPTION
+ If the time value lies outside of the range [-838:59:59, 838:59:59],
+ set it to the closest endpoint of the range and set
+ MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
+
+ RETURN
+ 0 time value is valid, but was possibly truncated
+ 1 time value is invalid
+*/
+
+int check_time_range(struct st_mysql_time *time, int *warning)
+{
+ longlong hour;
+
+ if (time->minute >= 60 || time->second >= 60)
+ return 1;
+
+ hour= time->hour + (24*time->day);
+ if (hour <= TIME_MAX_HOUR &&
+ (hour != TIME_MAX_HOUR || time->minute != TIME_MAX_MINUTE ||
+ time->second != TIME_MAX_SECOND || !time->second_part))
+ return 0;
+
+ time->day= 0;
+ time->hour= TIME_MAX_HOUR;
+ time->minute= TIME_MAX_MINUTE;
+ time->second= TIME_MAX_SECOND;
+ time->second_part= 0;
+ *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ return 0;
+}
+
+
+/*
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
SYNOPSIS
@@ -840,7 +888,7 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
int my_time_to_str(const MYSQL_TIME *l_time, char *to)
{
uint extra_hours= 0;
- return my_sprintf(to, (to, "%s%02d:%02d:%02d",
+ return my_sprintf(to, (to, "%s%02u:%02u:%02u",
(l_time->neg ? "-" : ""),
extra_hours+ l_time->hour,
l_time->minute,
@@ -849,7 +897,7 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to)
int my_date_to_str(const MYSQL_TIME *l_time, char *to)
{
- return my_sprintf(to, (to, "%04d-%02d-%02d",
+ return my_sprintf(to, (to, "%04u-%02u-%02u",
l_time->year,
l_time->month,
l_time->day));
@@ -857,7 +905,7 @@ int my_date_to_str(const MYSQL_TIME *l_time, char *to)
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to)
{
- return my_sprintf(to, (to, "%04d-%02d-%02d %02d:%02d:%02d",
+ return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u",
l_time->year,
l_time->month,
l_time->day,
diff --git a/sql/field.cc b/sql/field.cc
index 9b512fc6d4b..ec97bc92d24 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4762,9 +4762,10 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
{
TIME ltime;
long tmp;
- int error;
+ int error= 0;
+ int warning;
- if (str_to_time(from, len, &ltime, &error))
+ if (str_to_time(from, len, &ltime, &warning))
{
tmp=0L;
error= 2;
@@ -4773,29 +4774,27 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
}
else
{
- if (error)
+ if (warning & MYSQL_TIME_WARN_TRUNCATED)
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED,
from, len, MYSQL_TIMESTAMP_TIME, 1);
-
- if (ltime.month)
- ltime.day=0;
- tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
- if (tmp > 8385959)
+ if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE)
{
- tmp=8385959;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
from, len, MYSQL_TIMESTAMP_TIME, !error);
error= 1;
}
+ if (ltime.month)
+ ltime.day=0;
+ tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
if (error > 1)
error= 2;
}
if (ltime.neg)
tmp= -tmp;
- error |= Field_time::store((longlong) tmp, FALSE);
+ int3store(ptr,tmp);
return error;
}
@@ -4814,16 +4813,16 @@ int Field_time::store(double nr)
{
long tmp;
int error= 0;
- if (nr > 8385959.0)
+ if (nr > (double)TIME_MAX_VALUE)
{
- tmp=8385959L;
+ tmp= TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
error= 1;
}
- else if (nr < -8385959.0)
+ else if (nr < (double)-TIME_MAX_VALUE)
{
- tmp= -8385959L;
+ tmp= -TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
error= 1;
@@ -4851,17 +4850,17 @@ int Field_time::store(longlong nr, bool unsigned_val)
{
long tmp;
int error= 0;
- if (nr < (longlong) -8385959L && !unsigned_val)
+ if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val)
{
- tmp= -8385959L;
+ tmp= -TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr,
MYSQL_TIMESTAMP_TIME, 1);
error= 1;
}
- else if (nr > (longlong) 8385959 || nr < 0 && unsigned_val)
+ else if (nr > (longlong) TIME_MAX_VALUE || nr < 0 && unsigned_val)
{
- tmp=8385959L;
+ tmp= TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr,
MYSQL_TIMESTAMP_TIME, 1);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 48d6458bd88..be924bd3585 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -96,6 +96,125 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
/*
+ Wrapper over make_datetime() with validation of the input TIME value
+
+ NOTE
+ see make_datetime() for more information
+
+ RETURN
+ 1 if there was an error during converion
+ 0 otherwise
+*/
+
+static bool make_datetime_with_warn(date_time_format_types format, TIME *ltime,
+ String *str)
+{
+ int warning= 0;
+ bool rc;
+
+ if (make_datetime(format, ltime, str))
+ return 1;
+ if (check_time_range(ltime, &warning))
+ return 1;
+ if (!warning)
+ return 0;
+
+ make_truncated_value_warning(current_thd, str->ptr(), str->length(),
+ MYSQL_TIMESTAMP_TIME, NullS);
+ return make_datetime(format, ltime, str);
+}
+
+
+/*
+ Wrapper over make_time() with validation of the input TIME value
+
+ NOTE
+ see make_time() for more info
+
+ RETURN
+ 1 if there was an error during conversion
+ 0 otherwise
+*/
+
+static bool make_time_with_warn(const DATE_TIME_FORMAT *format,
+ TIME *l_time, String *str)
+{
+ int warning= 0;
+ make_time(format, l_time, str);
+ if (check_time_range(l_time, &warning))
+ return 1;
+ if (warning)
+ {
+ make_truncated_value_warning(current_thd, str->ptr(), str->length(),
+ MYSQL_TIMESTAMP_TIME, NullS);
+ make_time(format, l_time, str);
+ }
+
+ return 0;
+}
+
+
+/*
+ Convert seconds to TIME value with overflow checking
+
+ SYNOPSIS:
+ sec_to_time()
+ seconds number of seconds
+ unsigned_flag 1, if 'seconds' is unsigned, 0, otherwise
+ ltime output TIME value
+
+ DESCRIPTION
+ If the 'seconds' argument is inside TIME data range, convert it to a
+ corresponding value.
+ Otherwise, truncate the resulting value to the nearest endpoint, and
+ produce a warning message.
+
+ RETURN
+ 1 if the value was truncated during conversion
+ 0 otherwise
+*/
+
+static bool sec_to_time(longlong seconds, bool unsigned_flag, TIME *ltime)
+{
+ uint sec;
+
+ bzero((char *)ltime, sizeof(*ltime));
+
+ if (seconds < 0)
+ {
+ if (unsigned_flag)
+ goto overflow;
+ ltime->neg= 1;
+ if (seconds < -3020399)
+ goto overflow;
+ seconds= -seconds;
+ }
+ else if (seconds > 3020399)
+ goto overflow;
+
+ sec= (uint) ((ulonglong) seconds % 3600);
+ ltime->hour= (uint) (seconds/3600);
+ ltime->minute= sec/60;
+ ltime->second= sec % 60;
+
+ return 0;
+
+overflow:
+ ltime->hour= TIME_MAX_HOUR;
+ ltime->minute= TIME_MAX_MINUTE;
+ ltime->second= TIME_MAX_SECOND;
+
+ char buf[22];
+ int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10)
+ - buf);
+ make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME,
+ NullS);
+
+ return 1;
+}
+
+
+/*
Date formats corresponding to compound %r and %T conversion specifiers
Note: We should init at least first element of "positions" array
@@ -1570,8 +1689,6 @@ int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions)
String *Item_func_sec_to_time::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- longlong seconds=(longlong) args[0]->val_int();
- uint sec;
TIME ltime;
if ((null_value=args[0]->null_value) || str->alloc(19))
@@ -1580,19 +1697,8 @@ String *Item_func_sec_to_time::val_str(String *str)
return (String*) 0;
}
- ltime.neg= 0;
- if (seconds < 0)
- {
- seconds= -seconds;
- ltime.neg= 1;
- }
-
- sec= (uint) ((ulonglong) seconds % 3600);
- ltime.day= 0;
- ltime.hour= (uint) (seconds/3600);
- ltime.minute= sec/60;
- ltime.second= sec % 60;
-
+ sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
+
make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
return str;
}
@@ -1601,16 +1707,15 @@ String *Item_func_sec_to_time::val_str(String *str)
longlong Item_func_sec_to_time::val_int()
{
DBUG_ASSERT(fixed == 1);
- longlong seconds=args[0]->val_int();
- longlong sign=1;
+ TIME ltime;
+
if ((null_value=args[0]->null_value))
return 0;
- if (seconds < 0)
- {
- seconds= -seconds;
- sign= -1;
- }
- return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60));
+
+ sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
+
+ return (ltime.neg ? -1 : 1) *
+ ((ltime.hour)*10000 + ltime.minute*100 + ltime.second);
}
@@ -2151,11 +2256,15 @@ bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const
(date_sub_interval == other->date_sub_interval));
}
+/*
+ 'interval_names' reflects the order of the enumeration interval_type.
+ See item_timefunc.h
+ */
static const char *interval_names[]=
{
- "year", "quarter", "month", "day", "hour",
- "minute", "week", "second", "microsecond",
+ "year", "quarter", "month", "week", "day",
+ "hour", "minute", "second", "microsecond",
"year_month", "day_hour", "day_minute",
"day_second", "hour_minute", "hour_second",
"minute_second", "day_microsecond",
@@ -2692,7 +2801,9 @@ String *Item_func_add_time::val_str(String *str)
}
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
-
+
+ bzero((char *)&l_time3, sizeof(l_time3));
+
l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
&seconds, &microseconds);
@@ -2721,9 +2832,9 @@ String *Item_func_add_time::val_str(String *str)
}
l_time3.hour+= days*24;
- if (!make_datetime(l_time1.second_part || l_time2.second_part ?
- TIME_MICROSECOND : TIME_ONLY,
- &l_time3, str))
+ if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
+ TIME_MICROSECOND : TIME_ONLY,
+ &l_time3, str))
return str;
null_date:
@@ -2778,6 +2889,8 @@ String *Item_func_timediff::val_str(String *str)
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
+ bzero((char *)&l_time3, sizeof(l_time3));
+
l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign,
&seconds, &microseconds);
@@ -2791,9 +2904,9 @@ String *Item_func_timediff::val_str(String *str)
calc_time_from_sec(&l_time3, (long) seconds, microseconds);
- if (!make_datetime(l_time1.second_part || l_time2.second_part ?
- TIME_MICROSECOND : TIME_ONLY,
- &l_time3, str))
+ if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
+ TIME_MICROSECOND : TIME_ONLY,
+ &l_time3, str))
return str;
null_date:
@@ -2811,29 +2924,58 @@ String *Item_func_maketime::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
TIME ltime;
+ bool overflow= 0;
- long hour= (long) args[0]->val_int();
- long minute= (long) args[1]->val_int();
- long second= (long) args[2]->val_int();
+ longlong hour= args[0]->val_int();
+ longlong minute= args[1]->val_int();
+ longlong second= args[2]->val_int();
if ((null_value=(args[0]->null_value ||
- args[1]->null_value ||
- args[2]->null_value ||
- minute > 59 || minute < 0 ||
- second > 59 || second < 0 ||
- str->alloc(19))))
+ args[1]->null_value ||
+ args[2]->null_value ||
+ minute < 0 || minute > 59 ||
+ second < 0 || second > 59 ||
+ str->alloc(19))))
return 0;
+ bzero((char *)&ltime, sizeof(ltime));
ltime.neg= 0;
+
+ /* Check for integer overflows */
if (hour < 0)
{
- ltime.neg= 1;
- hour= -hour;
+ if (args[0]->unsigned_flag)
+ overflow= 1;
+ else
+ ltime.neg= 1;
+ }
+ if (-hour > UINT_MAX || hour > UINT_MAX)
+ overflow= 1;
+
+ if (!overflow)
+ {
+ ltime.hour= (uint) ((hour < 0 ? -hour : hour));
+ ltime.minute= (uint) minute;
+ ltime.second= (uint) second;
+ }
+ else
+ {
+ ltime.hour= TIME_MAX_HOUR;
+ ltime.minute= TIME_MAX_MINUTE;
+ ltime.second= TIME_MAX_SECOND;
+ char buf[28];
+ char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
+ int len = (int)(ptr - buf) +
+ my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second));
+ make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME,
+ NullS);
+ }
+
+ if (make_time_with_warn((DATE_TIME_FORMAT *) 0, &ltime, str))
+ {
+ null_value= 1;
+ return 0;
}
- ltime.hour= (ulong) hour;
- ltime.minute= (ulong) minute;
- ltime.second= (ulong) second;
- make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
return str;
}
@@ -3189,7 +3331,7 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date)
goto null_date;
null_value= 0;
- bzero((char*) ltime, sizeof(ltime));
+ bzero((char*) ltime, sizeof(*ltime));
date_time_format.format.str= (char*) format->ptr();
date_time_format.format.length= format->length();
if (extract_date_time(&date_time_format, val->ptr(), val->length(),
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index d5d3efeeab4..e79c62e6ffb 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -630,18 +630,21 @@ public:
};
/*
- The following must be sorted so that simple intervals comes first.
- (get_interval_value() depends on this)
+ 'interval_type' must be sorted so that simple intervals comes first,
+ ie year, quarter, month, week, day, hour, etc. The order based on
+ interval size is also important and the intervals should be kept in a
+ large to smaller order. (get_interval_value() depends on this)
*/
enum interval_type
{
- INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR,
- INTERVAL_MINUTE, INTERVAL_WEEK, INTERVAL_SECOND, INTERVAL_MICROSECOND ,
- INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
- INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
- INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
- INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND
+ INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_WEEK,
+ INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND,
+ INTERVAL_MICROSECOND, INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR,
+ INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE,
+ INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND,
+ INTERVAL_HOUR_MICROSECOND, INTERVAL_MINUTE_MICROSECOND,
+ INTERVAL_SECOND_MICROSECOND
};
class Item_date_add_interval :public Item_date_func
diff --git a/sql/time.cc b/sql/time.cc
index 5069031081d..c71ea08fb77 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -254,9 +254,9 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *in_dst_time_gap)
bool
str_to_time_with_warn(const char *str, uint length, TIME *l_time)
{
- int was_cut;
- bool ret_val= str_to_time(str, length, l_time, &was_cut);
- if (was_cut)
+ int warning;
+ bool ret_val= str_to_time(str, length, l_time, &warning);
+ if (ret_val || warning)
make_truncated_value_warning(current_thd, str, length,
MYSQL_TIMESTAMP_TIME, NullS);
return ret_val;