← Back to team overview

maria-developers team mailing list archive

MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types

 

Hello Sergei,

Please review a new version of the patch for MDEV-4511,
it also fixes more crashing problems found, as well as
non-crashing problems reported in MDEV-6302


I removed find_num_type() and fix_num_length_and_dec() which
were hard to track, and for some Items these methods were
erroneously called twice.


fix_num_length_and_dec() now only exists as a non-virtual method in
Item_sum and Item_udf_func.


The patch is for 5.3, but it is still valid for 10.0, as
we still have temporal functions with NOT_FIXED_DEC in 10.0.

Thanks.
=== modified file 'mysql-test/r/func_time.result'
--- mysql-test/r/func_time.result	2014-06-04 16:32:57 +0000
+++ mysql-test/r/func_time.result	2014-06-09 09:17:01 +0000
@@ -1914,6 +1914,136 @@ SELECT 1 FROM DUAL WHERE MINUTE(TIMEDIFF
 SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12'));
 1
 #
+# MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types
+#
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a FROM t1 GROUP BY TIMEDIFF('2004-06-12',a) * 1;
+a
+2005-05-04
+Warnings:
+Warning	1292	Truncated incorrect time value: '2004-06-12'
+Warning	1292	Truncated incorrect time value: '2004-06-12'
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a FROM t1 GROUP BY ADDTIME(a,'10')*1;
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY SEC_TO_TIME(concat(a,'10'))*1;
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY ADDTIME(timestamp('2001-01-01 00:00:00'),CAST(a AS SIGNED)&0xF)*1;
+a
+2005-05-04
+2000-02-23
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY STR_TO_DATE(a,concat('%Y-%m-%d.%f',if(rand(),'','')))*1;
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+STR_TO_DATE('2001-01-01', '%Y-%m-%d') AS date_only,
+STR_TO_DATE('10:10:10', '%H:%i:%s') AS time_only,
+STR_TO_DATE('10:10:10.123', '%H:%i:%s.%f') AS time_microsecond,
+STR_TO_DATE('2001-01-01 10:10:10', '%Y-%m-%d %H:%i:%s') AS date_time,
+STR_TO_DATE('2001-01-01 10:10:10.123', '%Y-%m-%d %H:%i:%s.%f') AS date_time_microsecond;
+SHOW COLUMNS FROM t1;
+Field	Type	Null	Key	Default	Extra
+date_only	date	YES		NULL	
+time_only	time	YES		NULL	
+time_microsecond	time(6)	YES		NULL	
+date_time	datetime	YES		NULL	
+date_time_microsecond	datetime(6)	YES		NULL	
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+SEC_TO_TIME(1)+0.1,
+SEC_TO_TIME(1.1)+0.1,
+SEC_TO_TIME(1.12)+0.1,
+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	
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))*1;
+a
+2000-02-23
+2005-05-04
+SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')))*1;
+a
+2005-05-04
+2000-02-23
+SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')));
+a
+2005-05-04
+2000-02-23
+SELECT * FROM t1 GROUP BY ABS(FROM_UNIXTIME(concat(a,'10')));
+a
+2000-02-23
+2005-05-04
+SELECT * FROM t1 GROUP BY @a:=(FROM_UNIXTIME(concat(a,'10'))*1);
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+#
+# MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0
+#
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a, FROM_UNIXTIME(CONCAT(a,'10')) AS f1, FROM_UNIXTIME(CONCAT(a,'10'))+0 AS f2 FROM t1;
+a	f1	f2
+2005-05-04	1970-01-01 03:33:25	19700101033325.000000
+2000-02-23	1970-01-01 03:33:20	19700101033320.000000
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(CONCAT(a,'10'))+0;
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))/1;
+a
+2000-02-23
+2005-05-04
+DROP TABLE t1;
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2005-05-04');
+SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
+f2
+0.000000
+SELECT CHAR_LENGTH(CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10')))) AS f2 FROM t1;
+f2
+8
+CREATE TABLE t2 AS SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
+SHOW CREATE TABLE t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `f2` varbinary(26) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM t2;
+f2
+0.000000
+DROP TABLE t1,t2;
+#
 # MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y'))
 #
 SET TIME_ZONE='+02:00';

=== modified file 'mysql-test/r/type_decimal.result'
--- mysql-test/r/type_decimal.result	2011-05-19 17:01:46 +0000
+++ mysql-test/r/type_decimal.result	2014-06-05 15:12:52 +0000
@@ -798,10 +798,10 @@ ROUND(qty,3)	dps	ROUND(qty,dps)
 DROP TABLE t1;
 SELECT 1 % .123456789123456789123456789123456789123456789123456789123456789123456789123456789 AS '%';
 %
-0.012345687012345687012345687012345687012345687012345687012345687012345687000000000
+0.012345687012345687012345687012
 SELECT MOD(1, .123456789123456789123456789123456789123456789123456789123456789123456789123456789) AS 'MOD()';
 MOD()
-0.012345687012345687012345687012345687012345687012345687012345687012345687000000000
+0.012345687012345687012345687012
 create table t1 (f1 decimal(6,6),f2 decimal(6,6) zerofill);
 insert into t1 values (-0.123456,0.123456);
 select group_concat(f1),group_concat(f2) from t1;

=== modified file 'mysql-test/r/type_newdecimal.result'
--- mysql-test/r/type_newdecimal.result	2011-12-11 09:34:44 +0000
+++ mysql-test/r/type_newdecimal.result	2014-06-04 10:30:49 +0000
@@ -703,7 +703,7 @@ select .77777777777777777777777777777777
 777777777777777777.777777777777777777700000000000
 select .7777777777777777777777777777777777777 - 0.1;
 .7777777777777777777777777777777777777 - 0.1
-0.6777777777777777777777777777777777777
+0.677777777777777777777777777778
 select .343434343434343434 + .343434343434343434;
 .343434343434343434 + .343434343434343434
 0.686868686868686868
@@ -1845,7 +1845,7 @@ CREATE TABLE t2 SELECT MIN(a + 0.0000000
 Note	1265	Data truncated for column 'c1' at row 3
 DESC t2;
 Field	Type	Null	Key	Default	Extra
-c1	decimal(32,30)	YES		NULL	
+c1	decimal(33,30)	YES		NULL	
 DROP TABLE t1,t2;
 CREATE TABLE t1 (a DECIMAL(30,30));
 INSERT INTO t1 VALUES (0.1),(0.2),(0.3);
@@ -1856,7 +1856,7 @@ Note	1265	Data truncated for column 'c1'
 Note	1265	Data truncated for column 'c1' at row 3
 DESC t2;
 Field	Type	Null	Key	Default	Extra
-c1	decimal(34,0)	YES		NULL	
+c1	decimal(33,30)	YES		NULL	
 DROP TABLE t1,t2;
 CREATE TABLE t1 (a DECIMAL(30,30));
 INSERT INTO t1 VALUES (0.1),(0.2),(0.3);

=== modified file 'mysql-test/t/func_time.test'
--- mysql-test/t/func_time.test	2014-06-04 16:32:57 +0000
+++ mysql-test/t/func_time.test	2014-06-09 09:16:33 +0000
@@ -1157,6 +1157,86 @@ SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF
 
 
 --echo #
+--echo # MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types
+--echo #
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a FROM t1 GROUP BY TIMEDIFF('2004-06-12',a) * 1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a FROM t1 GROUP BY ADDTIME(a,'10')*1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY SEC_TO_TIME(concat(a,'10'))*1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY ADDTIME(timestamp('2001-01-01 00:00:00'),CAST(a AS SIGNED)&0xF)*1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY STR_TO_DATE(a,concat('%Y-%m-%d.%f',if(rand(),'','')))*1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT
+  STR_TO_DATE('2001-01-01', '%Y-%m-%d') AS date_only,
+  STR_TO_DATE('10:10:10', '%H:%i:%s') AS time_only,
+  STR_TO_DATE('10:10:10.123', '%H:%i:%s.%f') AS time_microsecond,
+  STR_TO_DATE('2001-01-01 10:10:10', '%Y-%m-%d %H:%i:%s') AS date_time,
+  STR_TO_DATE('2001-01-01 10:10:10.123', '%Y-%m-%d %H:%i:%s.%f') AS date_time_microsecond;
+SHOW COLUMNS FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT
+  SEC_TO_TIME(1)+0.1,
+  SEC_TO_TIME(1.1)+0.1,
+  SEC_TO_TIME(1.12)+0.1,
+  SEC_TO_TIME(1.123456)+0.1,
+  SEC_TO_TIME(1.1234567)+0.1;
+SHOW COLUMNS FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))*1;
+SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')))*1;
+SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')));
+SELECT * FROM t1 GROUP BY ABS(FROM_UNIXTIME(concat(a,'10')));
+SELECT * FROM t1 GROUP BY @a:=(FROM_UNIXTIME(concat(a,'10'))*1);
+
+DROP TABLE t1;
+
+
+--echo #
+--echo # MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0
+--echo #
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT a, FROM_UNIXTIME(CONCAT(a,'10')) AS f1, FROM_UNIXTIME(CONCAT(a,'10'))+0 AS f2 FROM t1;
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(CONCAT(a,'10'))+0;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
+INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
+SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))/1;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2005-05-04');
+SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1; 
+SELECT CHAR_LENGTH(CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10')))) AS f2 FROM t1; 
+CREATE TABLE t2 AS SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
+SHOW CREATE TABLE t2;
+SELECT * FROM t2;
+DROP TABLE t1,t2;
+
+--echo #
 --echo # MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y'))
 --echo #
 SET TIME_ZONE='+02:00';

=== modified file 'sql/field.h'
--- sql/field.h	2014-03-16 20:03:01 +0000
+++ sql/field.h	2014-06-03 07:43:08 +0000
@@ -65,6 +65,29 @@ inline bool is_temporal_type(enum_field_
   return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR;
 }
 
+
+/**
+  Tests if field type is temporal and has time part,
+  i.e. represents TIME, DATETIME or TIMESTAMP types in SQL.
+
+  @param type    Field type, as returned by field->type().
+  @retval true   If field type is temporal type with time part.
+  @retval false  If field type is not temporal type with time part.
+*/
+inline bool is_temporal_type_with_time(enum_field_types type)
+{
+  switch (type)
+  {
+  case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_TIMESTAMP:
+    return true;
+  default:
+    return false;
+  }
+}
+
+
 /*
   Virtual_column_info is the class to contain additional
   characteristics that is specific for a virtual/computed

=== modified file 'sql/item.h'
--- sql/item.h	2014-03-16 20:03:01 +0000
+++ sql/item.h	2014-06-05 14:01:04 +0000
@@ -909,9 +909,46 @@ class Item {
   virtual cond_result eq_cmp_result() const { return COND_OK; }
   inline uint float_length(uint decimals_par) const
   { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
+  /* Returns total number of decimal digits */
   virtual uint decimal_precision() const;
+  /* Returns the number of integer part digits only */
   inline int decimal_int_part() const
   { return my_decimal_int_part(decimal_precision(), decimals); }
+  /*
+    Returns the number of fractional digits only.
+    NOT_FIXED_DEC is replaced to the maximum possible number
+    of fractional digits, taking into account the data type.
+  */
+  uint decimal_scale() const
+  {
+    return decimals < NOT_FIXED_DEC ? decimals :
+           is_temporal_type_with_time(field_type()) ?
+           TIME_SECOND_PART_DIGITS :
+           min(max_length, DECIMAL_MAX_SCALE);
+  }
+  /*
+    Returns how many digits a divisor adds into a division result.
+    This is important when the integer part of the divisor can be 0.
+    In this  example:
+      SELECT 1 / 0.000001; -> 1000000.0000
+    the divisor adds 5 digits into the result precision.
+
+    Currently this method only replaces NOT_FIXED_DEC to
+    TIME_SECOND_PART_DIGITS for temporal data types.
+    This method can be made virtual, to create more efficient (smaller)
+    data types for division results.
+    For example, in
+      SELECT 1/1.000001;
+    the divisor could provide no additional precision into the result,
+    so could any other items that are know to return a result
+    with non-zero integer part.
+  */
+  uint divisor_precision_increment() const
+  {
+    return decimals >= NOT_FIXED_DEC &&
+           is_temporal_type_with_time(field_type()) ?
+           TIME_SECOND_PART_DIGITS : decimals;
+  }
   /**
     TIME or DATETIME precision of the item: 0..6
   */

=== modified file 'sql/item_func.cc'
--- sql/item_func.cc	2014-06-04 16:32:57 +0000
+++ sql/item_func.cc	2014-06-09 07:58:55 +0000
@@ -569,7 +569,7 @@ my_decimal *Item_real_func::val_decimal(
 }
 
 
-void Item_func::fix_num_length_and_dec()
+void Item_udf_func::fix_num_length_and_dec()
 {
   uint fl_length= 0;
   decimals=0;
@@ -587,11 +587,6 @@ void Item_func::fix_num_length_and_dec()
 }
 
 
-void Item_func_numhybrid::fix_num_length_and_dec()
-{}
-
-
-
 /**
   Count max_length and decimals for temporal functions.
 
@@ -777,9 +772,9 @@ bool Item_func_connection_id::fix_fields
   function of two arguments.
 */
 
-void Item_num_op::find_num_type(void)
+void Item_num_op::fix_length_and_dec(void)
 {
-  DBUG_ENTER("Item_num_op::find_num_type");
+  DBUG_ENTER("Item_num_op::fix_length_and_dec");
   DBUG_PRINT("info", ("name %s", func_name()));
   DBUG_ASSERT(arg_count == 2);
   Item_result r0= args[0]->cast_to_int_type();
@@ -823,22 +818,26 @@ void Item_num_op::find_num_type(void)
   type depends only on the first argument)
 */
 
-void Item_func_num1::find_num_type()
+void Item_func_num1::fix_length_and_dec()
 {
-  DBUG_ENTER("Item_func_num1::find_num_type");
+  DBUG_ENTER("Item_func_num1::fix_length_and_dec");
   DBUG_PRINT("info", ("name %s", func_name()));
   switch (cached_result_type= args[0]->cast_to_int_type()) {
   case INT_RESULT:
+    max_length= args[0]->max_length;
     unsigned_flag= args[0]->unsigned_flag;
     break;
   case STRING_RESULT:
   case REAL_RESULT:
     cached_result_type= REAL_RESULT;
+    decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
     max_length= float_length(decimals);
     break;
   case TIME_RESULT:
     cached_result_type= DECIMAL_RESULT;
   case DECIMAL_RESULT:
+    decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
+    max_length= args[0]->max_length;
     break;
   case ROW_RESULT:
   case IMPOSSIBLE_RESULT:
@@ -853,20 +852,6 @@ void Item_func_num1::find_num_type()
 }
 
 
-void Item_func_num1::fix_num_length_and_dec()
-{
-  decimals= args[0]->decimals;
-  max_length= args[0]->max_length;
-}
-
-
-void Item_func_numhybrid::fix_length_and_dec()
-{
-  fix_num_length_and_dec();
-  find_num_type();
-}
-
-
 String *Item_func_hybrid_result_type::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
@@ -1455,11 +1440,14 @@ my_decimal *Item_func_plus::decimal_op(m
 */
 void Item_func_additive_op::result_precision()
 {
-  decimals= max(args[0]->decimals, args[1]->decimals);
-  int arg1_int= args[0]->decimal_precision() - args[0]->decimals;
-  int arg2_int= args[1]->decimal_precision() - args[1]->decimals;
+  decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
+  int arg1_int= args[0]->decimal_precision() - args[0]->decimal_scale();
+  int arg2_int= args[1]->decimal_precision() - args[1]->decimal_scale();
   int precision= max(arg1_int, arg2_int) + 1 + decimals;
 
+  DBUG_ASSERT(arg1_int >= 0);
+  DBUG_ASSERT(arg2_int >= 0);
+
   /* Integer operations keep unsigned_flag if one of arguments is unsigned */
   if (result_type() == INT_RESULT)
     unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
@@ -1568,7 +1556,7 @@ void Item_func_mul::result_precision()
     unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
   else
     unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
-  decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE);
+  decimals= min(args[0]->decimal_scale() + args[1]->decimal_scale(), DECIMAL_MAX_SCALE);
   uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
   uint precision= min(est_prec, DECIMAL_MAX_PRECISION);
   max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
@@ -1618,8 +1606,20 @@ my_decimal *Item_func_div::decimal_op(my
 
 void Item_func_div::result_precision()
 {
+  /*
+    We need to add args[1]->divisor_precision_increment(),
+    to properly handle the cases like this:
+      SELECT 5.05 / 0.014; -> 360.714286
+    i.e. when the divisor has a zero integer part
+    and non-zero digits appear only after the decimal point.
+    Precision in this example is calculated as
+      args[0]->decimal_precision()           +  // 3
+      args[1]->divisor_precision_increment() +  // 3
+      prec_increment                            // 4
+    which gives 10 decimals digits. 
+  */
   uint precision=min(args[0]->decimal_precision() + 
-                     args[1]->decimals + prec_increment,
+                     args[1]->divisor_precision_increment() + prec_increment,
                      DECIMAL_MAX_PRECISION);
 
   /* Integer operations keep unsigned_flag if one of arguments is unsigned */
@@ -1627,7 +1627,7 @@ void Item_func_div::result_precision()
     unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
   else
     unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
-  decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+  decimals= min(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
   max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
                                                            unsigned_flag);
 }
@@ -1776,7 +1776,7 @@ my_decimal *Item_func_mod::decimal_op(my
 
 void Item_func_mod::result_precision()
 {
-  decimals= max(args[0]->decimals, args[1]->decimals);
+  decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
   max_length= max(args[0]->max_length, args[1]->max_length);
 }
 
@@ -1818,18 +1818,13 @@ my_decimal *Item_func_neg::decimal_op(my
 }
 
 
-void Item_func_neg::fix_num_length_and_dec()
-{
-  decimals= args[0]->decimals;
-  /* 1 add because sign can appear */
-  max_length= args[0]->max_length + 1;
-}
-
-
 void Item_func_neg::fix_length_and_dec()
 {
   DBUG_ENTER("Item_func_neg::fix_length_and_dec");
+
   Item_func_num1::fix_length_and_dec();
+  /* 1 add because sign can appear */
+  max_length= args[0]->max_length + 1;
 
   /*
     If this is in integer context keep the context as integer if possible
@@ -2119,8 +2114,12 @@ void Item_func_integer::fix_length_and_d
   decimals=0;
 }
 
-void Item_func_int_val::fix_num_length_and_dec()
+
+void Item_func_int_val::fix_length_and_dec()
 {
+  DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
+  DBUG_PRINT("info", ("name %s", func_name()));
+
   ulonglong tmp_max_length= (ulonglong ) args[0]->max_length - 
     (args[0]->decimals ? args[0]->decimals + 1 : 0) + 2;
   max_length= tmp_max_length > (ulonglong) max_field_size ?
@@ -2128,13 +2127,7 @@ void Item_func_int_val::fix_num_length_a
   uint tmp= float_length(decimals);
   set_if_smaller(max_length,tmp);
   decimals= 0;
-}
-
 
-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 (cached_result_type= args[0]->cast_to_int_type())
   {
   case STRING_RESULT:
@@ -3569,12 +3562,6 @@ String *Item_func_udf_decimal::val_str(S
 }
 
 
-void Item_func_udf_decimal::fix_length_and_dec()
-{
-  fix_num_length_and_dec();
-}
-
-
 /* Default max_length is max argument length */
 
 void Item_func_udf_str::fix_length_and_dec()

=== modified file 'sql/item_func.h'
--- sql/item_func.h	2014-03-16 20:03:01 +0000
+++ sql/item_func.h	2014-06-09 07:57:55 +0000
@@ -145,7 +145,6 @@ class Item_func :public Item_result_fiel
   virtual void print(String *str, enum_query_type query_type);
   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(Item **item, uint nitems);
   void count_real_length();
   void count_decimal_length();
@@ -450,9 +449,6 @@ class Item_func_numhybrid: public Item_f
   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; }
 };
@@ -464,9 +460,7 @@ class Item_func_num1: public Item_func_n
 public:
   Item_func_num1(Item *a) :Item_func_numhybrid(a) {}
   Item_func_num1(Item *a, Item *b) :Item_func_numhybrid(a, b) {}
-
-  void fix_num_length_and_dec();
-  void find_num_type();
+  void fix_length_and_dec();
 };
 
 
@@ -482,7 +476,7 @@ class Item_num_op :public Item_func_numh
     print_op(str, query_type);
   }
 
-  void find_num_type();
+  void fix_length_and_dec();
 };
 
 
@@ -698,7 +692,6 @@ class Item_func_neg :public Item_func_nu
   const char *func_name() const { return "-"; }
   enum Functype functype() const   { return NEG_FUNC; }
   void fix_length_and_dec();
-  void fix_num_length_and_dec();
   uint decimal_precision() const { return args[0]->decimal_precision(); }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
   bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -857,8 +850,7 @@ class Item_func_int_val :public Item_fun
 {
 public:
   Item_func_int_val(Item *a) :Item_func_num1(a) {}
-  void fix_num_length_and_dec();
-  void find_num_type();
+  void fix_length_and_dec();
 };
 
 
@@ -1267,6 +1259,7 @@ class Item_udf_func :public Item_func
     fixed= 1;
     return res;
   }
