Commit 425abb49 authored by Georgi Kodinov's avatar Georgi Kodinov
Browse files

Bug#37662 nested if() inside sum() is parsed in exponential time

      
min() and max() functions are implemented in MySQL as macros.
This means that max(a,b) is expanded to: ((a) > (b) ? (a) : (b))
Note how 'a' is quoted two times.
Now imagine 'a' is a recursive function call that's several 10s of levels deep.
And the recursive function does max() with a function arg as well to dive into
recursion.
This means that simple function call can take most of the clock time.
Identified and fixed several such calls to max()/min() : including the IF() 
sql function implementation.
parent 47cd6462
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -131,3 +131,49 @@ drop table t1;
select if(0, 18446744073709551610, 18446744073709551610);
if(0, 18446744073709551610, 18446744073709551610)
18446744073709551610
CREATE TABLE t1(a DECIMAL(10,3));
SELECT t1.a,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,0)))))))))))))))))))))))))))))) + 1
FROM t1;
a	IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((ROUND(t1.a,2)=1), 2,
IF((R
DROP TABLE t1;
End of 5.0 tests
+43 −0
Original line number Diff line number Diff line
@@ -108,3 +108,46 @@ drop table t1;
select if(0, 18446744073709551610, 18446744073709551610);


#
# Bug #37662: nested if() inside sum() is parsed in exponential time
#

CREATE TABLE t1(a DECIMAL(10,3));

# check : should be fast. more than few secs means failure.
SELECT t1.a,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,
 IF((ROUND(t1.a,2)=1), 2,0)))))))))))))))))))))))))))))) + 1
FROM t1;

DROP TABLE t1;

--echo End of 5.0 tests
+8 −4
Original line number Diff line number Diff line
@@ -429,8 +429,11 @@ uint Item::decimal_precision() const
  Item_result restype= result_type();

  if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT))
    return min(my_decimal_length_to_precision(max_length, decimals, unsigned_flag),
               DECIMAL_MAX_PRECISION);
  {
    uint prec= 
      my_decimal_length_to_precision(max_length, decimals, unsigned_flag);
    return min(prec, DECIMAL_MAX_PRECISION);
  }
  return min(max_length, DECIMAL_MAX_PRECISION);
}

@@ -6838,8 +6841,9 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
  if (Field::result_merge_type(fld_type) == DECIMAL_RESULT)
  {
    decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE);
    int precision= min(max(prev_decimal_int_part, item->decimal_int_part())
                       + decimals, DECIMAL_MAX_PRECISION);
    int item_int_part= item->decimal_int_part();
    int item_prec = max(prev_decimal_int_part, item_int_part) + decimals;
    int precision= min(item_prec, DECIMAL_MAX_PRECISION);
    unsigned_flag&= item->unsigned_flag;
    max_length= my_decimal_precision_to_length(precision, decimals,
                                               unsigned_flag);
+8 −4
Original line number Diff line number Diff line
@@ -2098,8 +2098,11 @@ Item_func_ifnull::fix_length_and_dec()

uint Item_func_ifnull::decimal_precision() const
{
  int max_int_part=max(args[0]->decimal_int_part(),args[1]->decimal_int_part());
  return min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
  int arg0_int_part= args[0]->decimal_int_part();
  int arg1_int_part= args[1]->decimal_int_part();
  int max_int_part= max(arg0_int_part, arg1_int_part);
  int precision= max_int_part + decimals;
  return min(precision, DECIMAL_MAX_PRECISION);
}


@@ -2281,8 +2284,9 @@ Item_func_if::fix_length_and_dec()

uint Item_func_if::decimal_precision() const
{
  int precision=(max(args[1]->decimal_int_part(),args[2]->decimal_int_part())+
                 decimals);
  int arg1_prec= args[1]->decimal_int_part();
  int arg2_prec= args[2]->decimal_int_part();
  int precision=max(arg1_prec,arg2_prec) + decimals;
  return min(precision, DECIMAL_MAX_PRECISION);
}

+8 −7
Original line number Diff line number Diff line
@@ -1156,9 +1156,10 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
void Item_func_additive_op::result_precision()
{
  decimals= max(args[0]->decimals, args[1]->decimals);
  int max_int_part= max(args[0]->decimal_precision() - args[0]->decimals,
                        args[1]->decimal_precision() - args[1]->decimals);
  int precision= min(max_int_part + 1 + decimals, DECIMAL_MAX_PRECISION);
  int arg1_int= args[0]->decimal_precision() - args[0]->decimals;
  int arg2_int= args[1]->decimal_precision() - args[1]->decimals;
  int est_prec= max(arg1_int, arg2_int) + 1 + decimals;
  int precision= min(est_prec, DECIMAL_MAX_PRECISION);

  /* Integer operations keep unsigned_flag if one of arguments is unsigned */
  if (result_type() == INT_RESULT)
@@ -1267,8 +1268,8 @@ void Item_func_mul::result_precision()
  else
    unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
  decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE);
  int precision= min(args[0]->decimal_precision() + args[1]->decimal_precision(),
                     DECIMAL_MAX_PRECISION);
  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(precision, decimals,unsigned_flag);
}

@@ -1315,8 +1316,8 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)

void Item_func_div::result_precision()
{
  uint precision=min(args[0]->decimal_precision() + prec_increment,
                     DECIMAL_MAX_PRECISION);
  uint arg_prec= args[0]->decimal_precision() + prec_increment;
  uint precision=min(arg_prec, DECIMAL_MAX_PRECISION);
  /* 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;