#At file:///work/bzr/5.0-40761/ based on revid:ramil@stripped
2720 Gleb Shchepa 2008-11-24
Bug #40761: Assert on sum function on
IF(..., CAST(longtext AS UNSIGNED), signed_val)
(was: LEFT JOIN on inline view crashes server)
Select from a LONGTEXT column wrapped with an expression
like "IF(..., CAST(longtext_column AS UNSIGNED), smth_signed)"
failed an assertion or crashed the server. IFNULL function was
affected too.
LONGTEXT column item has a maximum length of 32^2-1 bytes,
at the same time this is a maximum possible length of any
MySQL item. CAST(longtext_column AS UNSIGNED) returns some
unsigned numeric result of length 32^2-1, so the result of
IF/IFNULL function of this number and some other signed number
will have text length of (32^2-1)+1=32^2 (one byte for the
minus sign) - there is integer overflow, and the length is
equal to zero. That caused assert/crash.
The bug has been fixed by the same solution as in the CASE
function implementation.
modified:
mysql-test/r/func_if.result
mysql-test/t/func_if.test
sql/item_cmpfunc.cc
sql/item_cmpfunc.h
sql/item_func.cc
sql/item_func.h
per-file messages:
mysql-test/r/func_if.result
Added test case for bug #40761.
mysql-test/t/func_if.test
Added test case for bug #40761.
sql/item_cmpfunc.cc
Bug #40761: Assert on sum function on
IF(..., CAST(longtext AS UNSIGNED), signed_val)
1. Item_func_case::agg_str_lengths method has been moved
to the Item_func superclass.
2. Item_func_ifnull/Item_func_if::fix_length_and_dec methods
have been updated to calculate max_length, decimals and
unsigned flag like Item_func_case.
sql/item_cmpfunc.h
Bug #40761: Assert on sum function on
IF(..., CAST(longtext AS UNSIGNED), signed_val)
Item_func_case::agg_str_lengths method has been moved to
the Item_func superclass.
sql/item_func.cc
Bug #40761: Assert on sum function on
IF(..., CAST(longtext AS UNSIGNED), signed_val)
Item_func_case::agg_str_lengths method has been moved to
the Item_func superclass.
sql/item_func.h
Bug #40761: Assert on sum function on
IF(..., CAST(longtext AS UNSIGNED), signed_val)
Item_func_case::agg_str_lengths method has been moved to
the Item_func superclass.
=== modified file 'mysql-test/r/func_if.result'
--- a/mysql-test/r/func_if.result 2008-07-30 11:07:37 +0000
+++ b/mysql-test/r/func_if.result 2008-11-24 14:49:35 +0000
@@ -176,4 +176,13 @@ IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((R
DROP TABLE t1;
+CREATE TABLE t1 (c LONGTEXT);
+INSERT INTO t1 VALUES(1), (2), (3), (4), ('12345678901234567890');
+SELECT * FROM (SELECT MAX(IF(1, CAST(c AS UNSIGNED), 0)) FROM t1) AS te;
+MAX(IF(1, CAST(c AS UNSIGNED), 0))
+12345678901234567890
+SELECT * FROM (SELECT MAX(IFNULL(CAST(c AS UNSIGNED), 0)) FROM t1) AS te;
+MAX(IFNULL(CAST(c AS UNSIGNED), 0))
+12345678901234567890
+DROP TABLE t1;
End of 5.0 tests
=== modified file 'mysql-test/t/func_if.test'
--- a/mysql-test/t/func_if.test 2008-07-30 11:07:37 +0000
+++ b/mysql-test/t/func_if.test 2008-11-24 14:49:35 +0000
@@ -150,4 +150,17 @@ FROM t1;
DROP TABLE t1;
+#
+# Bug #40761: Assert on sum func on IF(..., CAST(longtext AS UNSIGNED), signed)
+# (was: LEFT JOIN on inline view crashes server)
+#
+
+CREATE TABLE t1 (c LONGTEXT);
+INSERT INTO t1 VALUES(1), (2), (3), (4), ('12345678901234567890');
+
+SELECT * FROM (SELECT MAX(IF(1, CAST(c AS UNSIGNED), 0)) FROM t1) AS te;
+SELECT * FROM (SELECT MAX(IFNULL(CAST(c AS UNSIGNED), 0)) FROM t1) AS te;
+
+DROP TABLE t1;
+
--echo End of 5.0 tests
=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc 2008-09-09 15:05:27 +0000
+++ b/sql/item_cmpfunc.cc 2008-11-24 14:49:35 +0000
@@ -2068,21 +2068,23 @@ Item_func_ifnull::fix_length_and_dec()
{
agg_result_type(&hybrid_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)
{
- int len0= args[0]->max_length - args[0]->decimals
- - (args[0]->unsigned_flag ? 0 : 1);
-
- int len1= args[1]->max_length - args[1]->decimals
- - (args[1]->unsigned_flag ? 0 : 1);
-
- max_length= max(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
+ max_length= 0;
+ decimals= 0;
+ unsigned_flag= TRUE;
+ agg_num_lengths(args[0]);
+ agg_num_lengths(args[1]);
+ max_length= my_decimal_precision_to_length(max_length + decimals, decimals,
+ unsigned_flag);
}
else
+ {
max_length= max(args[0]->max_length, args[1]->max_length);
+ decimals= max(args[0]->decimals, args[1]->decimals);
+ unsigned_flag=args[0]->unsigned_flag && args[1]->unsigned_flag;
+ }
switch (hybrid_type) {
case STRING_RESULT:
@@ -2237,8 +2239,6 @@ void
Item_func_if::fix_length_and_dec()
{
maybe_null=args[1]->maybe_null || args[2]->maybe_null;
- decimals= max(args[1]->decimals, args[2]->decimals);
- unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
enum Item_result arg1_type=args[1]->result_type();
enum Item_result arg2_type=args[2]->result_type();
@@ -2275,16 +2275,20 @@ Item_func_if::fix_length_and_dec()
if ((cached_result_type == DECIMAL_RESULT )
|| (cached_result_type == INT_RESULT))
{
- int len1= args[1]->max_length - args[1]->decimals
- - (args[1]->unsigned_flag ? 0 : 1);
-
- int len2= args[2]->max_length - args[2]->decimals
- - (args[2]->unsigned_flag ? 0 : 1);
-
- max_length=max(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
+ max_length= 0;
+ decimals= 0;
+ unsigned_flag= TRUE;
+ agg_num_lengths(args[1]);
+ agg_num_lengths(args[2]);
+ max_length= my_decimal_precision_to_length(max_length + decimals, decimals,
+ unsigned_flag);
}
else
+ {
max_length= max(args[1]->max_length, args[2]->max_length);
+ decimals= max(args[1]->decimals, args[2]->decimals);
+ unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
+ }
}
@@ -2632,16 +2636,6 @@ void Item_func_case::agg_str_lengths(Ite
}
-void Item_func_case::agg_num_lengths(Item *arg)
-{
- uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
- arg->unsigned_flag) - arg->decimals;
- set_if_bigger(max_length, len);
- set_if_bigger(decimals, arg->decimals);
- unsigned_flag= unsigned_flag && arg->unsigned_flag;
-}
-
-
void Item_func_case::fix_length_and_dec()
{
Item **agg;
=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h 2008-09-05 08:30:01 +0000
+++ b/sql/item_cmpfunc.h 2008-11-24 14:49:35 +0000
@@ -759,7 +759,6 @@ public:
Item *find_item(String *str);
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
void agg_str_lengths(Item *arg);
- void agg_num_lengths(Item *arg);
};
=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc 2008-11-18 11:10:52 +0000
+++ b/sql/item_func.cc 2008-11-24 14:49:35 +0000
@@ -5668,3 +5668,13 @@ void Item_func_sp::update_used_tables()
const_item_cache= FALSE;
}
}
+
+
+void Item_func::agg_num_lengths(Item *arg)
+{
+ uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
+ arg->unsigned_flag) - arg->decimals;
+ set_if_bigger(max_length, len);
+ set_if_bigger(decimals, arg->decimals);
+ unsigned_flag= unsigned_flag && arg->unsigned_flag;
+}
=== modified file 'sql/item_func.h'
--- a/sql/item_func.h 2008-02-28 13:23:22 +0000
+++ b/sql/item_func.h 2008-11-24 14:49:35 +0000
@@ -193,6 +193,8 @@ public:
void * arg, traverse_order order);
bool is_expensive_processor(byte *arg);
virtual bool is_expensive() { return 0; }
+protected:
+ void agg_num_lengths(Item *arg);
};
| Thread |
|---|
| • bzr commit into mysql-5.0-bugteam branch (gshchepa:2720) Bug#40761 | Gleb Shchepa | 24 Nov |