From: Alexander Barkov Date: November 18 2011 1:27pm Subject: bzr push into mysql-trunk branch (alexander.barkov:3599 to 3600) List-Archive: http://lists.mysql.com/commits/142070 Message-Id: <201111181327.pAIDRVU8023365@bar.myoffice.izhnet.ru> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3600 Alexander Barkov 2011-11-18 [merge] Merging latest bug fixes from mysql-trunk-wl946 modified: mysql-test/r/timezone2.result mysql-test/r/type_temporal_fractional.result mysql-test/t/timezone2.test mysql-test/t/type_temporal_fractional.test sql/field.cc sql/field.h sql/item.cc sql/item.h sql/item_cmpfunc.cc sql/item_timefunc.cc sql/sql_time.cc sql/sql_time.h unittest/gunit/field-t.cc 3599 Mayank Prasad 2011-11-18 WL#5885 details: - Added test case for IS_ROUTINES/PARAMETERS tables to test DATETIME PRECISION for values 1-6. modified: mysql-test/r/information_schema_parameters.result mysql-test/r/information_schema_routines.result mysql-test/t/information_schema_parameters.test mysql-test/t/information_schema_routines.test === modified file 'mysql-test/r/timezone2.result' --- a/mysql-test/r/timezone2.result 2010-08-06 19:29:37 +0000 +++ b/mysql-test/r/timezone2.result 2011-11-18 09:52:00 +0000 @@ -309,3 +309,33 @@ CONVERT_TZ(1, 1, a) NULL DROP TABLE t1; End of 5.1 tests +# +# Start of 5.6 tests +# +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP, b VARCHAR(30)); +INSERT INTO t1 VALUES +('2003-03-30 01:59:59', 'Before the gap'), +('2003-03-30 02:30:00', 'Inside the gap'), +('2003-03-30 03:00:00', 'After the gap'); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'a' at row 2 +SELECT a, UNIX_TIMESTAMP(a), b FROM t1; +a UNIX_TIMESTAMP(a) b +2003-03-30 01:59:59 1048978799 Before the gap +2003-03-30 03:00:00 1048978800 Inside the gap +2003-03-30 03:00:00 1048978800 After the gap +DROP TABLE t1; +SELECT UNIX_TIMESTAMP('2003-03-30 01:59:59'), 'Before the gap' AS b; +UNIX_TIMESTAMP('2003-03-30 01:59:59') b +1048978799 Before the gap +SELECT UNIX_TIMESTAMP('2003-03-30 02:30:00'), 'Inside the gap' AS b; +UNIX_TIMESTAMP('2003-03-30 02:30:00') b +1048978800 Inside the gap +SELECT UNIX_TIMESTAMP('2003-03-30 03:00:00'), 'After the gap' AS b; +UNIX_TIMESTAMP('2003-03-30 03:00:00') b +1048978800 After the gap +SET time_zone=DEFAULT; +# +# End of 5.6 tests +# === modified file 'mysql-test/r/type_temporal_fractional.result' --- a/mysql-test/r/type_temporal_fractional.result 2011-11-17 13:41:28 +0000 +++ b/mysql-test/r/type_temporal_fractional.result 2011-11-18 13:26:35 +0000 @@ -16647,3 +16647,151 @@ Warning 1292 Truncated incorrect time va Warning 1292 Truncated incorrect time value: '2002-11-24' DROP VIEW v1; DROP TABLE t1; +# +# Bug#13401354 ITEM->FIELD_TYPE() == MYSQL_TYPE_TIME || ITEM->FIELD_TYPE() == MYSQL_TYPE_NULL +# +CREATE TABLE t1 (col_time_not_null TIME NOT NULL); +SELECT * FROM t1 +WHERE col_time_not_null = DATE(NULL) AND col_time_not_null = TIME(NULL); +col_time_not_null +DROP TABLE t1; +CREATE TABLE t1 ( +col_date DATE, +col_time_not_null TIME not null, +pk TIMESTAMP(6), +col_timestamp_3_not_null_key TIMESTAMP(3) NOT NULL, +col_date_not_null DATE NOT NULL, +col_time_3_key TIME(3), +col_timestamp_6_key TIMESTAMP(6) NOT NULL, +col_datetime DATETIME, +col_datetime_6 DATETIME(6), +col_timestamp_3_key TIMESTAMP(3) NOT NULL, +col_time_6_not_null TIME(6) NOT NULL, +col_time_6 TIME(6), +col_time_6_not_null_key TIME(6) NOT NULL, +col_datetime_not_null DATETIME NOT NULL, +col_datetime_3_not_null_key DATETIME(3) NOT NULL, +KEY (col_datetime_3_not_null_key)); +SELECT col_time_6 AS c1, NOW() AS c2 FROM t1 +WHERE +col_datetime_not_null BETWEEN SUBTIME('0000-00-00','00:00:00') AND col_date_not_null +AND +col_time_not_null=STR_TO_DATE(DATE_FORMAT(TIME('00:00:00.000000'),CONCAT_WS('-','%s','%k' )),CONCAT_WS(':','%i','%M')) +AND +col_date BETWEEN UNIX_TIMESTAMP('0000-00-00 00:00:00') AND col_timestamp_3_key +AND +col_datetime_6 NOT IN (col_time_6_not_null,'0000-00-00') +AND +col_time_not_null = TIMEDIFF(CURDATE(),'00:00:00.000000') +AND +col_time_3_key NOT IN (col_timestamp_6_key,pk) +AND +col_timestamp_3_not_null_key < col_datetime; +c1 c2 +Warnings: +Warning 1411 Incorrect datetime value: '00-0' for function str_to_date +Warning 1411 Incorrect datetime value: '00-0' for function str_to_date +DROP TABLE t1; +# +# Bug#13399082 - ASSERTION `MON > 0 && MON < 13' FAILED IN TZTIME.CC | SEC_SINCE_EPOCH +# +SET TIME_ZONE='+02:00'; +SELECT UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')); +UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')) +0 +SET TIME_ZONE=DEFAULT; +# This should return NULL +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES(NULL); +SELECT UNIX_TIMESTAMP(a) FROM t1; +UNIX_TIMESTAMP(a) +NULL +DROP TABLE t1; +SELECT UNIX_TIMESTAMP(NULL); +UNIX_TIMESTAMP(NULL) +NULL +SELECT UNIX_TIMESTAMP(DATE(NULL)); +UNIX_TIMESTAMP(DATE(NULL)) +NULL +SELECT UNIX_TIMESTAMP(TIME(NULL)); +UNIX_TIMESTAMP(TIME(NULL)) +NULL +SELECT UNIX_TIMESTAMP(TIMESTAMP(NULL)); +UNIX_TIMESTAMP(TIMESTAMP(NULL)) +NULL +# This should return 0 +CREATE TABLE t1 (a VARCHAR(30)); +INSERT INTO t1 VALUES +('0000-01-01 00:00:00'),('2001-00-01 00:00:01'),('2001-01-00 00:00:00'), +('0000-00-00 00:00:01'),('0000-00-00 00:00:00.1'); +SELECT UNIX_TIMESTAMP(a) FROM t1; +UNIX_TIMESTAMP(a) +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +DROP TABLE t1; +SELECT UNIX_TIMESTAMP('0000-01-01 00:00:00'); +UNIX_TIMESTAMP('0000-01-01 00:00:00') +0 +SELECT UNIX_TIMESTAMP('2001-00-01 00:00:00'); +UNIX_TIMESTAMP('2001-00-01 00:00:00') +0 +SELECT UNIX_TIMESTAMP('2001-01-00 00:00:00'); +UNIX_TIMESTAMP('2001-01-00 00:00:00') +0 +SELECT UNIX_TIMESTAMP('0000-00-00 10:10:20'); +UNIX_TIMESTAMP('0000-00-00 10:10:20') +0 +SELECT UNIX_TIMESTAMP('0000-00-00 00:00:00.1'); +UNIX_TIMESTAMP('0000-00-00 00:00:00.1') +0.0 +SELECT UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 10:10:20'); +UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 10:10:20') +0 +SELECT UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 00:00:00.1'); +UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 00:00:00.1') +0.0 +SELECT UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 10:10:20')); +UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 10:10:20')) +0 +SELECT UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 00:00:00.1')); +UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 00:00:00.1')) +0.0 +# +# Bug#13354387 - CRASH IN IN MY_DECIMAL::OPERATOR FOR VIEW AND FUNCTION UNIX_TIMESTAMP +# +SET timestamp=UNIX_TIMESTAMP('2011-11-18 14:36:00'); +CREATE TABLE t1 ( +pk time(2) NOT NULL DEFAULT '00:00:00.00', +col_timestamp_3_not_null timestamp(3) NULL DEFAULT '0000-00-00 00:00:00.000', +col_datetime_1_key datetime(1) DEFAULT NULL, +col_datetime_6_not_null_key datetime(6) NOT NULL, +col_datetime_2_not_null datetime(2) NOT NULL, +PRIMARY KEY (pk), +KEY col_datetime_1_key (col_datetime_1_key), +KEY col_datetime_6_not_null_key (col_datetime_6_not_null_key)) +ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES ('19:14:35.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','2000-08-28 07:34:20.051690','2009-09-20 07:32:39.06'); +INSERT INTO t1 VALUES ('19:14:37.36','2005-07-04 08:03:50.051','0000-00-00 00:00:00.0','0000-00-00 00:00:00.000000','0000-00-00 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:39.36','0000-00-00 00:00:00.000',NULL,'2005-10-11 07:24:45.059064','2009-11-19 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:41.36','2005-11-22 21:00:00.000',NULL,'0000-00-00 00:00:00.000000','0000-00-00 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:43.36','2009-11-06 21:00:00.000','2007-07-03 11:13:08.0','2002-09-02 00:17:37.037583','2003-09-25 09:29:41.00'); +INSERT INTO t1 VALUES ('19:14:36.36','2009-08-15 05:43:18.029','2009-01-18 00:00:00.0','0000-00-00 00:00:00.000000','2007-07-20 08:31:37.02'); +INSERT INTO t1 VALUES ('19:14:38.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','0000-00-00 00:00:00.000000','2004-08-16 05:11:47.04'); +INSERT INTO t1 VALUES ('19:14:40.36','2000-04-16 21:00:00.000',NULL,'2004-04-27 00:00:00.000000','2009-06-03 00:18:49.04'); +INSERT INTO t1 VALUES ('19:14:42.36','2009-01-18 20:46:41.035','2003-03-23 11:37:04.0','0000-00-00 00:00:00.000000','2009-12-26 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:44.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','2005-09-14 15:13:53.062460','0000-00-00 00:00:00.00'); +CREATE OR REPLACE VIEW v1 AS SELECT * FROM t1; +SELECT UNIX_TIMESTAMP(col_datetime_2_not_null) AS c1 +FROM v1 +WHERE col_datetime_6_not_null_key > col_datetime_1_key +AND col_timestamp_3_not_null IS NOT NULL +ORDER BY 1; +c1 +0.00 +1253421159.06 +DROP VIEW v1; +DROP TABLE t1; +SET timestamp=DEFAULT; === modified file 'mysql-test/t/timezone2.test' --- a/mysql-test/t/timezone2.test 2010-08-06 19:29:37 +0000 +++ b/mysql-test/t/timezone2.test 2011-11-18 09:52:00 +0000 @@ -284,3 +284,25 @@ SELECT CONVERT_TZ(1, 1, a) FROM t1; DROP TABLE t1; --echo End of 5.1 tests + +--echo # +--echo # Start of 5.6 tests +--echo # +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP, b VARCHAR(30)); +# Testing values around winter/summer time change +# Expect non-zero UNIX_TIMESTAMP value for all of them +INSERT INTO t1 VALUES +('2003-03-30 01:59:59', 'Before the gap'), +('2003-03-30 02:30:00', 'Inside the gap'), +('2003-03-30 03:00:00', 'After the gap'); +SELECT a, UNIX_TIMESTAMP(a), b FROM t1; +DROP TABLE t1; +SELECT UNIX_TIMESTAMP('2003-03-30 01:59:59'), 'Before the gap' AS b; +SELECT UNIX_TIMESTAMP('2003-03-30 02:30:00'), 'Inside the gap' AS b; +SELECT UNIX_TIMESTAMP('2003-03-30 03:00:00'), 'After the gap' AS b; +SET time_zone=DEFAULT; + +--echo # +--echo # End of 5.6 tests +--echo # === modified file 'mysql-test/t/type_temporal_fractional.test' --- a/mysql-test/t/type_temporal_fractional.test 2011-11-17 13:41:28 +0000 +++ b/mysql-test/t/type_temporal_fractional.test 2011-11-18 13:26:35 +0000 @@ -7220,6 +7220,118 @@ DROP VIEW v1; DROP TABLE t1; +--echo # +--echo # Bug#13401354 ITEM->FIELD_TYPE() == MYSQL_TYPE_TIME || ITEM->FIELD_TYPE() == MYSQL_TYPE_NULL +--echo # +CREATE TABLE t1 (col_time_not_null TIME NOT NULL); +SELECT * FROM t1 +WHERE col_time_not_null = DATE(NULL) AND col_time_not_null = TIME(NULL); +DROP TABLE t1; +CREATE TABLE t1 ( + col_date DATE, + col_time_not_null TIME not null, + pk TIMESTAMP(6), + col_timestamp_3_not_null_key TIMESTAMP(3) NOT NULL, + col_date_not_null DATE NOT NULL, + col_time_3_key TIME(3), + col_timestamp_6_key TIMESTAMP(6) NOT NULL, + col_datetime DATETIME, + col_datetime_6 DATETIME(6), + col_timestamp_3_key TIMESTAMP(3) NOT NULL, + col_time_6_not_null TIME(6) NOT NULL, + col_time_6 TIME(6), + col_time_6_not_null_key TIME(6) NOT NULL, + col_datetime_not_null DATETIME NOT NULL, + col_datetime_3_not_null_key DATETIME(3) NOT NULL, + KEY (col_datetime_3_not_null_key)); +SELECT col_time_6 AS c1, NOW() AS c2 FROM t1 +WHERE + col_datetime_not_null BETWEEN SUBTIME('0000-00-00','00:00:00') AND col_date_not_null +AND + col_time_not_null=STR_TO_DATE(DATE_FORMAT(TIME('00:00:00.000000'),CONCAT_WS('-','%s','%k' )),CONCAT_WS(':','%i','%M')) +AND + col_date BETWEEN UNIX_TIMESTAMP('0000-00-00 00:00:00') AND col_timestamp_3_key +AND + col_datetime_6 NOT IN (col_time_6_not_null,'0000-00-00') +AND + col_time_not_null = TIMEDIFF(CURDATE(),'00:00:00.000000') +AND + col_time_3_key NOT IN (col_timestamp_6_key,pk) +AND + col_timestamp_3_not_null_key < col_datetime; +DROP TABLE t1; + + +--echo # +--echo # Bug#13399082 - ASSERTION `MON > 0 && MON < 13' FAILED IN TZTIME.CC | SEC_SINCE_EPOCH +--echo # +SET TIME_ZONE='+02:00'; +SELECT UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')); +SET TIME_ZONE=DEFAULT; + +--echo # This should return NULL +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES(NULL); +SELECT UNIX_TIMESTAMP(a) FROM t1; +DROP TABLE t1; +SELECT UNIX_TIMESTAMP(NULL); +SELECT UNIX_TIMESTAMP(DATE(NULL)); +SELECT UNIX_TIMESTAMP(TIME(NULL)); +SELECT UNIX_TIMESTAMP(TIMESTAMP(NULL)); + +--echo # This should return 0 +CREATE TABLE t1 (a VARCHAR(30)); +INSERT INTO t1 VALUES +('0000-01-01 00:00:00'),('2001-00-01 00:00:01'),('2001-01-00 00:00:00'), +('0000-00-00 00:00:01'),('0000-00-00 00:00:00.1'); +SELECT UNIX_TIMESTAMP(a) FROM t1; +DROP TABLE t1; +SELECT UNIX_TIMESTAMP('0000-01-01 00:00:00'); +SELECT UNIX_TIMESTAMP('2001-00-01 00:00:00'); +SELECT UNIX_TIMESTAMP('2001-01-00 00:00:00'); +SELECT UNIX_TIMESTAMP('0000-00-00 10:10:20'); +SELECT UNIX_TIMESTAMP('0000-00-00 00:00:00.1'); +SELECT UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 10:10:20'); +SELECT UNIX_TIMESTAMP(TIMESTAMP'0000-00-00 00:00:00.1'); +SELECT UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 10:10:20')); +SELECT UNIX_TIMESTAMP(TIMESTAMP('0000-00-00 00:00:00.1')); + + +--echo # +--echo # Bug#13354387 - CRASH IN IN MY_DECIMAL::OPERATOR FOR VIEW AND FUNCTION UNIX_TIMESTAMP +--echo # +SET timestamp=UNIX_TIMESTAMP('2011-11-18 14:36:00'); +CREATE TABLE t1 ( + pk time(2) NOT NULL DEFAULT '00:00:00.00', + col_timestamp_3_not_null timestamp(3) NULL DEFAULT '0000-00-00 00:00:00.000', + col_datetime_1_key datetime(1) DEFAULT NULL, + col_datetime_6_not_null_key datetime(6) NOT NULL, + col_datetime_2_not_null datetime(2) NOT NULL, + PRIMARY KEY (pk), + KEY col_datetime_1_key (col_datetime_1_key), + KEY col_datetime_6_not_null_key (col_datetime_6_not_null_key)) +ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES ('19:14:35.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','2000-08-28 07:34:20.051690','2009-09-20 07:32:39.06'); +INSERT INTO t1 VALUES ('19:14:37.36','2005-07-04 08:03:50.051','0000-00-00 00:00:00.0','0000-00-00 00:00:00.000000','0000-00-00 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:39.36','0000-00-00 00:00:00.000',NULL,'2005-10-11 07:24:45.059064','2009-11-19 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:41.36','2005-11-22 21:00:00.000',NULL,'0000-00-00 00:00:00.000000','0000-00-00 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:43.36','2009-11-06 21:00:00.000','2007-07-03 11:13:08.0','2002-09-02 00:17:37.037583','2003-09-25 09:29:41.00'); +INSERT INTO t1 VALUES ('19:14:36.36','2009-08-15 05:43:18.029','2009-01-18 00:00:00.0','0000-00-00 00:00:00.000000','2007-07-20 08:31:37.02'); +INSERT INTO t1 VALUES ('19:14:38.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','0000-00-00 00:00:00.000000','2004-08-16 05:11:47.04'); +INSERT INTO t1 VALUES ('19:14:40.36','2000-04-16 21:00:00.000',NULL,'2004-04-27 00:00:00.000000','2009-06-03 00:18:49.04'); +INSERT INTO t1 VALUES ('19:14:42.36','2009-01-18 20:46:41.035','2003-03-23 11:37:04.0','0000-00-00 00:00:00.000000','2009-12-26 00:00:00.00'); +INSERT INTO t1 VALUES ('19:14:44.36','0000-00-00 00:00:00.000','0000-00-00 00:00:00.0','2005-09-14 15:13:53.062460','0000-00-00 00:00:00.00'); +CREATE OR REPLACE VIEW v1 AS SELECT * FROM t1; +SELECT UNIX_TIMESTAMP(col_datetime_2_not_null) AS c1 +FROM v1 +WHERE col_datetime_6_not_null_key > col_datetime_1_key +AND col_timestamp_3_not_null IS NOT NULL +ORDER BY 1; +DROP VIEW v1; +DROP TABLE t1; +SET timestamp=DEFAULT; + + ## TS-TODO: SELECT CAST('00:00:00' AS DATETIME) -> should it use curdate? ## TS-TODO: reuse count_datetime_length()/count_string_result_length() === modified file 'sql/field.cc' --- a/sql/field.cc 2011-11-17 13:41:28 +0000 +++ b/sql/field.cc 2011-11-18 13:26:35 +0000 @@ -1797,15 +1797,12 @@ bool Field::get_time(MYSQL_TIME *ltime) } -bool Field::get_timestamp(struct timeval *tm) +bool Field::get_timestamp(struct timeval *tm, int *warnings) { MYSQL_TIME ltime; - if (get_date(<ime, TIME_FUZZY_DATE)) - return true; - my_bool not_used; - tm->tv_sec= TIME_to_timestamp(current_thd, <ime, ¬_used); - tm->tv_usec= ltime.second_part; - return false; + DBUG_ASSERT(!is_null()); + return get_date(<ime, TIME_FUZZY_DATE) || + datetime_to_timeval(current_thd, <ime, tm, warnings); } @@ -5159,30 +5156,14 @@ Field_temporal_with_date_and_time::conve struct timeval *tm, int *warnings) { - my_bool in_dst_time_gap; - /* Only convert a correct date (not a zero date) */ - if (!ltime->month) + /* + No needs to do check_date(TIME_NO_ZERO_IN_DATE), + because it has been done earlier in + store_time(), number_to_datetime() or str_to_datetime(). + */ + if (datetime_with_no_zero_in_date_to_timeval(thd, ltime, tm, warnings)) { tm->tv_sec= tm->tv_usec= 0; - if (ltime->second_part) - { - /* Don't allow zero timestamp with microseconds */ - *warnings|= MYSQL_TIME_WARN_TRUNCATED; - return true; - } - return false; - } - - tm->tv_usec= ltime->second_part; - - if (!(tm->tv_sec= TIME_to_timestamp(thd, ltime, &in_dst_time_gap))) - { - *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; - return true; - } - else if (in_dst_time_gap) - { - *warnings|= MYSQL_TIME_WARN_INVALID_TIMESTAMP; return true; } return false; @@ -5361,7 +5342,7 @@ bool Field_timestamp::get_date_internal( /** Get TIMESTAMP field value as seconds since begging of Unix Epoch */ -bool Field_timestamp::get_timestamp(struct timeval *tm) +bool Field_timestamp::get_timestamp(struct timeval *tm, int *warnings) { if (is_null()) return true; @@ -5570,12 +5551,11 @@ Field_timestampf::get_date_internal(MYSQ } -bool Field_timestampf::get_timestamp(struct timeval *tm) +bool Field_timestampf::get_timestamp(struct timeval *tm, int *warnings) { THD *thd= table ? table->in_use : current_thd; thd->time_zone_used= 1; - if (is_null()) - return true; + DBUG_ASSERT(!is_null()); my_timestamp_from_binary(tm, ptr, dec); return false; } === modified file 'sql/field.h' --- a/sql/field.h 2011-11-17 13:41:28 +0000 +++ b/sql/field.h 2011-11-18 13:26:35 +0000 @@ -539,7 +539,7 @@ public: This method is used in "SELECT UNIX_TIMESTAMP(field)" to avoid conversion from timestamp to MYSQL_TIME and back. */ - virtual bool get_timestamp(struct timeval *tm); + virtual bool get_timestamp(struct timeval *tm, int *warnings); /** Stores a timestamp value in "struct timeval" format into a field. Note, store_timestamp(), get_timestamp() and store_time() @@ -2174,7 +2174,7 @@ public: Field::set_default(); } /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ - bool get_timestamp(struct timeval *tm); + bool get_timestamp(struct timeval *tm, int *warnings); bool get_date(MYSQL_TIME *ltime,uint fuzzydate); Field_timestamp *clone(MEM_ROOT *mem_root) const { DBUG_ASSERT(type() == MYSQL_TYPE_TIMESTAMP); @@ -2269,7 +2269,7 @@ public: else Field::set_default(); } - bool get_timestamp(struct timeval *tm); + bool get_timestamp(struct timeval *tm, int *warnings); }; === modified file 'sql/item.cc' --- a/sql/item.cc 2011-11-17 13:41:28 +0000 +++ b/sql/item.cc 2011-11-18 13:26:35 +0000 @@ -1375,17 +1375,14 @@ bool Item::get_time_from_non_temporal(MY } return (null_value= true); // Impossible result type } - -bool Item::get_timeval(struct timeval *tm) + +bool Item::get_timeval(struct timeval *tm, int *warnings) { MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return true; - my_bool not_used; - tm->tv_sec= TIME_to_timestamp(current_thd, <ime, ¬_used); - tm->tv_usec= ltime.second_part; - return false; + return (null_value= + (get_date(<ime, TIME_FUZZY_DATE) || + datetime_to_timeval(current_thd, <ime, tm, warnings))); } @@ -2677,9 +2674,10 @@ bool Item_field::get_time(MYSQL_TIME *lt return 0; } -bool Item_field::get_timeval(struct timeval *tm) +bool Item_field::get_timeval(struct timeval *tm, int *warnings) { - return ((null_value= field->get_timestamp(tm))); + return (null_value= (field->is_null() || + field->get_timestamp(tm, warnings))); } double Item_field::val_result() === modified file 'sql/item.h' --- a/sql/item.h 2011-11-10 14:58:23 +0000 +++ b/sql/item.h 2011-11-18 09:52:00 +0000 @@ -1082,7 +1082,7 @@ public: @retval false on success @retval true on error */ - virtual bool get_timeval(struct timeval *tm); + virtual bool get_timeval(struct timeval *tm, int *warnings); virtual bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate) { return get_date(ltime,fuzzydate); } /* @@ -1989,7 +1989,7 @@ public: bool get_date(MYSQL_TIME *ltime,uint fuzzydate); bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate); bool get_time(MYSQL_TIME *ltime); - bool get_timeval(struct timeval *tm); + bool get_timeval(struct timeval *tm, int *warnings); bool is_null() { return field->is_null(); } void update_null_value(); Item *get_tmp_table_item(THD *thd); === modified file 'sql/item_cmpfunc.cc' --- a/sql/item_cmpfunc.cc 2011-11-17 13:41:28 +0000 +++ b/sql/item_cmpfunc.cc 2011-11-18 13:26:35 +0000 @@ -910,8 +910,32 @@ get_time_value(THD *thd, Item ***item_ar longlong value; Item *item= **item_arg; + /* + Note, it's wrong to assume that we always get + a TIME expression or NULL here: + DBUG_ASSERT(item->field_type() == MYSQL_TYPE_TIME || item->field_type() == MYSQL_TYPE_NULL); + + because when this condition is optimized: + + WHERE time_column=DATE(NULL) AND time_column=TIME(NULL); + + rhe first AND part is eliminated and DATE(NULL) is substituted + to the second AND part like this: + + WHERE DATE(NULL) = TIME(NULL) // as TIME + + whose Arg_comparator has already get_time_value set for both arguments. + Therefore, get_time_value is executed for DATE(NULL). + This condition is further evaluated as impossible condition. + + TS-TODO: perhaps such cases should be evaluated without + calling get_time_value at all. + + See a similar comment in Arg_comparator::compare_temporal_packed, + for DATETIME comparison. + */ value= item->val_time_temporal(); *is_null= item->null_value; === modified file 'sql/item_timefunc.cc' --- a/sql/item_timefunc.cc 2011-11-17 13:41:28 +0000 +++ b/sql/item_timefunc.cc 2011-11-18 13:26:35 +0000 @@ -1474,14 +1474,19 @@ bool Item_func_unix_timestamp::val_timev tm->tv_usec= 0; return false; // no args: null_value is set in constructor and is always 0. } - if (args[0]->get_timeval(tm)) // Don't set null_value here + int warnings= 0; + if (args[0]->get_timeval(tm, &warnings)) // Don't set null_value here { - tm->tv_sec= tm->tv_usec= 0; /* - We set null_value only if args[0] returned null. + We set null_value only if args[0]->get_date() invoked + inside args[0]->get_timeval() returned null, + which is indicated by "warnings == 0". In case of wrong non-null datetime parameter we return 0. + "warnings" will not be equal to 0 in this case. */ - return (null_value= args[0]->null_value); + if (warnings == 0) + return (null_value= true); + tm->tv_sec= tm->tv_usec= 0; } return (null_value= false); } @@ -1502,10 +1507,9 @@ longlong Item_func_unix_timestamp::val_i DBUG_ASSERT(arg_count == 1 && args[0]->type() == Item::FIELD_ITEM && args[0]->field_type() == MYSQL_TYPE_TIMESTAMP); - Field *field=((Item_field*) args[0])->field; /* Leave the incl_endp intact */ struct timeval tm; - return (null_value= field->get_timestamp(&tm)) ? 0 : tm.tv_sec; + return val_timeval(&tm) ? 0 : tm.tv_sec; } === modified file 'sql/sql_time.cc' --- a/sql/sql_time.cc 2011-11-03 09:33:45 +0000 +++ b/sql/sql_time.cc 2011-11-18 09:52:00 +0000 @@ -637,6 +637,116 @@ my_time_t TIME_to_timestamp(THD *thd, co } +/** + Convert a datetime MYSQL_TIME representation + to corresponding "struct timeval" value. + + ltime must previously be checked for TIME_NO_ZERO_IN_DATE. + Things like '0000-01-01', '2000-00-01', '2000-01-00' are not allowed + and asserted. + + Things like '0000-00-00 10:30:30' or '0000-00-00 00:00:00.123456' + (i.e. empty date with non-empty time) return error. + + Zero datetime '0000-00-00 00:00:00.000000' + is allowed and is mapper to {tv_sec=0, tv_usec=0}. + + Note: In case of error, tm value is not initialized. + + Note: "warnings" is not initialized to zero, + so new warnings are added to the old ones. + Caller must make sure to initialize "warnings". + + @param IN thd current thd + @param IN ltime datetime value + @param OUT tm timeval value + @param OUT warnings pointer to warnings vector + @return + @retval false on success + @retval true on error +*/ +bool datetime_with_no_zero_in_date_to_timeval(THD *thd, + const MYSQL_TIME *ltime, + struct timeval *tm, + int *warnings) +{ + if (!ltime->month) /* Zero date */ + { + DBUG_ASSERT(!ltime->year && !ltime->day); + if (non_zero_time(ltime)) + { + /* + Return error for zero date with non-zero time, e.g.: + '0000-00-00 10:20:30' or '0000-00-00 00:00:00.123456' + */ + *warnings|= MYSQL_TIME_WARN_TRUNCATED; + return true; + } + tm->tv_sec= tm->tv_usec= 0; // '0000-00-00 00:00:00.000000' + return false; + } + + my_bool in_dst_time_gap; + if (!(tm->tv_sec= TIME_to_timestamp(current_thd, ltime, &in_dst_time_gap))) + { + /* + Date was outside of the supported timestamp range. + For example: '3001-01-01 00:00:00' or '1000-01-01 00:00:00' + */ + *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + return true; + } + else if (in_dst_time_gap) + { + /* + Set MYSQL_TIME_WARN_INVALID_TIMESTAMP warning to indicate + that date was fine but pointed to winter/summer time switch gap. + In this case tm is set to the fist second after gap. + For example: '2003-03-30 02:30:00 MSK' -> '2003-03-30 03:00:00 MSK' + */ + *warnings|= MYSQL_TIME_WARN_INVALID_TIMESTAMP; + } + tm->tv_usec= ltime->second_part; + return false; +} + + +/** + Convert a datetime MYSQL_TIME representation + to corresponding "struct timeval" value. + + Things like '0000-01-01', '2000-00-01', '2000-01-00' + (i.e. incomplete date) return error. + + Things like '0000-00-00 10:30:30' or '0000-00-00 00:00:00.123456' + (i.e. empty date with non-empty time) return error. + + Zero datetime '0000-00-00 00:00:00.000000' + is allowed and is mapper to {tv_sec=0, tv_usec=0}. + + Note: In case of error, tm value is not initialized. + + Note: "warnings" is not initialized to zero, + so new warnings are added to the old ones. + Caller must make sure to initialize "warnings". + + @param IN thd current thd + @param IN ltime datetime value + @param OUT tm timeval value + @param OUT warnings pointer to warnings vector + @return + @retval false on success + @retval true on error +*/ +bool datetime_to_timeval(THD *thd, const MYSQL_TIME *ltime, + struct timeval *tm, int *warnings) +{ + return + check_date(ltime, non_zero_date(ltime), TIME_NO_ZERO_IN_DATE, warnings) || + datetime_with_no_zero_in_date_to_timeval(current_thd, ltime, tm, warnings); +} + + /* Convert a time string to a MYSQL_TIME struct and produce a warning if string was cut during conversion. === modified file 'sql/sql_time.h' --- a/sql/sql_time.h 2011-11-07 11:50:35 +0000 +++ b/sql/sql_time.h 2011-11-18 09:52:00 +0000 @@ -36,6 +36,11 @@ ulong convert_month_to_period(ulong mont void mix_date_and_time(MYSQL_TIME *ldate, const MYSQL_TIME *ltime); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *not_exist); +bool datetime_with_no_zero_in_date_to_timeval(THD *thd, const MYSQL_TIME *t, + struct timeval *tm, + int *warnings); +bool datetime_to_timeval(THD *thd, const MYSQL_TIME *t, + struct timeval *tm, int *warnings); bool str_to_datetime_with_warn(String *str, MYSQL_TIME *l_time, uint flags); bool my_decimal_to_datetime_with_warn(const my_decimal *decimal, MYSQL_TIME *ltime, uint flags); === modified file 'unittest/gunit/field-t.cc' --- a/unittest/gunit/field-t.cc 2011-11-01 11:52:24 +0000 +++ b/unittest/gunit/field-t.cc 2011-11-18 11:49:13 +0000 @@ -258,7 +258,8 @@ TEST_F(FieldTest, FieldTimef) // it requires a mock table struct timeval tv; - EXPECT_EQ(0, f->get_timestamp(&tv)); + int warnings= 0; + EXPECT_EQ(0, f->get_timestamp(&tv, &warnings)); // EXPECT_EQ(40992, tv.tv_sec); // This is 11:23:12. Why? Time zone? EXPECT_EQ(123400, tv.tv_usec); No bundle (reason: useless for push emails).