From a25ce5ab4b38f0557abba8c5eb71b0839dc1ec4b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sat, 8 Dec 2018 16:10:44 +0400 Subject: [PATCH] MDEV-17928 Conversion from TIMESTAMP to VARCHAR SP variables does not work well on fractional digits --- mysql-test/main/sp.result | 6 ++--- mysql-test/main/type_timestamp.result | 35 +++++++++++++++++++++++++++ mysql-test/main/type_timestamp.test | 35 +++++++++++++++++++++++++++ sql/field.cc | 15 +++++------- sql/field.h | 9 +++++-- sql/sql_show.cc | 3 ++- sql/sql_type.h | 3 +++ sql/structs.h | 3 +++ 8 files changed, 94 insertions(+), 15 deletions(-) diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result index 16b0b1d60e5..ade6bbc212c 100644 --- a/mysql-test/main/sp.result +++ b/mysql-test/main/sp.result @@ -8559,11 +8559,11 @@ RETURN a = timestamp'2038-01-19 03:14:07.999999' END $$ SELECT f1(e) FROM t1; -ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30' +ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30.000000' SELECT f2(e) FROM t1; -ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30' +ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30.000000' SELECT f3(e) FROM t1; -ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30' +ERROR 22007: Truncated incorrect DOUBLE value: '2001-01-01 10:20:30.000000' DROP FUNCTION f1; DROP FUNCTION f2; DROP FUNCTION f3; diff --git a/mysql-test/main/type_timestamp.result b/mysql-test/main/type_timestamp.result index a708ef5ec34..ea1dc24386f 100644 --- a/mysql-test/main/type_timestamp.result +++ b/mysql-test/main/type_timestamp.result @@ -1033,5 +1033,40 @@ CREATE TABLE t1 (a TIMESTAMP); INSERT INTO t1 SELECT CAST(20010101 AS UNSIGNED); DROP TABLE t1; # +# MDEV-17928 Conversion from TIMESTAMP to VARCHAR SP variables does not work well on fractional digits +# +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30.123456'); +CREATE PROCEDURE p1() +BEGIN +DECLARE ts10 TIMESTAMP(1) DEFAULT NOW(); +DECLARE ts16 TIMESTAMP(1) DEFAULT NOW(6); +DECLARE dt10 DATETIME(1) DEFAULT NOW(); +DECLARE dt16 DATETIME(1) DEFAULT NOW(6); +DECLARE vts10 VARCHAR(32) DEFAULT ts10; +DECLARE vts16 VARCHAR(32) DEFAULT ts16; +DECLARE vdt10 VARCHAR(32) DEFAULT dt10; +DECLARE vdt16 VARCHAR(32) DEFAULT dt16; +DECLARE tts10 TEXT(32) DEFAULT ts10; +DECLARE tts16 TEXT(32) DEFAULT ts16; +DECLARE tdt10 TEXT(32) DEFAULT dt10; +DECLARE tdt16 TEXT(32) DEFAULT dt16; +SELECT vts10, vts16, vdt10, vdt16; +SELECT tts10, tts16, tdt10, tdt16; +END; +$$ +CALL p1; +vts10 2001-01-01 10:20:30.0 +vts16 2001-01-01 10:20:30.1 +vdt10 2001-01-01 10:20:30.0 +vdt16 2001-01-01 10:20:30.1 +tts10 2001-01-01 10:20:30.0 +tts16 2001-01-01 10:20:30.1 +tdt10 2001-01-01 10:20:30.0 +tdt16 2001-01-01 10:20:30.1 +DROP PROCEDURE p1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# # End of 10.4 tests # diff --git a/mysql-test/main/type_timestamp.test b/mysql-test/main/type_timestamp.test index eeb6e2f8213..033795ee183 100644 --- a/mysql-test/main/type_timestamp.test +++ b/mysql-test/main/type_timestamp.test @@ -625,6 +625,41 @@ CREATE TABLE t1 (a TIMESTAMP); INSERT INTO t1 SELECT CAST(20010101 AS UNSIGNED); DROP TABLE t1; + +--echo # +--echo # MDEV-17928 Conversion from TIMESTAMP to VARCHAR SP variables does not work well on fractional digits +--echo # + +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30.123456'); +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE ts10 TIMESTAMP(1) DEFAULT NOW(); + DECLARE ts16 TIMESTAMP(1) DEFAULT NOW(6); + DECLARE dt10 DATETIME(1) DEFAULT NOW(); + DECLARE dt16 DATETIME(1) DEFAULT NOW(6); + DECLARE vts10 VARCHAR(32) DEFAULT ts10; + DECLARE vts16 VARCHAR(32) DEFAULT ts16; + DECLARE vdt10 VARCHAR(32) DEFAULT dt10; + DECLARE vdt16 VARCHAR(32) DEFAULT dt16; + DECLARE tts10 TEXT(32) DEFAULT ts10; + DECLARE tts16 TEXT(32) DEFAULT ts16; + DECLARE tdt10 TEXT(32) DEFAULT dt10; + DECLARE tdt16 TEXT(32) DEFAULT dt16; + + SELECT vts10, vts16, vdt10, vdt16; + SELECT tts10, tts16, tdt10, tdt16; +END; +$$ +DELIMITER ;$$ +--vertical_results +CALL p1; +--horizontal_results +DROP PROCEDURE p1; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + --echo # --echo # End of 10.4 tests --echo # diff --git a/sql/field.cc b/sql/field.cc index 6b38b38782c..6edca83329c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1831,12 +1831,9 @@ int Field::store(const char *to, size_t length, CHARSET_INFO *cs, } -int Field::store_timestamp(my_time_t ts, ulong sec_part) +int Field::store_timestamp_dec(const timeval &ts, uint dec) { - MYSQL_TIME ltime; - THD *thd= get_thd(); - thd->timestamp_to_TIME(<ime, ts, sec_part, date_mode_t(0)); - return store_time_dec(<ime, decimals()); + return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec); } /** @@ -5014,7 +5011,7 @@ int Field_timestamp::save_in_field(Field *to) { ulong sec_part; my_time_t ts= get_timestamp(&sec_part); - return to->store_timestamp(ts, sec_part); + return to->store_timestamp_dec(Timeval(ts, sec_part), decimals()); } my_time_t Field_timestamp::get_timestamp(const uchar *pos, @@ -5126,11 +5123,11 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) } -int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part) +int Field_timestamp::store_timestamp_dec(const timeval &ts, uint dec) { int warn= 0; time_round_mode_t mode= Datetime::default_round_mode(get_thd()); - store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn)); + store_TIMESTAMP(Timestamp(ts).round(decimals(), mode, &warn)); if (warn) { /* @@ -5144,7 +5141,7 @@ int Field_timestamp::store_timestamp(my_time_t ts, ulong sec_part) */ set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); } - if (ts == 0 && sec_part == 0 && + if (ts.tv_sec == 0 && ts.tv_usec == 0 && get_thd()->variables.sql_mode & (ulonglong) TIME_NO_ZERO_DATE) { ErrConvString s( diff --git a/sql/field.h b/sql/field.h index 6c0a981fc0a..19ab24bc2cb 100644 --- a/sql/field.h +++ b/sql/field.h @@ -784,7 +784,12 @@ public: virtual int store(longlong nr, bool unsigned_val)=0; virtual int store_decimal(const my_decimal *d)=0; virtual int store_time_dec(const MYSQL_TIME *ltime, uint dec); - virtual int store_timestamp(my_time_t timestamp, ulong sec_part); + virtual int store_timestamp_dec(const timeval &ts, uint dec); + int store_timestamp(my_time_t timestamp, ulong sec_part) + { + return store_timestamp_dec(Timeval(timestamp, sec_part), + TIME_SECOND_PART_DIGITS); + } int store_time(const MYSQL_TIME *ltime) { return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); } int store(const char *to, size_t length, CHARSET_INFO *cs, @@ -2744,7 +2749,7 @@ public: int store(longlong nr, bool unsigned_val); int store_time_dec(const MYSQL_TIME *ltime, uint dec); int store_decimal(const my_decimal *); - int store_timestamp(my_time_t timestamp, ulong sec_part); + int store_timestamp_dec(const timeval &ts, uint dec); int save_in_field(Field *to); double val_real(void); longlong val_int(void); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a62603dbf1c..5adee6731b5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7495,7 +7495,8 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, } else if (part_info->vers_info->interval.is_set()) { - table->field[11]->store_timestamp((my_time_t)part_elem->range_value, 0); + Timeval tv((my_time_t) part_elem->range_value, 0); + table->field[11]->store_timestamp_dec(tv, AUTO_SEC_PART_DIGITS); table->field[11]->set_notnull(); } } diff --git a/sql/sql_type.h b/sql/sql_type.h index 0e023a3ef65..10b7ed94da0 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2234,6 +2234,9 @@ public: Timestamp(my_time_t timestamp, ulong sec_part) :Timeval(timestamp, sec_part) { } + explicit Timestamp(const timeval &tv) + :Timeval(tv) + { } const struct timeval &tv() const { return *this; } long fraction_remainder(uint dec) const { diff --git a/sql/structs.h b/sql/structs.h index dec1e4de0c7..c7af86ee6a5 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -853,6 +853,9 @@ public: tv_sec= sec; tv_usec= usec; } + explicit Timeval(const timeval &tv) + :timeval(tv) + { } };