Commit ffe23f0e authored by Gleb Shchepa's avatar Gleb Shchepa

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.


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.
parent 0837c111
...@@ -176,4 +176,13 @@ IF((ROUND(t1.a,2)=1), 2, ...@@ -176,4 +176,13 @@ IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2, IF((ROUND(t1.a,2)=1), 2,
IF((R IF((R
DROP TABLE t1; 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 End of 5.0 tests
...@@ -150,4 +150,17 @@ FROM t1; ...@@ -150,4 +150,17 @@ FROM t1;
DROP TABLE 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 --echo End of 5.0 tests
...@@ -2069,21 +2069,23 @@ Item_func_ifnull::fix_length_and_dec() ...@@ -2069,21 +2069,23 @@ Item_func_ifnull::fix_length_and_dec()
{ {
agg_result_type(&hybrid_type, args, 2); agg_result_type(&hybrid_type, args, 2);
maybe_null=args[1]->maybe_null; 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 (hybrid_type == DECIMAL_RESULT || hybrid_type == INT_RESULT)
{ {
int len0= args[0]->max_length - args[0]->decimals max_length= 0;
- (args[0]->unsigned_flag ? 0 : 1); decimals= 0;
unsigned_flag= TRUE;
int len1= args[1]->max_length - args[1]->decimals agg_num_lengths(args[0]);
- (args[1]->unsigned_flag ? 0 : 1); agg_num_lengths(args[1]);
max_length= my_decimal_precision_to_length(max_length + decimals, decimals,
max_length= max(len0, len1) + decimals + (unsigned_flag ? 0 : 1); unsigned_flag);
} }
else else
{
max_length= max(args[0]->max_length, args[1]->max_length); 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) { switch (hybrid_type) {
case STRING_RESULT: case STRING_RESULT:
...@@ -2238,8 +2240,6 @@ void ...@@ -2238,8 +2240,6 @@ void
Item_func_if::fix_length_and_dec() Item_func_if::fix_length_and_dec()
{ {
maybe_null=args[1]->maybe_null || args[2]->maybe_null; 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 arg1_type=args[1]->result_type();
enum Item_result arg2_type=args[2]->result_type(); enum Item_result arg2_type=args[2]->result_type();
...@@ -2276,16 +2276,20 @@ Item_func_if::fix_length_and_dec() ...@@ -2276,16 +2276,20 @@ Item_func_if::fix_length_and_dec()
if ((cached_result_type == DECIMAL_RESULT ) if ((cached_result_type == DECIMAL_RESULT )
|| (cached_result_type == INT_RESULT)) || (cached_result_type == INT_RESULT))
{ {
int len1= args[1]->max_length - args[1]->decimals max_length= 0;
- (args[1]->unsigned_flag ? 0 : 1); decimals= 0;
unsigned_flag= TRUE;
int len2= args[2]->max_length - args[2]->decimals agg_num_lengths(args[1]);
- (args[2]->unsigned_flag ? 0 : 1); agg_num_lengths(args[2]);
max_length= my_decimal_precision_to_length(max_length + decimals, decimals,
max_length=max(len1, len2) + decimals + (unsigned_flag ? 0 : 1); unsigned_flag);
} }
else else
{
max_length= max(args[1]->max_length, args[2]->max_length); 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;
}
} }
...@@ -2633,16 +2637,6 @@ void Item_func_case::agg_str_lengths(Item* arg) ...@@ -2633,16 +2637,6 @@ void Item_func_case::agg_str_lengths(Item* arg)
} }
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() void Item_func_case::fix_length_and_dec()
{ {
Item **agg; Item **agg;
......
...@@ -759,7 +759,6 @@ public: ...@@ -759,7 +759,6 @@ public:
Item *find_item(String *str); Item *find_item(String *str);
CHARSET_INFO *compare_collation() { return cmp_collation.collation; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
void agg_str_lengths(Item *arg); void agg_str_lengths(Item *arg);
void agg_num_lengths(Item *arg);
}; };
......
...@@ -5668,3 +5668,13 @@ void Item_func_sp::update_used_tables() ...@@ -5668,3 +5668,13 @@ void Item_func_sp::update_used_tables()
const_item_cache= FALSE; 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;
}
...@@ -193,6 +193,8 @@ public: ...@@ -193,6 +193,8 @@ public:
void * arg, traverse_order order); void * arg, traverse_order order);
bool is_expensive_processor(byte *arg); bool is_expensive_processor(byte *arg);
virtual bool is_expensive() { return 0; } virtual bool is_expensive() { return 0; }
protected:
void agg_num_lengths(Item *arg);
}; };
......
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