From cd32f842148d57c33e13a22a663278b7faa4ed24 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 10 May 2017 08:30:56 +0400 Subject: [PATCH] MDEV-12770 Add Type_handler::decimal_precision() + MDEV-12769 This patch for MDEV-12770 is also fixing: MDEV-12769 Arithmetic operators with temporal types create excessive column types --- mysql-test/r/func_time.result | 18 ++++---- mysql-test/r/type_datetime_hires.result | 12 ++--- mysql-test/r/type_time.result | 12 ++--- mysql-test/r/type_time_hires.result | 12 ++--- mysql-test/r/type_timestamp_hires.result | 12 ++--- sql/item.cc | 22 ---------- sql/item.h | 5 ++- sql/sql_type.cc | 56 ++++++++++++++++++++++++ sql/sql_type.h | 14 ++++++ 9 files changed, 107 insertions(+), 56 deletions(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index a05c6a78cdb..4a37c56d28f 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -950,10 +950,10 @@ sec_to_time(1) + 0, from_unixtime(1) + 0; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `now() - now()` bigint(21) NOT NULL, - `curtime() - curtime()` bigint(12) NOT NULL, - `sec_to_time(1) + 0` bigint(12) DEFAULT NULL, - `from_unixtime(1) + 0` bigint(21) DEFAULT NULL + `now() - now()` bigint(16) NOT NULL, + `curtime() - curtime()` int(9) NOT NULL, + `sec_to_time(1) + 0` int(9) DEFAULT NULL, + `from_unixtime(1) + 0` bigint(16) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; SELECT SEC_TO_TIME(3300000); @@ -2054,11 +2054,11 @@ SEC_TO_TIME(1.123456)+0.1, SEC_TO_TIME(1.1234567)+0.1; SHOW COLUMNS FROM t1; Field Type Null Key Default Extra -SEC_TO_TIME(1)+0.1 decimal(12,1) YES NULL -SEC_TO_TIME(1.1)+0.1 decimal(13,1) YES NULL -SEC_TO_TIME(1.12)+0.1 decimal(14,2) YES NULL -SEC_TO_TIME(1.123456)+0.1 decimal(18,6) YES NULL -SEC_TO_TIME(1.1234567)+0.1 decimal(18,6) YES NULL +SEC_TO_TIME(1)+0.1 decimal(9,1) YES NULL +SEC_TO_TIME(1.1)+0.1 decimal(9,1) YES NULL +SEC_TO_TIME(1.12)+0.1 decimal(10,2) YES NULL +SEC_TO_TIME(1.123456)+0.1 decimal(14,6) YES NULL +SEC_TO_TIME(1.1234567)+0.1 decimal(14,6) YES NULL DROP TABLE t1; CREATE TABLE t1 (a DATE) ENGINE=MyISAM; INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23'); diff --git a/mysql-test/r/type_datetime_hires.result b/mysql-test/r/type_datetime_hires.result index e2c2f83a96c..003ecf15e69 100644 --- a/mysql-test/r/type_datetime_hires.result +++ b/mysql-test/r/type_datetime_hires.result @@ -131,18 +131,18 @@ show create table t2; Table Create Table t2 CREATE TABLE `t2` ( `a` datetime(4) DEFAULT NULL, - `a+0` decimal(25,4) DEFAULT NULL, - `a-1` decimal(25,4) DEFAULT NULL, - `a*1` decimal(25,4) DEFAULT NULL, - `a/2` decimal(28,8) DEFAULT NULL + `a+0` decimal(19,4) DEFAULT NULL, + `a-1` decimal(19,4) DEFAULT NULL, + `a*1` decimal(19,4) DEFAULT NULL, + `a/2` decimal(22,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 show create table t3; Table Create Table t3 CREATE TABLE `t3` ( `max(a)` datetime(4) DEFAULT NULL, `min(a)` datetime(4) DEFAULT NULL, - `sum(a)` decimal(46,4) DEFAULT NULL, - `avg(a)` decimal(28,8) DEFAULT NULL + `sum(a)` decimal(40,4) DEFAULT NULL, + `avg(a)` decimal(22,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1, t2, t3; create table t1 (f0_datetime datetime(0), f1_datetime datetime(1), f2_datetime datetime(2), f3_datetime datetime(3), f4_datetime datetime(4), f5_datetime datetime(5), f6_datetime datetime(6)); diff --git a/mysql-test/r/type_time.result b/mysql-test/r/type_time.result index 3f125598f9b..8e1b626342a 100644 --- a/mysql-test/r/type_time.result +++ b/mysql-test/r/type_time.result @@ -221,7 +221,7 @@ MAX(t0)+1 101011 SHOW COLUMNS FROM t2; Field Type Null Key Default Extra -MAX(t0)+1 bigint(12) YES NULL +MAX(t0)+1 int(9) YES NULL DROP TABLE t2,t1; CREATE TABLE t1 (t0 TIME); INSERT INTO t1 VALUES ('10:10:10'); @@ -234,7 +234,7 @@ 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 +MAX(t0)+1.1 decimal(9,1) YES NULL DROP TABLE t2,t1; CREATE TABLE t1 (t0 TIME); INSERT INTO t1 VALUES ('10:10:10'); @@ -260,7 +260,7 @@ MAX(t1)+1 101011.0 SHOW COLUMNS FROM t2; Field Type Null Key Default Extra -MAX(t1)+1 decimal(13,1) YES NULL +MAX(t1)+1 decimal(9,1) YES NULL DROP TABLE t2,t1; CREATE TABLE t1 (t0 DATETIME); INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); @@ -273,7 +273,7 @@ MAX(t0)+1 20010101101011 SHOW COLUMNS FROM t2; Field Type Null Key Default Extra -MAX(t0)+1 bigint(21) YES NULL +MAX(t0)+1 bigint(16) YES NULL DROP TABLE t2,t1; CREATE TABLE t1 (t0 DATETIME); INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); @@ -286,7 +286,7 @@ 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 +MAX(t0)+1.1 decimal(16,1) YES NULL DROP TABLE t2,t1; CREATE TABLE t1 (t0 DATETIME); INSERT INTO t1 VALUES ('2001-01-01 10:10:10'); @@ -312,7 +312,7 @@ MAX(t1)+1 20010101101011.0 SHOW COLUMNS FROM t2; Field Type Null Key Default Extra -MAX(t1)+1 decimal(22,1) YES NULL +MAX(t1)+1 decimal(16,1) YES NULL DROP TABLE t2,t1; # # MDEV-4858 Wrong results for a huge unsigned value inserted into a TIME column diff --git a/mysql-test/r/type_time_hires.result b/mysql-test/r/type_time_hires.result index a9345a7e83f..47185116bea 100644 --- a/mysql-test/r/type_time_hires.result +++ b/mysql-test/r/type_time_hires.result @@ -145,18 +145,18 @@ show create table t2; Table Create Table t2 CREATE TABLE `t2` ( `a` time(4) DEFAULT NULL, - `a+0` decimal(16,4) DEFAULT NULL, - `a-1` decimal(16,4) DEFAULT NULL, - `a*1` decimal(16,4) DEFAULT NULL, - `a/2` decimal(19,8) DEFAULT NULL + `a+0` decimal(12,4) DEFAULT NULL, + `a-1` decimal(12,4) DEFAULT NULL, + `a*1` decimal(12,4) DEFAULT NULL, + `a/2` decimal(15,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 show create table t3; Table Create Table t3 CREATE TABLE `t3` ( `max(a)` time(4) DEFAULT NULL, `min(a)` time(4) DEFAULT NULL, - `sum(a)` decimal(37,4) DEFAULT NULL, - `avg(a)` decimal(19,8) DEFAULT NULL + `sum(a)` decimal(33,4) DEFAULT NULL, + `avg(a)` decimal(15,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1, t2, t3; create table t1 (f0_time time(0), f1_time time(1), f2_time time(2), f3_time time(3), f4_time time(4), f5_time time(5), f6_time time(6)); diff --git a/mysql-test/r/type_timestamp_hires.result b/mysql-test/r/type_timestamp_hires.result index 7179277aa9a..fa6adc075ed 100644 --- a/mysql-test/r/type_timestamp_hires.result +++ b/mysql-test/r/type_timestamp_hires.result @@ -131,18 +131,18 @@ show create table t2; Table Create Table t2 CREATE TABLE `t2` ( `a` timestamp(4) NOT NULL DEFAULT current_timestamp(4) ON UPDATE current_timestamp(4), - `a+0` decimal(25,4) NOT NULL, - `a-1` decimal(25,4) NOT NULL, - `a*1` decimal(25,4) NOT NULL, - `a/2` decimal(28,8) DEFAULT NULL + `a+0` decimal(19,4) NOT NULL, + `a-1` decimal(19,4) NOT NULL, + `a*1` decimal(19,4) NOT NULL, + `a/2` decimal(22,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 show create table t3; Table Create Table t3 CREATE TABLE `t3` ( `max(a)` timestamp(4) NULL DEFAULT NULL, `min(a)` timestamp(4) NULL DEFAULT NULL, - `sum(a)` decimal(46,4) DEFAULT NULL, - `avg(a)` decimal(28,8) DEFAULT NULL + `sum(a)` decimal(40,4) DEFAULT NULL, + `avg(a)` decimal(22,8) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1, t2, t3; create table t1 (f0_timestamp timestamp(0), f1_timestamp timestamp(1), f2_timestamp timestamp(2), f3_timestamp timestamp(3), f4_timestamp timestamp(4), f5_timestamp timestamp(5), f6_timestamp timestamp(6)); diff --git a/sql/item.cc b/sql/item.cc index e775862ca3a..3ecca83ea17 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -541,28 +541,6 @@ Item::Item(THD *thd, Item *item): } -uint Item::decimal_precision() const -{ - Item_result restype= result_type(); - - if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - { - uint prec= - my_decimal_length_to_precision(max_char_length(), decimals, - unsigned_flag); - return MY_MIN(prec, DECIMAL_MAX_PRECISION); - } - uint res= max_char_length(); - /* - Return at least one decimal digit, even if Item::max_char_length() - returned 0. This is important to avoid attempts to create fields of types - INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL: - CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a; - */ - return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; -} - - void Item::print_parenthesised(String *str, enum_query_type query_type, enum precedence parent_prec) { diff --git a/sql/item.h b/sql/item.h index ad34320c560..c81f4833ebd 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1143,7 +1143,10 @@ public: inline uint float_length(uint decimals_par) const { return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} /* Returns total number of decimal digits */ - virtual uint decimal_precision() const; + virtual uint decimal_precision() const + { + return type_handler()->Item_decimal_precision(this); + } /* Returns the number of integer part digits only */ inline int decimal_int_part() const { return my_decimal_int_part(decimal_precision(), decimals); } diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 7d0bd9004da..f632b47680c 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -3953,6 +3953,62 @@ uint Type_handler_temporal_result:: /***************************************************************************/ +uint Type_handler_string_result::Item_decimal_precision(const Item *item) const +{ + uint res= item->max_char_length(); + /* + Return at least one decimal digit, even if Item::max_char_length() + returned 0. This is important to avoid attempts to create fields of types + INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL: + CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a; + */ + return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; +} + +uint Type_handler_real_result::Item_decimal_precision(const Item *item) const +{ + uint res= item->max_char_length(); + return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; +} + +uint Type_handler_decimal_result::Item_decimal_precision(const Item *item) const +{ + uint prec= my_decimal_length_to_precision(item->max_char_length(), + item->decimals, + item->unsigned_flag); + return MY_MIN(prec, DECIMAL_MAX_PRECISION); +} + +uint Type_handler_int_result::Item_decimal_precision(const Item *item) const +{ + uint prec= my_decimal_length_to_precision(item->max_char_length(), + item->decimals, + item->unsigned_flag); + return MY_MIN(prec, DECIMAL_MAX_PRECISION); +} + +uint Type_handler_time_common::Item_decimal_precision(const Item *item) const +{ + return 7 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +uint Type_handler_date_common::Item_decimal_precision(const Item *item) const +{ + return 8; +} + +uint Type_handler_datetime_common::Item_decimal_precision(const Item *item) const +{ + return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +uint Type_handler_timestamp_common::Item_decimal_precision(const Item *item) const +{ + return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +/***************************************************************************/ + bool Type_handler_real_result:: subquery_type_allows_materialization(const Item *inner, const Item *outer) const diff --git a/sql/sql_type.h b/sql/sql_type.h index c741e2117ec..8abc8e61510 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -639,6 +639,7 @@ public: virtual uint Item_time_precision(Item *item) const; virtual uint Item_datetime_precision(Item *item) const; virtual uint Item_decimal_scale(const Item *item) const; + virtual uint Item_decimal_precision(const Item *item) const= 0; /* Returns how many digits a divisor adds into a division result. See Item::divisor_precision_increment() in item.h for more comments. @@ -935,6 +936,11 @@ public: DBUG_ASSERT(0); return 0; } + uint Item_decimal_precision(const Item *item) const + { + DBUG_ASSERT(0); + return DECIMAL_MAX_PRECISION; + } bool Item_save_in_value(Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -1156,6 +1162,7 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + uint Item_decimal_precision(const Item *item) const; bool Item_save_in_value(Item *item, st_value *value) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; @@ -1217,6 +1224,7 @@ public: const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; uint32 max_display_length(const Item *item) const; + uint Item_decimal_precision(const Item *item) const; bool Item_save_in_value(Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -1278,6 +1286,7 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + uint Item_decimal_precision(const Item *item) const; bool Item_save_in_value(Item *item, st_value *value) const; int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; @@ -1408,6 +1417,7 @@ public: { return Item_temporal_precision(item, false); } + uint Item_decimal_precision(const Item *item) const; bool Item_save_in_value(Item *item, st_value *value) const; bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -1703,6 +1713,7 @@ public: { return Item_decimal_scale_with_seconds(item); } + uint Item_decimal_precision(const Item *item) const; uint Item_divisor_precision_increment(const Item *item) const { return Item_divisor_precision_increment_with_seconds(item); @@ -1780,6 +1791,7 @@ public: { return MYSQL_TIMESTAMP_DATE; } + uint Item_decimal_precision(const Item *item) const; String *print_item_value(THD *thd, Item *item, String *str) const; bool Item_hybrid_func_fix_attributes(THD *thd, Item_hybrid_func *func, Item **items, uint nitems) const; @@ -1828,6 +1840,7 @@ public: { return Item_decimal_scale_with_seconds(item); } + uint Item_decimal_precision(const Item *item) const; uint Item_divisor_precision_increment(const Item *item) const { return Item_divisor_precision_increment_with_seconds(item); @@ -1889,6 +1902,7 @@ public: { return Item_decimal_scale_with_seconds(item); } + uint Item_decimal_precision(const Item *item) const; uint Item_divisor_precision_increment(const Item *item) const { return Item_divisor_precision_increment_with_seconds(item);