+  void fix_num_length_and_dec();
   void update_used_tables() 
   {
     /*
@@ -1380,7 +1373,7 @@ class Item_func_udf_decimal :public Item
   my_decimal *val_decimal(my_decimal *);
   String *val_str(String *str);
   enum Item_result result_type () const { return DECIMAL_RESULT; }
-  void fix_length_and_dec();
+  void fix_length_and_dec() { fix_num_length_and_dec(); }
 };
 
 

=== modified file 'sql/item_timefunc.h'
--- sql/item_timefunc.h	2014-01-24 12:50:39 +0000
+++ sql/item_timefunc.h	2014-06-05 20:56:46 +0000
@@ -365,16 +365,15 @@ class Item_func_seconds_hybrid: public I
 public:
   Item_func_seconds_hybrid() :Item_func_numhybrid() {}
   Item_func_seconds_hybrid(Item *a) :Item_func_numhybrid(a) {}
-  void fix_num_length_and_dec()
+  void fix_length_and_dec()
   {
     if (arg_count)
       decimals= args[0]->temporal_precision(arg0_expected_type());
     set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
     max_length=17 + (decimals ? decimals + 1 : 0);
     maybe_null= true;
+    cached_result_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; }
@@ -421,11 +420,6 @@ class Item_func_time_to_sec :public Item
 public:
   Item_func_time_to_sec(Item *item) :Item_func_seconds_hybrid(item) {}
   const char *func_name() const { return "time_to_sec"; }
-  void fix_num_length_and_dec()
-  {
-    maybe_null= true;
-    Item_func_seconds_hybrid::fix_num_length_and_dec();
-  }
   bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
   bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
   bool check_valid_arguments_processor(uchar *int_arg)


Follow ups