Commit aa323361 authored by unknown's avatar unknown

Bug #24791: Union with AVG-groups generates wrong results

The problem in this bug is when we create temporary tables. When
temporary tables are created for unions, there is some 
inferrence being carried out regarding the type of the column.
Whenever this column type is inferred to be REAL (i.e. FLOAT or
DOUBLE), MySQL will always try to maintain exact precision, and
if that is not possible (there are hardware limits, since FLOAT
and DOUBLE are stored as approximate values) will switch to
using approximate values. The problem here is that at this point
the information about number of significant digits is not 
available. Furthermore, the number of significant digits should
be increased for the AVG function, however, this was not properly 
handled. There are 4 parts to the problem:

#1: DOUBLE and FLOAT fields don't display their proper display 
lengths in max_display_length(). This is hard-coded as 53 for 
DOUBLE and 24 for FLOAT. Now changed to instead return the 
field_length.

#2: Type holders for temporary tables do not preserve the 
max_length of the Item's from which they are created, and is 
instead reverted to the 53 and 24 from above. This causes 
*all* fields to get non-fixed significant digits.

#3: AVG function does not update max_length (display length)
when updating number of decimals.

#4: The function that switches to non-fixed number of 
significant digits should use DBL_DIG + 2 or FLT_DIG + 2 as 
cut-off values (Since fixed precision does not use the 'e' 
notation)

Of these points, #1 is the controversial one, but this 
change is preferred and has been cleared with Monty. The 
function causes quite a few unit tests to blow up and they had
to b changed, but each one is annotated and motivated. We 
frequently see the magical 53 and 24 give way to more relevant
numbers.


mysql-test/r/create.result:
  bug#24791
  
  changed test result
  
  With the changes made for FLOAT and DOUBLE, the original display
  lengths are now preserved.
mysql-test/r/temp_table.result:
  bug#24791
  
  changed test resullt
  
  Test case added
mysql-test/r/type_float.result:
  bug#24791
  
  changed test result
  
  delta 1: field was originally declared as DOUBLE with no display
  length, so the hardware maximum is chosen rather than 53.
  
  delta 2: fields exceed the maximum precision and thus switch to
  non-fixed significant digits
  
  delta 3: Same as above, number of decmals and significant digits
  was not specified when t3 was created.
mysql-test/t/temp_table.test:
  bug#24791
  
  Test case
sql/field.h:
  bug#24791
  
  The method max_display_length is reimplemented as
  
  uint32 max_display_length() { return field_length; }
  
  in Field_double and Field_float. Since all subclasses of 
  Field_real now have the same implementation of this method, the
  implementation has been moved up the hierarchy to Field_real.
sql/item.cc:
  bug#24791
  
  We switch to a non-fixed number of significant digits
  (by setting decimals=NOT_FIXED_DECIMAL) if the calculated 
  display length is greater than the display length of a value 
  with the maximum precision. These values differ for double and 
  float, obviously.
sql/item_sum.cc:
  bug#24791
  
  We must increase the display length accordinly whenever we 
  change number of decimal places.
