From 2394fa67d4908f62ef14f3ca90269fb32806d193 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 21 Nov 2013 11:46:36 +0400 Subject: [PATCH] MDEV-4859 Wrong value and data type of "SELECT MAX(time_column) + 1 FROM t1" Fixed. --- mysql-test/r/func_time.result | 8 +-- mysql-test/r/type_time.result | 107 ++++++++++++++++++++++++++++++++++ mysql-test/t/type_time.test | 68 +++++++++++++++++++++ sql/item.cc | 69 ++++++++++++++++++++++ sql/item.h | 6 ++ sql/item_cmpfunc.cc | 9 ++- sql/item_func.cc | 2 + sql/item_sum.cc | 2 +- sql/item_timefunc.cc | 21 ------- sql/item_timefunc.h | 4 +- 10 files changed, 265 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 645733a3dc0..c1b17948402 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -958,10 +958,10 @@ sec_to_time(1) + 0, from_unixtime(1) + 0; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `now() - now()` decimal(20,0) NOT NULL DEFAULT '0', - `curtime() - curtime()` decimal(11,0) NOT NULL DEFAULT '0', - `sec_to_time(1) + 0` decimal(11,0) DEFAULT NULL, - `from_unixtime(1) + 0` decimal(20,0) DEFAULT NULL + `now() - now()` bigint(21) NOT NULL DEFAULT '0', + `curtime() - curtime()` bigint(12) NOT NULL DEFAULT '0', + `sec_to_time(1) + 0` bigint(12) DEFAULT NULL, + `from_unixtime(1) + 0` bigint(21) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; SELECT SEC_TO_TIME(3300000); diff --git a/mysql-test/r/type_time.result b/mysql-test/r/type_time.result index c66f3f08e08..84cd6ca4d9e 100644 --- a/mysql-test/r/type_time.result +++ b/mysql-test/r/type_time.result @@ -191,5 +191,112 @@ SELECT CONCAT(GREATEST(TIME('32 00:00:01'),TIME('00:00:00'))); CONCAT(GREATEST(TIME('32 00:00:01'),TIME('00:00:00'))) 768:00:01 # +# MDEV-4859 Wrong value and data type of "SELECT MAX(time_column) + 1 FROM t1" +# +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1 FROM t1; +MAX(t0)+1 +101011 +CREATE TABLE t2 AS SELECT MAX(t0)+1 FROM t1; +SELECT * FROM t2; +MAX(t0)+1 +101011 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1 bigint(12) YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1.1 FROM t1; +MAX(t0)+1.1 +101011.1 +CREATE TABLE t2 AS SELECT MAX(t0)+1.1 FROM t1; +SELECT * FROM t2; +MAX(t0)+1.1 +101011.1 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1.1 decimal(12,1) YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1e0 FROM t1; +MAX(t0)+1e0 +101011 +CREATE TABLE t2 AS SELECT MAX(t0)+1e0 FROM t1; +SELECT * FROM t2; +MAX(t0)+1e0 +101011 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1e0 double YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t1 TIME(1)); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t1)+1 FROM t1; +MAX(t1)+1 +101011.0 +CREATE TABLE t2 AS SELECT MAX(t1)+1 FROM t1; +SELECT * FROM t2; +MAX(t1)+1 +101011.0 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t1)+1 decimal(13,1) YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1 FROM t1; +MAX(t0)+1 +20010101101011 +CREATE TABLE t2 AS SELECT MAX(t0)+1 FROM t1; +SELECT * FROM t2; +MAX(t0)+1 +20010101101011 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1 bigint(21) YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1.1 FROM t1; +MAX(t0)+1.1 +20010101101011.1 +CREATE TABLE t2 AS SELECT MAX(t0)+1.1 FROM t1; +SELECT * FROM t2; +MAX(t0)+1.1 +20010101101011.1 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1.1 decimal(21,1) YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1e0 FROM t1; +MAX(t0)+1e0 +20010101101011 +CREATE TABLE t2 AS SELECT MAX(t0)+1e0 FROM t1; +SELECT * FROM t2; +MAX(t0)+1e0 +20010101101011 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t0)+1e0 double YES NULL +DROP TABLE t2,t1; +CREATE TABLE t1 (t1 DATETIME(1)); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t1)+1 FROM t1; +MAX(t1)+1 +20010101101011.0 +CREATE TABLE t2 AS SELECT MAX(t1)+1 FROM t1; +SELECT * FROM t2; +MAX(t1)+1 +20010101101011.0 +SHOW COLUMNS FROM t2; +Field Type Null Key Default Extra +MAX(t1)+1 decimal(22,1) YES NULL +DROP TABLE t2,t1; +# # End of 5.3 tests # diff --git a/mysql-test/t/type_time.test b/mysql-test/t/type_time.test index 1c0ba75e274..724e25625af 100644 --- a/mysql-test/t/type_time.test +++ b/mysql-test/t/type_time.test @@ -134,6 +134,74 @@ SELECT CONVERT_TZ(GREATEST(TIME('00:00:00'),TIME('00:00:00')),'+00:00','+7:5'); SELECT CONCAT(GREATEST(TIME('00:00:01'),TIME('00:00:00'))); SELECT CONCAT(GREATEST(TIME('32 00:00:01'),TIME('00:00:00'))); +--echo # +--echo # MDEV-4859 Wrong value and data type of "SELECT MAX(time_column) + 1 FROM t1" +--echo # +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1.1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1.1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t0 TIME); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t0)+1e0 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1e0 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t1 TIME(1)); +INSERT INTO t1 VALUES ('10:10:10'); +SELECT MAX(t1)+1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t1)+1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1.1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1.1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t0 DATETIME); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t0)+1e0 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t0)+1e0 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + +CREATE TABLE t1 (t1 DATETIME(1)); +INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); +SELECT MAX(t1)+1 FROM t1; +CREATE TABLE t2 AS SELECT MAX(t1)+1 FROM t1; +SELECT * FROM t2; +SHOW COLUMNS FROM t2; +DROP TABLE t2,t1; + + --echo # --echo # End of 5.3 tests --echo # diff --git a/sql/item.cc b/sql/item.cc index a415464a2f9..29cfd2d08d1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -350,6 +350,27 @@ my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) } +longlong Item::val_int_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, 0)) + return 0; + longlong v= TIME_to_ulonglong(<ime); + return ltime.neg ? -v : v; +} + + +double Item::val_real_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, 0)) + return 0; + return TIME_to_double(<ime); +} + + double Item::val_real_from_decimal() { /* Note that fix_fields may not be called for Item_avg_field items */ @@ -8381,6 +8402,18 @@ int Item_cache_int::save_in_field(Field *field, bool no_conversions) } +longlong Item_cache_temporal::val_temporal_packed() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; + return 0; + } + return value; +} + + String *Item_cache_temporal::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -8393,6 +8426,42 @@ String *Item_cache_temporal::val_str(String *str) } +my_decimal *Item_cache_temporal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return NULL; + } + return val_decimal_from_date(decimal_value); +} + + +longlong Item_cache_temporal::val_int() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return 0; + } + return val_int_from_date(); +} + + +double Item_cache_temporal::val_real() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return 0; + } + return val_real_from_date(); +} + + bool Item_cache_temporal::cache_value() { if (!example) diff --git a/sql/item.h b/sql/item.h index 084e77e4d23..03b333e7997 100644 --- a/sql/item.h +++ b/sql/item.h @@ -847,7 +847,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); @@ -3830,6 +3832,10 @@ public: } String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); + longlong val_int(); + longlong val_temporal_packed(); + double val_real(); bool cache_value(); bool get_date(MYSQL_TIME *ltime, uint fuzzydate); int save_in_field(Field *field, bool no_conversions); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 49e56a46e84..4817dadafd9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -877,10 +877,13 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, enum_field_types f_type= item->cmp_type() == TIME_RESULT ? item->field_type() : warn_item->field_type(); - if (item->result_type() == INT_RESULT && item->cmp_type() == TIME_RESULT) + if (item->result_type() == INT_RESULT && + item->cmp_type() == TIME_RESULT && + item->type() == Item::CACHE_ITEM) { - /* it's our Item_cache_int, as created below */ - value= item->val_int(); + /* it's our Item_cache_temporal, as created below */ + DBUG_ASSERT(is_temporal_type(((Item_cache *) item)->field_type())); + value= ((Item_cache_temporal*) item)->val_temporal_packed(); } else { diff --git a/sql/item_func.cc b/sql/item_func.cc index 9416d05cb2f..03cd6b5139d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -798,6 +798,8 @@ void Item_num_op::find_num_type(void) cached_result_type= DECIMAL_RESULT; result_precision(); fix_decimals(); + if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0) + cached_result_type= INT_RESULT; } else { diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 489c0f1c23e..2d2001381a1 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -687,7 +687,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg) { - if (!(value= Item_cache::get_cache(item))) + if (!(value= Item_cache::get_cache(item, item->cmp_type()))) return; value->setup(item); value->store(value_arg); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index cb60368aafd..3baf583eaa0 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1362,27 +1362,6 @@ String *Item_temporal_func::val_str(String *str) } -longlong Item_temporal_func::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return 0; - longlong v= TIME_to_ulonglong(<ime); - return ltime.neg ? -v : v; -} - - -double Item_temporal_func::val_real() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return 0; - return TIME_to_double(<ime); -} - - bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { longlong value=args[0]->val_int(); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index bc048e1ff64..d97a5fbc903 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -448,8 +448,8 @@ public: enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } String *val_str(String *str); - longlong val_int(); - double val_real(); + longlong val_int() { return val_int_from_date(); } + double val_real() { return val_real_from_date(); } bool get_date(MYSQL_TIME *res, uint fuzzy_date) { DBUG_ASSERT(0); return 1; } my_decimal *val_decimal(my_decimal *decimal_value) { return val_decimal_from_date(decimal_value); }