diff options
author | Alexander Barkov <bar@mnogosearch.org> | 2013-09-09 15:32:25 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mnogosearch.org> | 2013-09-09 15:32:25 +0400 |
commit | c2b38529a9ca2ea09dfa73186d9350a0d6dcd6ac (patch) | |
tree | c4aac68dfd58c8f87a6c1cc8d4c456a8fa02e83b | |
parent | 02338228dd48752a48d611b03ce35efd35d143d7 (diff) | |
download | mariadb-git-c2b38529a9ca2ea09dfa73186d9350a0d6dcd6ac.tar.gz |
MDEV-4863 COALESCE(time_or_datetime) returns wrong results in numeric context
-rw-r--r-- | mysql-test/r/func_time.result | 109 | ||||
-rw-r--r-- | mysql-test/r/type_datetime.result | 9 | ||||
-rw-r--r-- | mysql-test/t/func_time.test | 93 | ||||
-rw-r--r-- | sql/field.h | 13 | ||||
-rw-r--r-- | sql/item.h | 13 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 145 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 52 | ||||
-rw-r--r-- | sql/item_func.cc | 238 | ||||
-rw-r--r-- | sql/item_func.h | 108 | ||||
-rw-r--r-- | sql/item_timefunc.h | 10 | ||||
-rw-r--r-- | sql/mysql_priv.h | 11 |
11 files changed, 628 insertions, 173 deletions
diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 88599b79a1f..8bbc692aa4a 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1923,3 +1923,112 @@ NULL Warnings: Error 1411 Incorrect datetime value: '2020' for function str_to_date SET TIME_ZONE=DEFAULT; +# +# MDEV-4863 COALESCE(time_or_datetime) returns wrong results in numeric context +# +CREATE TABLE t1 (a TIMESTAMP(3)); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30.999'); +SELECT CAST(COALESCE(a,a) AS SIGNED) AS c1, CAST(COALESCE(a,a) AS DECIMAL(25,3)) AS c2, ROUND(COALESCE(a,a)) AS c2 FROM t1; +c1 c2 c2 +20010101102030 20010101102030.999 20010101102031 +DROP TABLE t1; +CREATE TABLE t1 (a TIME(3)); +INSERT INTO t1 VALUES ('10:20:30.999'); +SELECT CAST(COALESCE(a,a) AS SIGNED) AS c1, CAST(COALESCE(a,a) AS DECIMAL(25,3)) AS c2, ROUND(COALESCE(a,a)) AS c2 FROM t1; +c1 c2 c2 +102030 102030.999 102031 +DROP TABLE t1; +SELECT +CAST(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, +CAST(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, +COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, +CONCAT(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, +TIME(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, +DATE(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, +TIMESTAMP(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010101000000 20010101000000.0000 20010101000000 2001-01-01 00:00:00.000000 00:00:00.000000 2001-01-01 2001-01-01 00:00:00.000000 +SELECT +CAST(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS SIGNED) AS c1, +CAST(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS DECIMAL(25,4)) AS c2, +COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))+0e0 AS c3, +CONCAT(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c4, +TIME(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c5, +DATE(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c6, +TIMESTAMP(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010102102030 20010102102030.0000 20010102102030 2001-01-02 10:20:30.000000 10:20:30.000000 2001-01-02 2001-01-02 10:20:30.000000 +SELECT +CAST(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, +CAST(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, +IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, +CONCAT(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, +TIME(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, +DATE(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, +TIMESTAMP(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010101000000 20010101000000.0000 20010101000000 2001-01-01 00:00:00.000000 00:00:00.000000 2001-01-01 2001-01-01 00:00:00.000000 +SELECT +CAST(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS SIGNED) AS c1, +CAST(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS DECIMAL(25,4)) AS c2, +IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))+0e0 AS c3, +CONCAT(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c4, +TIME(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c5, +DATE(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c6, +TIMESTAMP(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010102102030 20010102102030.0000 20010102102030 2001-01-02 10:20:30.000000 10:20:30.000000 2001-01-02 2001-01-02 10:20:30.000000 +SELECT +CAST(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, +CAST(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, +IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, +CONCAT(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, +TIME(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, +DATE(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, +TIMESTAMP(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010101000000 20010101000000.0000 20010101000000 2001-01-01 00:00:00.000000 00:00:00.000000 2001-01-01 2001-01-01 00:00:00.000000 +SELECT +CAST(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, +CAST(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, +IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, +CONCAT(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, +TIME(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, +DATE(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, +TIMESTAMP(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010102102030 20010102102030.0000 20010102102030 2001-01-02 10:20:30.000000 10:20:30.000000 2001-01-02 2001-01-02 10:20:30.000000 +SELECT +CAST(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS SIGNED) AS c1, +CAST(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS DECIMAL(25,4)) AS c2, +CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END+0e0 AS c3, +CONCAT(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c4, +TIME(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c5, +DATE(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c6, +TIMESTAMP(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010101000000 20010101000000.0000 20010101000000 2001-01-01 00:00:00.000000 00:00:00.000000 2001-01-01 2001-01-01 00:00:00.000000 +SELECT +CAST(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS SIGNED) AS c1, +CAST(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS DECIMAL(25,4)) AS c2, +CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END+0e0 AS c3, +CONCAT(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c4, +TIME(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c5, +DATE(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c6, +TIMESTAMP(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c7; +c1 c2 c3 c4 c5 c6 c7 +20010102102030 20010102102030.0000 20010102102030 2001-01-02 10:20:30.000000 10:20:30.000000 2001-01-02 2001-01-02 10:20:30.000000 +CREATE TABLE t1 AS SELECT +CONCAT(COALESCE(TIME(101010),TIME(101010))) AS c1, +CONCAT(IF(0,TIME(101010),TIME(101010))) AS c2, +CONCAT(IFNULL(TIME(101010),TIME(101010))) AS c3, +CONCAT(CASE WHEN 1 THEN TIME(101010) ELSE TIME(101010) END) AS c4; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` varbinary(10) DEFAULT NULL, + `c2` varbinary(10) DEFAULT NULL, + `c3` varbinary(10) DEFAULT NULL, + `c4` varbinary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result index 286a84c56e7..de0bc2a0b80 100644 --- a/mysql-test/r/type_datetime.result +++ b/mysql-test/r/type_datetime.result @@ -657,13 +657,8 @@ create table t1 (d date, t time) engine=myisam; insert into t1 values ('2000-12-03','22:55:23'),('2008-05-03','10:19:31'); select case when d = '2012-12-12' then d else t end as cond, group_concat( d ) from t1 group by cond; cond group_concat( d ) -NULL 2000-12-03 -NULL 2008-05-03 -Warnings: -Warning 1292 Incorrect datetime value: '22:55:23' -Warning 1292 Incorrect datetime value: '10:19:31' -Warning 1292 Incorrect datetime value: '22:55:23' -Warning 1292 Incorrect datetime value: '10:19:31' +0000-00-00 10:19:31 2008-05-03 +0000-00-00 22:55:23 2000-12-03 drop table t1; # # Semantics of the condition <non-nullable datetime field> IS NULL diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index ff2216802bf..6aedf3f55df 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -1162,3 +1162,96 @@ SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12')); SET TIME_ZONE='+02:00'; SELECT UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')); SET TIME_ZONE=DEFAULT; + + +--echo # +--echo # MDEV-4863 COALESCE(time_or_datetime) returns wrong results in numeric context +--echo # +CREATE TABLE t1 (a TIMESTAMP(3)); +INSERT INTO t1 VALUES ('2001-01-01 10:20:30.999'); +SELECT CAST(COALESCE(a,a) AS SIGNED) AS c1, CAST(COALESCE(a,a) AS DECIMAL(25,3)) AS c2, ROUND(COALESCE(a,a)) AS c2 FROM t1; +DROP TABLE t1; +CREATE TABLE t1 (a TIME(3)); +INSERT INTO t1 VALUES ('10:20:30.999'); +SELECT CAST(COALESCE(a,a) AS SIGNED) AS c1, CAST(COALESCE(a,a) AS DECIMAL(25,3)) AS c2, ROUND(COALESCE(a,a)) AS c2 FROM t1; +DROP TABLE t1; + +SELECT + CAST(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, + CAST(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, + COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, + CONCAT(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, + TIME(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, + DATE(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, + TIMESTAMP(COALESCE(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; + +SELECT + CAST(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS SIGNED) AS c1, + CAST(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS DECIMAL(25,4)) AS c2, + COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))+0e0 AS c3, + CONCAT(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c4, + TIME(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c5, + DATE(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c6, + TIMESTAMP(COALESCE(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c7; + +SELECT + CAST(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, + CAST(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, + IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, + CONCAT(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, + TIME(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, + DATE(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, + TIMESTAMP(IFNULL(DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; + +SELECT + CAST(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS SIGNED) AS c1, + CAST(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01')) AS DECIMAL(25,4)) AS c2, + IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))+0e0 AS c3, + CONCAT(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c4, + TIME(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c5, + DATE(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c6, + TIMESTAMP(IFNULL(TIMESTAMP('2001-01-02 10:20:30'),DATE('2001-01-01'))) AS c7; + +SELECT + CAST(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, + CAST(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, + IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, + CONCAT(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, + TIME(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, + DATE(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, + TIMESTAMP(IF(1,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; + +SELECT + CAST(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS SIGNED) AS c1, + CAST(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30')) AS DECIMAL(25,4)) AS c2, + IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))+0e0 AS c3, + CONCAT(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c4, + TIME(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c5, + DATE(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c6, + TIMESTAMP(IF(0,DATE('2001-01-01'),TIMESTAMP('2001-01-02 10:20:30'))) AS c7; + +SELECT + CAST(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS SIGNED) AS c1, + CAST(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS DECIMAL(25,4)) AS c2, + CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END+0e0 AS c3, + CONCAT(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c4, + TIME(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c5, + DATE(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c6, + TIMESTAMP(CASE WHEN 1 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c7; + +SELECT + CAST(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS SIGNED) AS c1, + CAST(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END AS DECIMAL(25,4)) AS c2, + CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END+0e0 AS c3, + CONCAT(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c4, + TIME(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c5, + DATE(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c6, + TIMESTAMP(CASE WHEN 0 THEN DATE('2001-01-01') ELSE TIMESTAMP('2001-01-02 10:20:30') END) AS c7; + +CREATE TABLE t1 AS SELECT + CONCAT(COALESCE(TIME(101010),TIME(101010))) AS c1, + CONCAT(IF(0,TIME(101010),TIME(101010))) AS c2, + CONCAT(IFNULL(TIME(101010),TIME(101010))) AS c3, + CONCAT(CASE WHEN 1 THEN TIME(101010) ELSE TIME(101010) END) AS c4; +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/sql/field.h b/sql/field.h index 665b784d820..c1ae476fede 100644 --- a/sql/field.h +++ b/sql/field.h @@ -52,6 +52,19 @@ inline uint get_set_pack_length(int elements) return len > 4 ? 8 : len; } +/** + Tests if field type is temporal, i.e. represents + DATE, TIME, DATETIME or TIMESTAMP types in SQL. + + @param type Field type, as returned by field->type(). + @retval true If field type is temporal + @retval false If field type is not temporal +*/ +inline bool is_temporal_type(enum_field_types type) +{ + return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR; +} + /* Virtual_column_info is the class to contain additional characteristics that is specific for a virtual/computed diff --git a/sql/item.h b/sql/item.h index c66573fc0ef..5613fe547c4 100644 --- a/sql/item.h +++ b/sql/item.h @@ -45,6 +45,14 @@ struct TABLE_LIST; void item_init(void); /* Init item functions */ class Item_field; +static inline uint32 +char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg) +{ + ulonglong tmp= ((ulonglong) char_length_arg) * mbmaxlen_arg; + return (tmp > UINT_MAX32) ? (uint32) UINT_MAX32 : (uint32) tmp; +} + + /* "Declared Type Collation" A combination of collation and its derivation. @@ -1287,6 +1295,11 @@ public: { return Field::GEOM_GEOMETRY; }; String *check_well_formed_result(String *str, bool send_error= 0); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); + void fix_char_length(uint32 max_char_length_arg) + { + max_length= char_to_byte_length_safe(max_char_length_arg, + collation.collation->mbmaxlen); + } Item* set_expr_cache(THD *thd); virtual Item *get_cached_item() { return NULL; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e133e2811bd..f0069757ee3 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2374,12 +2374,13 @@ void Item_func_between::print(String *str, enum_query_type query_type) void Item_func_ifnull::fix_length_and_dec() { - agg_result_type(&hybrid_type, args, 2); + agg_result_type(&cached_result_type, args, 2); + cached_field_type= agg_field_type(args, 2); maybe_null=args[1]->maybe_null; decimals= max(args[0]->decimals, args[1]->decimals); unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag; - if (hybrid_type == DECIMAL_RESULT || hybrid_type == INT_RESULT) + if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) { int len0= args[0]->max_length - args[0]->decimals - (args[0]->unsigned_flag ? 0 : 1); @@ -2392,9 +2393,10 @@ Item_func_ifnull::fix_length_and_dec() else max_length= max(args[0]->max_length, args[1]->max_length); - switch (hybrid_type) { + switch (cached_result_type) { case STRING_RESULT: - agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1); + if (count_string_result_length(cached_field_type, args, arg_count)) + return; break; case DECIMAL_RESULT: case REAL_RESULT: @@ -2407,7 +2409,6 @@ Item_func_ifnull::fix_length_and_dec() case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } - cached_field_type= agg_field_type(args, 2); } @@ -2421,11 +2422,6 @@ uint Item_func_ifnull::decimal_precision() const } -enum_field_types Item_func_ifnull::field_type() const -{ - return cached_field_type; -} - Field *Item_func_ifnull::tmp_table_field(TABLE *table) { return tmp_table_field_from_field_type(table, 0); @@ -2499,6 +2495,18 @@ Item_func_ifnull::str_op(String *str) } +bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, uint fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + if (!args[0]->get_date(ltime, fuzzydate & ~TIME_FUZZY_DATES)) + return (null_value= false); + if (!args[1]->get_date(ltime, fuzzydate & ~TIME_FUZZY_DATES)) + return (null_value= false); + bzero((char*) ltime,sizeof(*ltime)); + return null_value= !(fuzzydate & TIME_FUZZY_DATES); +} + + /** Perform context analysis of an IF item tree. @@ -2587,16 +2595,16 @@ Item_func_if::fix_length_and_dec() else { agg_result_type(&cached_result_type, args+1, 2); + cached_field_type= agg_field_type(args + 1, 2); if (cached_result_type == STRING_RESULT) { - if (agg_arg_charsets(collation, args+1, 2, MY_COLL_ALLOW_CONV, 1)) - return; + count_string_result_length(cached_field_type, args + 1, 2); + return; } else { collation.set(&my_charset_bin); // Number } - cached_field_type= agg_field_type(args + 1, 2); } if ((cached_result_type == DECIMAL_RESULT ) @@ -2625,7 +2633,7 @@ uint Item_func_if::decimal_precision() const double -Item_func_if::val_real() +Item_func_if::real_op() { DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; @@ -2635,7 +2643,7 @@ Item_func_if::val_real() } longlong -Item_func_if::val_int() +Item_func_if::int_op() { DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; @@ -2645,7 +2653,7 @@ Item_func_if::val_int() } String * -Item_func_if::val_str(String *str) +Item_func_if::str_op(String *str) { DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; @@ -2658,7 +2666,7 @@ Item_func_if::val_str(String *str) my_decimal * -Item_func_if::val_decimal(my_decimal *decimal_value) +Item_func_if::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; @@ -2668,6 +2676,14 @@ Item_func_if::val_decimal(my_decimal *decimal_value) } +bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + Item *arg= args[0]->val_bool() ? args[1] : args[2]; + return (null_value= arg->get_date(ltime, fuzzydate)); +} + + void Item_func_nullif::fix_length_and_dec() { @@ -2826,7 +2842,7 @@ Item *Item_func_case::find_item(String *str) } -String *Item_func_case::val_str(String *str) +String *Item_func_case::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; @@ -2844,7 +2860,7 @@ String *Item_func_case::val_str(String *str) } -longlong Item_func_case::val_int() +longlong Item_func_case::int_op() { DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; @@ -2862,7 +2878,7 @@ longlong Item_func_case::val_int() return res; } -double Item_func_case::val_real() +double Item_func_case::real_op() { DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; @@ -2881,7 +2897,7 @@ double Item_func_case::val_real() } -my_decimal *Item_func_case::val_decimal(my_decimal *decimal_value) +my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; @@ -2901,6 +2917,18 @@ my_decimal *Item_func_case::val_decimal(my_decimal *decimal_value) } +bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + char buff[MAX_FIELD_WIDTH]; + String dummy_str(buff, sizeof(buff), default_charset()); + Item *item= find_item(&dummy_str); + if (!item) + return (null_value= true); + return (null_value= item->get_date(ltime, fuzzydate)); +} + + bool Item_func_case::fix_fields(THD *thd, Item **ref) { /* @@ -2946,7 +2974,10 @@ void Item_func_case::fix_length_and_dec() uint found_types= 0; if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1)))) return; - + + if (else_expr_num == -1 || args[else_expr_num]->maybe_null) + maybe_null= 1; + /* Aggregate all THEN and ELSE expression types and collations when string result @@ -2959,13 +2990,29 @@ void Item_func_case::fix_length_and_dec() agg[nagg++]= args[else_expr_num]; agg_result_type(&cached_result_type, agg, nagg); - if ((cached_result_type == STRING_RESULT) && - agg_arg_charsets(collation, agg, nagg, MY_COLL_ALLOW_CONV, 1)) - return; - cached_field_type= agg_field_type(agg, nagg); + + if (cached_result_type == STRING_RESULT) + { + if (count_string_result_length(cached_field_type, agg, nagg)) + return; + } + else + { + max_length= 0; + decimals= 0; + unsigned_flag= TRUE; + for (uint i= 0; i < nagg; i++) + agg_num_lengths(agg[i]); + if (else_expr_num != -1) + agg_num_lengths(args[else_expr_num]); + max_length= my_decimal_precision_to_length(max_length + decimals, decimals, + unsigned_flag); + } + + /* - Aggregate first expression and all THEN expression types + Aggregate first expression and all WHEN expression types and collations when string comparison */ if (first_expr_num != -1) @@ -3019,29 +3066,6 @@ void Item_func_case::fix_length_and_dec() args[i]->cmp_context= item_cmp_type(left_result_type, args[i]->result_type()); } - - if (else_expr_num == -1 || args[else_expr_num]->maybe_null) - maybe_null=1; - - max_length=0; - decimals=0; - unsigned_flag= TRUE; - if (cached_result_type == STRING_RESULT) - { - for (uint i= 0; i < ncases; i+= 2) - agg_str_lengths(args[i + 1]); - if (else_expr_num != -1) - agg_str_lengths(args[else_expr_num]); - } - else - { - for (uint i= 0; i < ncases; i+= 2) - agg_num_lengths(args[i + 1]); - if (else_expr_num != -1) - agg_num_lengths(args[else_expr_num]); - max_length= my_decimal_precision_to_length(max_length + decimals, decimals, - unsigned_flag); - } } @@ -3149,7 +3173,7 @@ double Item_func_coalesce::real_op() } -bool Item_func_coalesce::get_date(MYSQL_TIME *ltime,uint fuzzydate) +bool Item_func_coalesce::date_op(MYSQL_TIME *ltime,uint fuzzydate) { DBUG_ASSERT(fixed == 1); null_value= 0; @@ -3182,20 +3206,11 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value) void Item_func_coalesce::fix_length_and_dec() { cached_field_type= agg_field_type(args, arg_count); - agg_result_type(&hybrid_type, args, arg_count); - Item_result cmp_type; - agg_cmp_type(&cmp_type, args, arg_count); - ///< @todo let result_type() return TIME_RESULT and remove this special case - if (cmp_type == TIME_RESULT) - { - count_real_length(); - return; - } - switch (hybrid_type) { + agg_result_type(&cached_result_type, args, arg_count); + switch (cached_result_type) { case STRING_RESULT: - count_only_length(); - decimals= NOT_FIXED_DEC; - agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1); + if (count_string_result_length(cached_field_type, args, arg_count)) + return; break; case DECIMAL_RESULT: count_decimal_length(); @@ -3204,7 +3219,7 @@ void Item_func_coalesce::fix_length_and_dec() count_real_length(); break; case INT_RESULT: - count_only_length(); + count_only_length(args, arg_count); decimals= 0; break; case ROW_RESULT: diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 0ed16c86c0a..37871c31e0b 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -724,24 +724,19 @@ public: }; -class Item_func_coalesce :public Item_func_numhybrid +class Item_func_coalesce :public Item_func_hybrid_field_type { -protected: - enum_field_types cached_field_type; - Item_func_coalesce(Item *a, Item *b) :Item_func_numhybrid(a, b) {} public: - Item_func_coalesce(List<Item> &list) :Item_func_numhybrid(list) {} + Item_func_coalesce(Item *a, Item *b) :Item_func_hybrid_field_type(a, b) {} + Item_func_coalesce(List<Item> &list) :Item_func_hybrid_field_type(list) {} double real_op(); longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); + bool date_op(MYSQL_TIME *ltime,uint fuzzydate); void fix_length_and_dec(); - void find_num_type() {} - enum Item_result result_type () const { return hybrid_type; } const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } - enum_field_types field_type() const { return cached_field_type; } - bool get_date(MYSQL_TIME *ltime,uint fuzzydate); }; @@ -755,7 +750,7 @@ public: longlong int_op(); String *str_op(String *str); my_decimal *decimal_op(my_decimal *); - enum_field_types field_type() const; + bool date_op(MYSQL_TIME *ltime,uint fuzzydate); void fix_length_and_dec(); void update_used_tables() { @@ -768,20 +763,17 @@ public: }; -class Item_func_if :public Item_func +class Item_func_if :public Item_func_hybrid_field_type { - enum Item_result cached_result_type; - enum_field_types cached_field_type; public: Item_func_if(Item *a,Item *b,Item *c) - :Item_func(a,b,c), cached_result_type(INT_RESULT) + :Item_func_hybrid_field_type(a,b,c) {} - double val_real(); - longlong val_int(); - String *val_str(String *str); - my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return cached_result_type; } - enum_field_types field_type() const { return cached_field_type; } + bool date_op(MYSQL_TIME *ltime, uint fuzzydate); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); + String *str_op(String *); bool fix_fields(THD *, Item **); void fix_length_and_dec(); void update_used_tables() @@ -1216,21 +1208,20 @@ public: function and only comparators for there result types are used. */ -class Item_func_case :public Item_func +class Item_func_case :public Item_func_hybrid_field_type { int first_expr_num, else_expr_num; - enum Item_result cached_result_type, left_result_type; + enum Item_result left_result_type; String tmp_value; uint ncases; Item_result cmp_type; DTCollation cmp_collation; - enum_field_types cached_field_type; cmp_item *cmp_items[6]; /* For all result types */ cmp_item *case_item; public: Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg) - :Item_func(), first_expr_num(-1), else_expr_num(-1), - cached_result_type(INT_RESULT), left_result_type(INT_RESULT), case_item(0) + :Item_func_hybrid_field_type(), first_expr_num(-1), else_expr_num(-1), + left_result_type(INT_RESULT), case_item(0) { ncases= list.elements; if (first_expr_arg) @@ -1246,10 +1237,11 @@ public: set_arguments(list); bzero(&cmp_items, sizeof(cmp_items)); } - double val_real(); - longlong val_int(); - String *val_str(String *); - my_decimal *val_decimal(my_decimal *); + double real_op(); + longlong int_op(); + String *str_op(String *); + my_decimal *decimal_op(my_decimal *); + bool date_op(MYSQL_TIME *ltime, uint fuzzydate); bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); void update_used_tables() @@ -1260,8 +1252,6 @@ public: } uint decimal_precision() const; table_map not_null_tables() const { return 0; } - enum Item_result result_type () const { return cached_result_type; } - enum_field_types field_type() const { return cached_field_type; } const char *func_name() const { return "case"; } virtual void print(String *str, enum_query_type query_type); Item *find_item(String *str); diff --git a/sql/item_func.cc b/sql/item_func.cc index 9079de6f06e..98da660f8a7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -594,6 +594,30 @@ void Item_func_numhybrid::fix_num_length_and_dec() {} + +/** + Count max_length and decimals for temporal functions. + + @param item Argument array + @param nitems Number of arguments in the array. + + @retval False on success, true on error. +*/ +void Item_func::count_datetime_length(Item **item, uint nitems) +{ + unsigned_flag= 0; + decimals= 0; + if (field_type() != MYSQL_TYPE_DATE) + { + for (uint i= 0; i < nitems; i++) + set_if_bigger(decimals, item[i]->decimals); + } + set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); + uint len= decimals ? (decimals + 1) : 0; + len+= mysql_temporal_int_part_length(field_type()); + fix_char_length(len); +} + /** Set max_length/decimals of function if function is fixed point and result length/precision depends on argument ones. @@ -620,14 +644,14 @@ void Item_func::count_decimal_length() Set max_length of if it is maximum length of its arguments. */ -void Item_func::count_only_length() +void Item_func::count_only_length(Item **item, uint nitems) { max_length= 0; unsigned_flag= 0; - for (uint i=0 ; i < arg_count ; i++) + for (uint i= 0; i < nitems ; i++) { - set_if_bigger(max_length, args[i]->max_length); - set_if_bigger(unsigned_flag, args[i]->unsigned_flag); + set_if_bigger(max_length, item[i]->max_length); + set_if_bigger(unsigned_flag, item[i]->unsigned_flag); } } @@ -663,6 +687,30 @@ void Item_func::count_real_length() } +/** + Calculate max_length and decimals for STRING_RESULT functions. + + @param field_type Field type. + @param items Argument array. + @param nitems Number of arguments. + + @retval False on success, true on error. +*/ +bool Item_func::count_string_result_length(enum_field_types field_type, + Item **items, uint nitems) +{ + if (agg_arg_charsets(collation, items, nitems, MY_COLL_ALLOW_CONV, 1)) + return true; + if (is_temporal_type(field_type)) + count_datetime_length(items, nitems); + else + { + decimals= NOT_FIXED_DEC; // TODO + count_only_length(items, nitems); + } + return false; +} + void Item_func::signal_divide_by_null() { @@ -735,26 +783,26 @@ void Item_num_op::find_num_type(void) { count_real_length(); max_length= float_length(decimals); - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; } else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT || r0 == TIME_RESULT || r1 == TIME_RESULT) { - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; result_precision(); fix_decimals(); } else { DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); - hybrid_type=INT_RESULT; + cached_result_type=INT_RESULT; result_precision(); decimals= 0; } DBUG_PRINT("info", ("Type: %s", - (hybrid_type == REAL_RESULT ? "REAL_RESULT" : - hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : - hybrid_type == INT_RESULT ? "INT_RESULT" : + (cached_result_type == REAL_RESULT ? "REAL_RESULT" : + cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + cached_result_type == INT_RESULT ? "INT_RESULT" : "--ILLEGAL!!!--"))); DBUG_VOID_RETURN; } @@ -770,17 +818,17 @@ void Item_func_num1::find_num_type() { DBUG_ENTER("Item_func_num1::find_num_type"); DBUG_PRINT("info", ("name %s", func_name())); - switch (hybrid_type= args[0]->cast_to_int_type()) { + switch (cached_result_type= args[0]->cast_to_int_type()) { case INT_RESULT: unsigned_flag= args[0]->unsigned_flag; break; case STRING_RESULT: case REAL_RESULT: - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; max_length= float_length(decimals); break; case TIME_RESULT: - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; case DECIMAL_RESULT: break; case ROW_RESULT: @@ -788,9 +836,9 @@ void Item_func_num1::find_num_type() DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: %s", - (hybrid_type == REAL_RESULT ? "REAL_RESULT" : - hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : - hybrid_type == INT_RESULT ? "INT_RESULT" : + (cached_result_type == REAL_RESULT ? "REAL_RESULT" : + cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + cached_result_type == INT_RESULT ? "INT_RESULT" : "--ILLEGAL!!!--"))); DBUG_VOID_RETURN; } @@ -810,10 +858,10 @@ void Item_func_numhybrid::fix_length_and_dec() } -String *Item_func_numhybrid::val_str(String *str) +String *Item_func_hybrid_result_type::val_str(String *str) { DBUG_ASSERT(fixed == 1); - switch (hybrid_type) { + switch (cached_result_type) { case DECIMAL_RESULT: { my_decimal decimal_value, *val; @@ -840,6 +888,21 @@ String *Item_func_numhybrid::val_str(String *str) break; } case STRING_RESULT: + if (is_temporal_type(field_type())) + { + MYSQL_TIME ltime; + if (date_op(<ime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) || + str->alloc(MAX_DATE_STRING_REP_LENGTH)) + { + null_value= 1; + return (String *) 0; + } + ltime.time_type= mysql_type_to_time_type(field_type()); + str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); + str->set_charset(&my_charset_bin); + return str; + } return str_op(&str_value); case TIME_RESULT: case ROW_RESULT: @@ -850,10 +913,10 @@ String *Item_func_numhybrid::val_str(String *str) } -double Item_func_numhybrid::val_real() +double Item_func_hybrid_result_type::val_real() { DBUG_ASSERT(fixed == 1); - switch (hybrid_type) { + switch (cached_result_type) { case DECIMAL_RESULT: { my_decimal decimal_value, *val; @@ -872,6 +935,18 @@ double Item_func_numhybrid::val_real() return real_op(); case STRING_RESULT: { + if (is_temporal_type(field_type())) + { + MYSQL_TIME ltime; + if (date_op(<ime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0 )) + { + null_value= 1; + return 0; + } + ltime.time_type= mysql_type_to_time_type(field_type()); + return TIME_to_double(<ime); + } char *end_not_used; int err_not_used; String *res= str_op(&str_value); @@ -887,10 +962,10 @@ double Item_func_numhybrid::val_real() } -longlong Item_func_numhybrid::val_int() +longlong Item_func_hybrid_result_type::val_int() { DBUG_ASSERT(fixed == 1); - switch (hybrid_type) { + switch (cached_result_type) { case DECIMAL_RESULT: { my_decimal decimal_value, *val; @@ -906,6 +981,18 @@ longlong Item_func_numhybrid::val_int() return (longlong) rint(real_op()); case STRING_RESULT: { + if (is_temporal_type(field_type())) + { + MYSQL_TIME ltime; + if (date_op(<ime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) + { + null_value= 1; + return 0; + } + ltime.time_type= mysql_type_to_time_type(field_type()); + return TIME_to_ulonglong(<ime); + } int err_not_used; String *res; if (!(res= str_op(&str_value))) @@ -924,11 +1011,11 @@ longlong Item_func_numhybrid::val_int() } -my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) +my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value) { my_decimal *val= decimal_value; DBUG_ASSERT(fixed == 1); - switch (hybrid_type) { + switch (cached_result_type) { case DECIMAL_RESULT: val= decimal_op(decimal_value); break; @@ -946,6 +1033,19 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) } case STRING_RESULT: { + if (is_temporal_type(field_type())) + { + MYSQL_TIME ltime; + if (date_op(<ime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) + { + my_decimal_set_zero(decimal_value); + null_value= 1; + return 0; + } + ltime.time_type= mysql_type_to_time_type(field_type()); + return date2my_decimal(<ime, decimal_value); + } String *res; if (!(res= str_op(&str_value))) return NULL; @@ -963,6 +1063,62 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) } +bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, uint fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + switch (cached_result_type) { + case DECIMAL_RESULT: + { + my_decimal value, *res; + if (!(res= decimal_op(&value)) || + decimal_to_datetime_with_warn(res, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case INT_RESULT: + { + longlong value= int_op(); + if (null_value || int_to_datetime_with_warn(value, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case REAL_RESULT: + { + double value= real_op(); + if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case STRING_RESULT: + { + if (is_temporal_type(field_type())) + return date_op(ltime, fuzzydate); + char buff[40]; + String tmp(buff,sizeof(buff), &my_charset_bin),*res; + if (!(res= str_op(&tmp)) || + str_to_datetime_with_warn(res->ptr(), res->length(), + ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + goto err; + break; + break; + } + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); + } + + return (null_value= 0); + +err: + bzero(ltime, sizeof(*ltime)); + return null_value|= !(fuzzydate & TIME_FUZZY_DATES); +} + + void Item_func_signed::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("cast(")); @@ -1471,7 +1627,7 @@ void Item_func_div::fix_length_and_dec() DBUG_ENTER("Item_func_div::fix_length_and_dec"); prec_increment= current_thd->variables.div_precincrement; Item_num_op::fix_length_and_dec(); - switch (hybrid_type) { + switch (cached_result_type) { case REAL_RESULT: { decimals=max(args[0]->decimals,args[1]->decimals)+prec_increment; @@ -1487,7 +1643,7 @@ void Item_func_div::fix_length_and_dec() break; } case INT_RESULT: - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); result_precision(); break; @@ -1670,7 +1826,7 @@ void Item_func_neg::fix_length_and_dec() Use val() to get value as arg_type doesn't mean that item is Item_int or Item_real due to existence of Item_param. */ - if (hybrid_type == INT_RESULT && args[0]->const_item()) + if (cached_result_type == INT_RESULT && args[0]->const_item()) { longlong val= args[0]->val_int(); if ((ulonglong) val >= (ulonglong) LONGLONG_MIN && @@ -1681,7 +1837,7 @@ void Item_func_neg::fix_length_and_dec() Ensure that result is converted to DECIMAL, as longlong can't hold the negated number */ - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); } } @@ -1968,11 +2124,11 @@ void Item_func_int_val::find_num_type() { DBUG_ENTER("Item_func_int_val::find_num_type"); DBUG_PRINT("info", ("name %s", func_name())); - switch (hybrid_type= args[0]->cast_to_int_type()) + switch (cached_result_type= args[0]->cast_to_int_type()) { case STRING_RESULT: case REAL_RESULT: - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; max_length= float_length(decimals); break; case INT_RESULT: @@ -1985,12 +2141,12 @@ void Item_func_int_val::find_num_type() if ((args[0]->max_length - args[0]->decimals) >= (DECIMAL_LONGLONG_DIGITS - 2)) { - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; } else { unsigned_flag= args[0]->unsigned_flag; - hybrid_type= INT_RESULT; + cached_result_type= INT_RESULT; } break; case ROW_RESULT: @@ -1998,9 +2154,9 @@ void Item_func_int_val::find_num_type() DBUG_ASSERT(0); } DBUG_PRINT("info", ("Type: %s", - (hybrid_type == REAL_RESULT ? "REAL_RESULT" : - hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : - hybrid_type == INT_RESULT ? "INT_RESULT" : + (cached_result_type == REAL_RESULT ? "REAL_RESULT" : + cached_result_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + cached_result_type == INT_RESULT ? "INT_RESULT" : "--ILLEGAL!!!--"))); DBUG_VOID_RETURN; @@ -2115,10 +2271,10 @@ void Item_func_round::fix_length_and_dec() if (args[0]->result_type() == DECIMAL_RESULT) { max_length++; - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; } else - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; return; } @@ -2136,14 +2292,14 @@ void Item_func_round::fix_length_and_dec() { decimals= min(decimals_to_set, NOT_FIXED_DEC); max_length= float_length(decimals); - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; return; } switch (args[0]->result_type()) { case REAL_RESULT: case STRING_RESULT: - hybrid_type= REAL_RESULT; + cached_result_type= REAL_RESULT; decimals= min(decimals_to_set, NOT_FIXED_DEC); max_length= float_length(decimals); break; @@ -2153,14 +2309,14 @@ void Item_func_round::fix_length_and_dec() int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned); max_length= args[0]->max_length + length_can_increase; /* Here we can keep INT_RESULT */ - hybrid_type= INT_RESULT; + cached_result_type= INT_RESULT; decimals= 0; break; } /* fall through */ case DECIMAL_RESULT: { - hybrid_type= DECIMAL_RESULT; + cached_result_type= DECIMAL_RESULT; decimals_to_set= min(DECIMAL_MAX_SCALE, decimals_to_set); int decimals_delta= args[0]->decimals - decimals_to_set; int precision= args[0]->decimal_precision(); diff --git a/sql/item_func.h b/sql/item_func.h index c4b21b6287d..44070354990 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -148,9 +148,12 @@ public: void print_op(String *str, enum_query_type query_type); void print_args(String *str, uint from, enum_query_type query_type); virtual void fix_num_length_and_dec(); - void count_only_length(); + void count_only_length(Item **item, uint nitems); void count_real_length(); void count_decimal_length(); + void count_datetime_length(Item **item, uint nitems); + bool count_string_result_length(enum_field_types field_type, + Item **item, uint nitems); inline bool get_arg0_date(MYSQL_TIME *ltime, uint fuzzy_date) { return (null_value=args[0]->get_date(ltime, fuzzy_date)); @@ -327,38 +330,33 @@ public: }; -class Item_func_numhybrid: public Item_func +class Item_func_hybrid_result_type: public Item_func { protected: - Item_result hybrid_type; + Item_result cached_result_type; + public: - Item_func_numhybrid() :Item_func(), hybrid_type(REAL_RESULT) + Item_func_hybrid_result_type() :Item_func(), cached_result_type(REAL_RESULT) {} - Item_func_numhybrid(Item *a) :Item_func(a), hybrid_type(REAL_RESULT) + Item_func_hybrid_result_type(Item *a) :Item_func(a), cached_result_type(REAL_RESULT) {} - Item_func_numhybrid(Item *a,Item *b) - :Item_func(a,b), hybrid_type(REAL_RESULT) + Item_func_hybrid_result_type(Item *a,Item *b) + :Item_func(a,b), cached_result_type(REAL_RESULT) {} - Item_func_numhybrid(List<Item> &list) - :Item_func(list), hybrid_type(REAL_RESULT) + Item_func_hybrid_result_type(Item *a,Item *b,Item *c) + :Item_func(a,b,c), cached_result_type(REAL_RESULT) + {} + Item_func_hybrid_result_type(List<Item> &list) + :Item_func(list), cached_result_type(REAL_RESULT) {} - enum Item_result result_type () const { return hybrid_type; } - void fix_length_and_dec(); - void fix_num_length_and_dec(); - virtual void find_num_type()= 0; /* To be called from fix_length_and_dec */ - - inline void fix_decimals() - { - DBUG_ASSERT(result_type() == DECIMAL_RESULT); - if (decimals == NOT_FIXED_DEC) - set_if_smaller(decimals, max_length - 1); - } + enum Item_result result_type () const { return cached_result_type; } double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); String *val_str(String*str); + bool get_date(MYSQL_TIME *res, uint fuzzy_date); /** @brief Performs the operation that this functions implements when the @@ -395,9 +393,75 @@ public: @return The result of the operation. */ virtual String *str_op(String *)= 0; - bool is_null() { update_null_value(); return null_value; } + + /** + @brief Performs the operation that this functions implements when + field type is a temporal type. + @return The result of the operation. + */ + virtual bool date_op(MYSQL_TIME *res, uint fuzzy_date)= 0; + }; + + +class Item_func_hybrid_field_type :public Item_func_hybrid_result_type +{ +protected: + enum_field_types cached_field_type; +public: + Item_func_hybrid_field_type() + :Item_func_hybrid_result_type(), cached_field_type(MYSQL_TYPE_DOUBLE) + {} + Item_func_hybrid_field_type(Item *a, Item *b) + :Item_func_hybrid_result_type(a, b), cached_field_type(MYSQL_TYPE_DOUBLE) + {} + Item_func_hybrid_field_type(Item *a, Item *b, Item *c) + :Item_func_hybrid_result_type(a, b, c), + cached_field_type(MYSQL_TYPE_DOUBLE) + {} + Item_func_hybrid_field_type(List<Item> &list) + :Item_func_hybrid_result_type(list), + cached_field_type(MYSQL_TYPE_DOUBLE) + {} + enum_field_types field_type() const { return cached_field_type; } +}; + + + +class Item_func_numhybrid: public Item_func_hybrid_result_type +{ +protected: + + inline void fix_decimals() + { + DBUG_ASSERT(result_type() == DECIMAL_RESULT); + if (decimals == NOT_FIXED_DEC) + set_if_smaller(decimals, max_length - 1); + } + +public: + Item_func_numhybrid() :Item_func_hybrid_result_type() + {} + Item_func_numhybrid(Item *a) :Item_func_hybrid_result_type(a) + {} + Item_func_numhybrid(Item *a,Item *b) + :Item_func_hybrid_result_type(a,b) + {} + Item_func_numhybrid(Item *a,Item *b,Item *c) + :Item_func_hybrid_result_type(a,b,c) + {} + Item_func_numhybrid(List<Item> &list) + :Item_func_hybrid_result_type(list) + {} + void fix_length_and_dec(); + void fix_num_length_and_dec(); + virtual void find_num_type()= 0; /* To be called from fix_length_and_dec */ + String *str_op(String *str) { DBUG_ASSERT(0); return 0; } + bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } +}; + + /* function where type of result detected by first argument */ class Item_func_num1: public Item_func_numhybrid { @@ -407,7 +471,6 @@ public: void fix_num_length_and_dec(); void find_num_type(); - String *str_op(String *str) { DBUG_ASSERT(0); return 0; } }; @@ -424,7 +487,6 @@ class Item_num_op :public Item_func_numhybrid } void find_num_type(); - String *str_op(String *str) { DBUG_ASSERT(0); return 0; } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index b17e1c794ee..b0245e6f743 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -372,9 +372,11 @@ public: max_length=17 + (decimals ? decimals + 1 : 0); set_persist_maybe_null(1); } - void find_num_type() { hybrid_type= decimals ? DECIMAL_RESULT : INT_RESULT; } + void find_num_type() + { cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT; } double real_op() { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } + bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } }; @@ -451,12 +453,8 @@ public: { return save_date_in_field(field); } void fix_length_and_dec() { - static const uint max_time_type_width[5]= - { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH, - MAX_DATETIME_WIDTH, MIN_TIME_WIDTH }; - set_persist_maybe_null(1); - max_length= max_time_type_width[mysql_type_to_time_type(field_type())+2]; + max_length= mysql_temporal_int_part_length(field_type()); if (decimals) { if (decimals == NOT_FIXED_DEC) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1bf5816430c..913ab66a843 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -930,6 +930,17 @@ mysql_type_to_time_type(enum enum_field_types mysql_type) } } + +static inline uint +mysql_temporal_int_part_length(enum enum_field_types mysql_type) +{ + static uint max_time_type_width[5]= + { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH, + MAX_DATETIME_WIDTH, MIN_TIME_WIDTH }; + return max_time_type_width[mysql_type_to_time_type(mysql_type)+2]; +} + + #include "sql_list.h" #include "sql_map.h" #include "handler.h" |