parent ff810fb9
...@@ -447,8 +447,8 @@ t2 CREATE TABLE `t2` ( ...@@ -447,8 +447,8 @@ t2 CREATE TABLE `t2` (
`ifnull(c,c)` mediumint(8) default NULL, `ifnull(c,c)` mediumint(8) default NULL,
`ifnull(d,d)` int(11) default NULL, `ifnull(d,d)` int(11) default NULL,
`ifnull(e,e)` bigint(20) default NULL, `ifnull(e,e)` bigint(20) default NULL,
`ifnull(f,f)` float(24,2) default NULL, `ifnull(f,f)` float(3,2) default NULL,
`ifnull(g,g)` double(53,3) default NULL, `ifnull(g,g)` double(4,3) default NULL,
`ifnull(h,h)` decimal(5,4) default NULL, `ifnull(h,h)` decimal(5,4) default NULL,
`ifnull(i,i)` year(4) default NULL, `ifnull(i,i)` year(4) default NULL,
`ifnull(j,j)` date default NULL, `ifnull(j,j)` date default NULL,
......
...@@ -152,3 +152,24 @@ SELECT * FROM t1; ...@@ -152,3 +152,24 @@ SELECT * FROM t1;
i i
DROP TABLE t1; DROP TABLE t1;
End of 4.1 tests. End of 4.1 tests.
CREATE TABLE t1 ( c FLOAT( 20, 14 ) );
INSERT INTO t1 VALUES( 12139 );
CREATE TABLE t2 ( c FLOAT(30,18) );
INSERT INTO t2 VALUES( 123456 );
SELECT AVG( c ) FROM t1 UNION SELECT 1;
AVG( c )
12139
1
SELECT 1 UNION SELECT AVG( c ) FROM t1;
1
1
12139
SELECT 1 UNION SELECT * FROM t2 UNION SELECT 1;
1
1
123456
SELECT c/1 FROM t1 UNION SELECT 1;
c/1
12139
1
DROP TABLE t1, t2;
...@@ -92,7 +92,7 @@ show create table t2; ...@@ -92,7 +92,7 @@ show create table t2;
Table Create Table Table Create Table
t2 CREATE TABLE `t2` ( t2 CREATE TABLE `t2` (
`col1` double default NULL, `col1` double default NULL,
`col2` double(53,5) default NULL, `col2` double(22,5) default NULL,
`col3` double default NULL, `col3` double default NULL,
`col4` double default NULL `col4` double default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
...@@ -232,12 +232,12 @@ insert into t2 values ("1.23456780"); ...@@ -232,12 +232,12 @@ insert into t2 values ("1.23456780");
create table t3 select * from t2 union select * from t1; create table t3 select * from t2 union select * from t1;
select * from t3; select * from t3;
d d
1.234567800 1.2345678
100000000.000000000 100000000
show create table t3; show create table t3;
Table Create Table Table Create Table
t3 CREATE TABLE `t3` ( t3 CREATE TABLE `t3` (
`d` double(22,9) default NULL `d` double default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1, t2, t3; drop table t1, t2, t3;
create table t1 select 105213674794682365.00 + 0.0 x; create table t1 select 105213674794682365.00 + 0.0 x;
......
...@@ -163,3 +163,19 @@ DROP TABLE t1; ...@@ -163,3 +163,19 @@ DROP TABLE t1;
--echo End of 4.1 tests. --echo End of 4.1 tests.
#
# Bug #24791: Union with AVG-groups generates wrong results
#
CREATE TABLE t1 ( c FLOAT( 20, 14 ) );
INSERT INTO t1 VALUES( 12139 );
CREATE TABLE t2 ( c FLOAT(30,18) );
INSERT INTO t2 VALUES( 123456 );
SELECT AVG( c ) FROM t1 UNION SELECT 1;
SELECT 1 UNION SELECT AVG( c ) FROM t1;
SELECT 1 UNION SELECT * FROM t2 UNION SELECT 1;
SELECT c/1 FROM t1 UNION SELECT 1;
DROP TABLE t1, t2;
...@@ -432,6 +432,7 @@ class Field_real :public Field_num { ...@@ -432,6 +432,7 @@ class Field_real :public Field_num {
int store_decimal(const my_decimal *); int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *); my_decimal *val_decimal(my_decimal *);
uint32 max_display_length() { return field_length; }
}; };
...@@ -461,7 +462,6 @@ class Field_decimal :public Field_real { ...@@ -461,7 +462,6 @@ class Field_decimal :public Field_real {
void overflow(bool negative); void overflow(bool negative);
bool zero_pack() const { return 0; } bool zero_pack() const { return 0; }
void sql_type(String &str) const; void sql_type(String &str) const;
uint32 max_display_length() { return field_length; }
}; };
...@@ -719,7 +719,6 @@ class Field_float :public Field_real { ...@@ -719,7 +719,6 @@ class Field_float :public Field_real {
void sort_string(char *buff,uint length); void sort_string(char *buff,uint length);
uint32 pack_length() const { return sizeof(float); } uint32 pack_length() const { return sizeof(float); }
void sql_type(String &str) const; void sql_type(String &str) const;
uint32 max_display_length() { return 24; }
}; };
...@@ -762,7 +761,6 @@ class Field_double :public Field_real { ...@@ -762,7 +761,6 @@ class Field_double :public Field_real {
void sort_string(char *buff,uint length); void sort_string(char *buff,uint length);
uint32 pack_length() const { return sizeof(double); } uint32 pack_length() const { return sizeof(double); }
void sql_type(String &str) const; void sql_type(String &str) const;
uint32 max_display_length() { return 53; }
uint size_of() const { return sizeof(*this); } uint size_of() const { return sizeof(*this); }
}; };
......
...@@ -6348,8 +6348,6 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) ...@@ -6348,8 +6348,6 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
:Item(thd, item), enum_set_typelib(0), fld_type(get_real_type(item)) :Item(thd, item), enum_set_typelib(0), fld_type(get_real_type(item))
{ {
DBUG_ASSERT(item->fixed); DBUG_ASSERT(item->fixed);
max_length= display_length(item);
maybe_null= item->maybe_null; maybe_null= item->maybe_null;
collation.set(item->collation); collation.set(item->collation);
get_full_info(item); get_full_info(item);
...@@ -6521,11 +6519,17 @@ bool Item_type_holder::join_types(THD *thd, Item *item) ...@@ -6521,11 +6519,17 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
{ {
int delta1= max_length_orig - decimals_orig; int delta1= max_length_orig - decimals_orig;
int delta2= item->max_length - item->decimals; int delta2= item->max_length - item->decimals;
if (fld_type == MYSQL_TYPE_DECIMAL) max_length= max(delta1, delta2) + decimals;
max_length= max(delta1, delta2) + decimals; if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2)
else {
max_length= min(max(delta1, delta2) + decimals, max_length= FLT_DIG + 6;
(fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7); decimals= NOT_FIXED_DEC;
}
if (fld_type == MYSQL_TYPE_DOUBLE && max_length > DBL_DIG + 2)
{
max_length= DBL_DIG + 7;
decimals= NOT_FIXED_DEC;
}
} }
else else
max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7; max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7;
......
...@@ -1096,8 +1096,10 @@ void Item_sum_avg::fix_length_and_dec() ...@@ -1096,8 +1096,10 @@ void Item_sum_avg::fix_length_and_dec()
f_scale= args[0]->decimals; f_scale= args[0]->decimals;
dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
} }
else else {
decimals= min(args[0]->decimals + prec_increment, NOT_FIXED_DEC); decimals= min(args[0]->decimals + prec_increment, NOT_FIXED_DEC);
max_length= args[0]->max_length + prec_increment;
}
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment