List:Commits« Previous MessageNext Message »
From:eugene Date:August 9 2006 10:01pm
Subject:bk commit into 5.1 tree (evgen:1.2247) BUG#18360
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of evgen. When evgen does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2006-08-10 00:01:54+04:00, evgen@stripped +4 -0
  Fixed bug #18360: Type aggregation for IN and CASE may lead to a wrong
  result
  
  The IN function aggregates result types of all expressions. It uses that 
  type in comparison of left expression and expressions in right part. 
  This approach works in most cases. But let's consider the case when the
  right part contains both strings and integers. In that case this approach may
  cause wrong results because all strings which do not start with a digit are
  evaluated as 0.
  CASE uses the same approach when a CASE expression is given thus it's also
  affected.
  
  The idea behind this fix is to make IN function to compare expressions with
  different result types differently. For example a string in the left
  part will be compared as string with strings specified in right part and
  will be converted to real for comparison to int or real items in the right
  part.
  
  The agg_cmp_type() function now aggregates the type of the left expression and 
  each expression in the right part individually. It collects different
  result types found. It also can be forced to aggregate collected result types
  to preserve compatibility.
  
  The cmp_item class now has the flag value_added. It indicates that a value is
  already stored.
  
  The Item_func_in class now can refer up to 5 cmp_item objects: 1 for each
  result type for comparison purposes. cmp_item objects are allocated according
  to found result types. The comparison of the left expression with any
  right part expression is now based only on result types of these expressions.
  
  The Item_func_case class is modified in the similar way when a CASE
  expression is specified. Now it can allocate up to 5 cmp_item objects
  to compare CASE expression with WHEN expressions of different types.
  The comparison of the CASE expression with any WHEN expression now based only 
  on result types of these expressions.
  

  mysql-test/r/func_in.result@stripped, 2006-08-09 23:56:37+04:00, evgen@stripped +12 -0
    Added test case for bug#18360: Type aggregation for IN and CASE may lead to a wrong result

  mysql-test/t/func_in.test@stripped, 2006-08-09 23:56:05+04:00, evgen@stripped +9 -0
    Added test case for bug#18360: Type aggregation for IN and CASE may lead to a wrong
    result

  sql/item_cmpfunc.cc@stripped, 2006-08-09 23:56:50+04:00, evgen@stripped +211 -116
    Fixed bug #18360: Type aggregation for IN and CASE may lead to a wrong result
    The agg_cmp_type() function now aggregates the type of the left expression and 
    each expression in the right part individually. It collects different
    result types found. It also can be forced to aggregate collected result types
    to preserve compatibility.
    The cmp_item class now has the flag value_added. It indicates that a value is
    already stored.
    The Item_func_in class now can refer up to 5 cmp_item objects: 1 for each
    result type for comparison purposes. cmp_item objects are allocated according
    to found result types. The comparison of the left expression with any
    right part expression is now based only on result types of these expressions.
    The Item_func_case class is modified in the similar way when a CASE
    expression is specified. Now it can allocate up to 5 cmp_item objects
    to compare CASE expression with WHEN expressions of different types.
    The comparison of the CASE expression with any WHEN expression now based only 
    on result types of these expressions.

  sql/item_cmpfunc.h@stripped, 2006-08-09 23:58:16+04:00, evgen@stripped +93 -47
    Fixed bug#18360: Type aggregation for IN and CASE may lead to a wrong result
    The cmp_item class now has the flag value_added. It indicates that a value is
    already stored.
    The Item_func_in class now can refer up to 5 cmp_item objects.
    The Item_func_case class is modified in the similar way.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	evgen
# Host:	moonbone.local
# Root:	/work/18360-bug-5.1-opt-mysql

--- 1.210/sql/item_cmpfunc.cc	2006-08-10 00:01:59 +04:00
+++ 1.211/sql/item_cmpfunc.cc	2006-08-10 00:01:59 +04:00
@@ -68,55 +68,57 @@
 
   SYNOPSIS:
     agg_cmp_type()
-    thd          thread handle
-    type   [out] the aggregated type
-    items        array of items to aggregate the type from
-    nitems       number of items in the array
+    thd               thread handle
+    found_types [out] collected types
+    type_cnt [in/out] in: whether to aggregate found result types
+                      out: number of types found
+    items             array of items to aggregate the type from
+    nitems            number of items in the array
 
   DESCRIPTION
-    This function aggregates result types from the array of items. Found type
-    supposed to be used later for comparison of values of these items.
+    This function collects different result types for comparison of the first
+    item in the list with each of the remaining items in the 'items' array.
     Aggregation itself is performed by the item_cmp_type() function.
 
   NOTES
     Aggregation rules:
     If there are DATE/TIME fields/functions in the list and no string
     fields/functions in the list then:
-      The INT_RESULT type will be used for aggregation instead of original
-      result type of any DATE/TIME field/function in the list
+      The INT_RESULT type will be used instead of the original
+      result type of each DATE/TIME field/function in the list
       All constant items in the list will be converted to a DATE/TIME using
-      found field or result field of found function.
+      found DATE/TIME field or result field of found DATE/TIME function.
 
     Implementation notes:
       The code is equivalent to:
-      1. Check the list for presence of a STRING field/function.
-         Collect the is_const flag.
+      1. Check the list for presence of STRING field/function.
       2. Get a Field* object to use for type coercion
       3. Perform type conversion.
       1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME
-      field/function and checks presence of a STRING field/function.
+      field/function and checks the presence of a STRING field/function.
       The second loop works only if a DATE/TIME field/function is found.
-      It checks presence of a STRING field/function in the rest of the list.
+      It checks the presence of a STRING field/function in the rest of the list.
 
   TODO
-    1) The current implementation can produce false comparison results for
+    1) The current implementation can produce wrong comparison results for
     expressions like:
       date_time_field BETWEEN string_field_with_dates AND string_constant
-    if the string_constant will omit some of leading zeroes.
-    In order to fully implement correct comparison of DATE/TIME the new
-    DATETIME_RESULT result type should be introduced and agg_cmp_type()
-    should return the DATE/TIME field used for the conversion. Later
+      if some of the leading zeroes are omitted in the string_constant.
+    In order to fully implement correct comparison of DATE/TIME a new
+    DATETIME_RESULT result type should be introduced. agg_cmp_type()
+    should also return the DATE/TIME field used for the conversion. Later
     this field can be used by comparison functions like Item_func_between to
     convert string values to ints on the fly and thus return correct results.
     This modification will affect functions BETWEEN, IN and CASE.
 
-    2) If in the list a DATE field/function and a DATETIME field/function
-    are present in the list then the first found field/function will be
-    used for conversion. This may lead to wrong results and probably should
-    be fixed.
+    2) If in the list a DATE/TIME field/function and a DATETIME/TIMESTAMP
+    field/function are present in the list then the first found
+    field/function will be used for conversion. This may lead to wrong
+    results and probably should be fixed.
 */
 
-static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems)
+static void agg_cmp_type(THD *thd, uint *found_types, uint *type_cnt,
+                         Item **items, uint nitems)
 {
   uint i;
   Item::Type res= (Item::Type)0;
@@ -124,6 +126,7 @@
   char buff[20];
   uchar null_byte;
   Field *field= NULL;
+  Item_result left_result;
 
   /* Search for date/time fields/functions */
   for (i= 0; i < nitems; i++)
@@ -173,9 +176,9 @@
   */
   if (field)
     /* Suppose we are comparing dates */
-    type[0]= INT_RESULT;
+    left_result= INT_RESULT;
   else
-    type[0]= items[0]->result_type();
+    left_result= items[0]->result_type();
 
   for (i= 0; i < nitems ; i++)
   {
@@ -189,11 +192,33 @@
          (items[i]->const_item() && convert_constant_item(thd, field,
                                                           &items[i]))))
       result= INT_RESULT;
-    type[0]= item_cmp_type(type[0], result);
+    if (i)
+      *found_types|= 1<< (uint)item_cmp_type(left_result, result);
   }
 
   if (res == Item::FUNC_ITEM && field)
     delete field;
+
+  if (*type_cnt)
+  {
+    Item_result res= (Item_result)-1;
+    for (i= 0; i <= DECIMAL_RESULT; i++)
+      if (*found_types & 1 << i)
+      {
+        if(res == (Item_result)-1)
+          res= (Item_result)i;
+        else
+          res= item_cmp_type(res, (Item_result)i);
+      }
+    *found_types= 1 << (uint)res;
+    *type_cnt= 1;
+  }
+  else
+  {
+    for (i= 0; i <= DECIMAL_RESULT; i++)
+      if (*found_types & 1 << i)
+        (*type_cnt)++;
+  }
 }
 
 
@@ -1214,16 +1239,19 @@
 
 void Item_func_between::fix_length_and_dec()
 {
-   max_length= 1;
-   THD *thd= current_thd;
-
+  max_length= 1;
+  THD *thd= current_thd;
+  uint dummy= 1;
+  uint found_types= 0;
   /*
     As some compare functions are generated after sql_yacc,
     we have to check for out of memory conditions here
   */
   if (!args[0] || !args[1] || !args[2])
     return;
-  agg_cmp_type(thd, &cmp_type, args, 3);
+  agg_cmp_type(thd, &found_types, &dummy, args, 3);
+  for (dummy= 0; found_types - 1; found_types>>= 1, dummy++);
+  cmp_type= (Item_result)dummy;
 
   if (cmp_type == STRING_RESULT)
       agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV);
@@ -1700,74 +1728,53 @@
   LINT_INIT(first_expr_real);
   LINT_INIT(first_expr_dec);
 
-  if (first_expr_num != -1)
+  if (first_expr_num == -1)
+  {
+    for (uint i=0 ; i < ncases ; i+=2)
+    {
+      // No expression between CASE and the first WHEN
+      if (args[i]->val_bool())
+	return args[i+1];
+      continue;
+    }
+  }
+  else
   {
-    switch (cmp_type)
+    if (cmp_str) cmp_str->value_added= 0;
+    if (cmp_int) cmp_int->value_added= 0;
+    if (cmp_real) cmp_real->value_added= 0;
+    if (cmp_decimal) cmp_decimal->value_added= 0;
+
+    // Compare every WHEN argument with it and return the first match
+    for (uint i=0 ; i < ncases ; i+=2)
     {
+      switch (item_cmp_type(left_result_type, args[i]->result_type())) {
       case STRING_RESULT:
-      	// We can't use 'str' here as this may be overwritten
-	if (!(first_expr_str= args[first_expr_num]->val_str(&buff_str)))
-	  return else_expr_num != -1 ? args[else_expr_num] : 0;	// Impossible
+        case_item= cmp_str;
         break;
       case INT_RESULT:
-	first_expr_int= args[first_expr_num]->val_int();
-	if (args[first_expr_num]->null_value)
-	  return else_expr_num != -1 ? args[else_expr_num] : 0;
-	break;
+        case_item= cmp_int;
+        break;
       case REAL_RESULT:
-	first_expr_real= args[first_expr_num]->val_real();
-	if (args[first_expr_num]->null_value)
-	  return else_expr_num != -1 ? args[else_expr_num] : 0;
-	break;
+        case_item= cmp_real;
+        break;
       case DECIMAL_RESULT:
-        first_expr_dec= args[first_expr_num]->val_decimal(&first_expr_dec_val);
-        if (args[first_expr_num]->null_value)
-          return else_expr_num != -1 ? args[else_expr_num] : 0;
+        case_item= cmp_decimal;
         break;
       case ROW_RESULT:
       default:
-        // This case should never be chosen
 	DBUG_ASSERT(0);
 	break;
-    }
-  }
-
-  // Compare every WHEN argument with it and return the first match
-  for (uint i=0 ; i < ncases ; i+=2)
-  {
-    if (first_expr_num == -1)
-    {
-      // No expression between CASE and the first WHEN
-      if (args[i]->val_bool())
-	return args[i+1];
-      continue;
-    }
-    switch (cmp_type) {
-    case STRING_RESULT:
-      if ((tmp=args[i]->val_str(str)))		// If not null
-	if (sortcmp(tmp,first_expr_str,cmp_collation.collation)==0)
-	  return args[i+1];
-      break;
-    case INT_RESULT:
-      if (args[i]->val_int()==first_expr_int && !args[i]->null_value) 
-        return args[i+1];
-      break;
-    case REAL_RESULT: 
-      if (args[i]->val_real() == first_expr_real && !args[i]->null_value)
-        return args[i+1];
-      break;
-    case DECIMAL_RESULT:
-    {
-      my_decimal value;
-      if (my_decimal_cmp(args[i]->val_decimal(&value), first_expr_dec) == 0)
-        return args[i+1];
-      break;
-    }
-    case ROW_RESULT:
-    default:
-      // This case should never be chosen
-      DBUG_ASSERT(0);
-      break;
+      }
+      if (!case_item->value_added)
+      {
+        case_item->store_value(args[first_expr_num]);
+        if ((null_value=args[first_expr_num]->null_value))
+          return else_expr_num != -1 ? args[else_expr_num] : 0;
+        case_item->value_added= 1;
+      }
+      if (!case_item->cmp(args[i]) && !args[i]->null_value)
+        return args[i + 1];
     }
   }
   // No, WHEN clauses all missed, return ELSE expression
@@ -1874,7 +1881,8 @@
   Item **agg;
   uint nagg;
   THD *thd= current_thd;
-  
+  uint found_types= 0;
+  uint type_cnt= 0;
   if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1))))
     return;
   
@@ -1902,13 +1910,44 @@
   if (first_expr_num != -1)
   {
     agg[0]= args[first_expr_num];
+    left_result_type= agg[0]->result_type();
+
     for (nagg= 0; nagg < ncases/2 ; nagg++)
       agg[nagg+1]= args[nagg*2];
     nagg++;
-    agg_cmp_type(thd, &cmp_type, agg, nagg);
-    if ((cmp_type == STRING_RESULT) &&
-        agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV))
-      return;
+    agg_cmp_type(thd, &found_types, &type_cnt, agg, nagg);
+
+    if (found_types & (1 << (uint) STRING_RESULT))
+    {
+      if (!cmp_str)
+      {
+        if (agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV) ||
+            !(cmp_str= new cmp_item_sort_string(cmp_collation.collation)))
+          return;
+      }
+      cmp_type= STRING_RESULT;
+    }
+    if (found_types & (1 << (uint) INT_RESULT))
+    {
+      if (!cmp_int && (!(cmp_int= new cmp_item_int())))
+          return;
+
+      cmp_type= INT_RESULT;
+    }
+    if (found_types & (1 << (uint) REAL_RESULT))
+    {
+      if (!cmp_real && (!(cmp_real= new cmp_item_real())))
+          return;
+
+      cmp_type= REAL_RESULT;
+    }
+    if (found_types & (1 << (uint) DECIMAL_RESULT))
+    {
+      if (!cmp_decimal && (!(cmp_decimal= new cmp_item_decimal())))
+          return;
+
+      cmp_type= DECIMAL_RESULT;
+    }
   }
   
   if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
@@ -2495,43 +2534,45 @@
 void Item_func_in::fix_length_and_dec()
 {
   Item **arg, **arg_end;
-  uint const_itm= 1;
+  bool const_itm= 1;
   THD *thd= current_thd;
-  
-  agg_cmp_type(thd, &cmp_type, args, arg_count);
+  uint found_types= 0;
+  uint type_cnt= 0;
+  left_result_type= args[0]->result_type();
+  agg_cmp_type(thd, &found_types, &type_cnt, args, arg_count);
 
-  if (cmp_type == STRING_RESULT &&
-      agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV))
-    return;
-
-  for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
-  {
+  for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
     if (!arg[0]->const_item())
     {
       const_itm= 0;
       break;
     }
-  }
-
   /*
-    Row item with NULLs inside can return NULL or FALSE => 
+    Row item with NULLs inside can return NULL or FALSE =>
     they can't be processed as static
   */
-  if (const_itm && !nulls_in_row())
+  if (type_cnt == 1 && const_itm && !nulls_in_row())
   {
+    uint tmp_type;
+    for (tmp_type= 0; found_types - 1; found_types>>= 1)
+      tmp_type++;
+    cmp_type= (Item_result)tmp_type;
+
     switch (cmp_type) {
     case STRING_RESULT:
-      array=new in_string(arg_count-1,(qsort2_cmp) srtcmp_in, 
+      if (agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV))
+        return;
+      array=new in_string(arg_count - 1,(qsort2_cmp) srtcmp_in,
 			  cmp_collation.collation);
       break;
     case INT_RESULT:
-      array= new in_longlong(arg_count-1);
+      array= new in_longlong(arg_count - 1);
       break;
     case REAL_RESULT:
-      array= new in_double(arg_count-1);
+      array= new in_double(arg_count - 1);
       break;
     case ROW_RESULT:
-      array= new in_row(arg_count-1, args[0]);
+      array= new in_row(arg_count - 1, args[0]);
       break;
     case DECIMAL_RESULT:
       array= new in_decimal(arg_count - 1);
@@ -2551,15 +2592,39 @@
 	else
 	  have_null= 1;
       }
-      if ((array->used_count=j))
+      if ((array->used_count= j))
 	array->sort();
     }
   }
   else
   {
-    in_item= cmp_item::get_comparator(cmp_type, cmp_collation.collation);
-    if (cmp_type  == STRING_RESULT)
-      in_item->cmp_charset= cmp_collation.collation;
+    if (found_types & (1 << (uint) STRING_RESULT))
+    {
+      if (!cmp_str &&
+          (agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV) ||
+           !(cmp_str= new cmp_item_sort_string(cmp_collation.collation))))
+          return;
+    }
+    if (found_types & (1 << (uint) INT_RESULT))
+    {
+      if (!cmp_int && (!(cmp_int= new cmp_item_int())))
+          return;
+    }
+    if (found_types & (1 << (uint) REAL_RESULT))
+    {
+      if (!cmp_real && (!(cmp_real= new cmp_item_real())))
+          return;
+    }
+    if (found_types & (1 << (uint) DECIMAL_RESULT))
+    {
+      if (!cmp_decimal && (!(cmp_decimal= new cmp_item_decimal())))
+          return;
+    }
+    if (found_types & (1 << (uint) ROW_RESULT))
+    {
+      if (!cmp_row && (!(cmp_row= new cmp_item_row())))
+          return;
+    }
   }
   maybe_null= args[0]->maybe_null;
   max_length= 1;
@@ -2580,6 +2645,7 @@
 
 longlong Item_func_in::val_int()
 {
+  cmp_item *in_item;
   DBUG_ASSERT(fixed == 1);
   if (array)
   {
@@ -2587,16 +2653,45 @@
     null_value=args[0]->null_value || (!tmp && have_null);
     return (longlong) (!null_value && tmp != negated);
   }
-  in_item->store_value(args[0]);
-  if ((null_value=args[0]->null_value))
-    return 0;
-  have_null= 0;
+
+  if (cmp_str) cmp_str->value_added= 0;
+  if (cmp_int) cmp_int->value_added= 0;
+  if (cmp_real) cmp_real->value_added= 0;
+  if (cmp_decimal) cmp_decimal->value_added= 0;
+  if (cmp_row) cmp_row->value_added= 0;
+
   for (uint i=1 ; i < arg_count ; i++)
   {
+    switch (item_cmp_type(left_result_type, args[i]->result_type()))
+    {
+      case STRING_RESULT:
+        in_item= cmp_str;
+        break;
+      case INT_RESULT:
+        in_item= cmp_int;
+        break;
+      case REAL_RESULT:
+        in_item= cmp_real;
+        break;
+      case DECIMAL_RESULT:
+        in_item= cmp_decimal;
+        break;
+      case ROW_RESULT:
+        in_item= cmp_row;
+    }
+    if (!in_item->value_added)
+    {
+      in_item->store_value(args[0]);
+      if ((null_value=args[0]->null_value))
+        return 0;
+      have_null= 0;
+      in_item->value_added= 1;
+    }
     if (!in_item->cmp(args[i]) && !args[i]->null_value)
       return (longlong) (!negated);
     have_null|= args[i]->null_value;
   }
+
   null_value= have_null;
   return (longlong) (!null_value && negated);
 }

--- 1.128/sql/item_cmpfunc.h	2006-08-10 00:01:59 +04:00
+++ 1.129/sql/item_cmpfunc.h	2006-08-10 00:01:59 +04:00
@@ -586,49 +586,6 @@
 };
 
 
-class Item_func_case :public Item_func
-{
-  int first_expr_num, else_expr_num;
-  enum Item_result cached_result_type;
-  String tmp_value;
-  uint ncases;
-  Item_result cmp_type;
-  DTCollation cmp_collation;
-public:
-  Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg)
-    :Item_func(), first_expr_num(-1), else_expr_num(-1),
-    cached_result_type(INT_RESULT)
-  {
-    ncases= list.elements;
-    if (first_expr_arg)
-    {
-      first_expr_num= list.elements;
-      list.push_back(first_expr_arg);
-    }
-    if (else_expr_arg)
-    {
-      else_expr_num= list.elements;
-      list.push_back(else_expr_arg);
-    }
-    set_arguments(list);
-  }
-  double val_real();
-  longlong val_int();
-  String *val_str(String *);
-  my_decimal *val_decimal(my_decimal *);
-  bool fix_fields(THD *thd, Item **ref);
-  void fix_length_and_dec();
-  uint decimal_precision() const;
-  table_map not_null_tables() const { return 0; }
-  enum Item_result result_type () const { return cached_result_type; }
-  const char *func_name() const { return "case"; }
-  void print(String *str);
-  Item *find_item(String *str);
-  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
-  bool check_partition_func_processor(byte *bool_arg) { return 0;}
-};
-
-
 /* Functions to handle the optimized IN */
 
 
@@ -773,6 +730,7 @@
 class cmp_item :public Sql_alloc
 {
 public:
+  bool value_added;
   CHARSET_INFO *cmp_charset;
   cmp_item() { cmp_charset= &my_charset_bin; }
   virtual ~cmp_item() {}
@@ -782,6 +740,8 @@
   virtual int compare(cmp_item *item)= 0;
   static cmp_item* get_comparator(Item_result type, CHARSET_INFO *cs);
   virtual cmp_item *make_same()= 0;
+  void reset_value() { value_added= 0; }
+  void add_value() { value_added= 1; }
   virtual void store_value_by_template(cmp_item *tmpl, Item *item)
   {
     store_value(item);
@@ -793,7 +753,9 @@
 protected:
   String *value_res;
 public:
+  cmp_item_string () {}
   cmp_item_string (CHARSET_INFO *cs) { cmp_charset= cs; }
+  void set_charset(CHARSET_INFO *cs) { cmp_charset= cs; }
   friend class cmp_item_sort_string;
   friend class cmp_item_sort_string_in_static;
 };
@@ -804,6 +766,8 @@
   char value_buff[STRING_BUFFER_USUAL_SIZE];
   String value;
 public:
+  cmp_item_sort_string():
+    cmp_item_string() {}
   cmp_item_sort_string(CHARSET_INFO *cs):
     cmp_item_string(cs),
     value(value_buff, sizeof(value_buff), cs) {}
@@ -825,6 +789,11 @@
     return sortcmp(value_res, cmp->value_res, cmp_charset);
   } 
   cmp_item *make_same();
+  void set_charset(CHARSET_INFO *cs)
+  {
+    cmp_charset= cs;
+    value.set_quick(value_buff, sizeof(value_buff), cs);
+  }
 };
 
 class cmp_item_int :public cmp_item
@@ -940,17 +909,86 @@
   }
 };
 
+class Item_func_case :public Item_func
+{
+  int first_expr_num, else_expr_num;
+  enum Item_result cached_result_type, left_result_type;
+  String tmp_value;
+  uint ncases;
+  Item_result cmp_type;
+  DTCollation cmp_collation;
+  cmp_item_sort_string *cmp_str;
+  cmp_item_int *cmp_int;
+  cmp_item_real *cmp_real;
+  cmp_item_decimal *cmp_decimal;
+  cmp_item *case_item;
+public:
+  Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg)
+    :Item_func(), first_expr_num(-1), else_expr_num(-1),
+    cached_result_type(INT_RESULT), left_result_type(INT_RESULT), cmp_str(0),
+    cmp_int(0), cmp_real(0), cmp_decimal(0), case_item(0)
+  {
+    ncases= list.elements;
+    if (first_expr_arg)
+    {
+      first_expr_num= list.elements;
+      list.push_back(first_expr_arg);
+    }
+    if (else_expr_arg)
+    {
+      else_expr_num= list.elements;
+      list.push_back(else_expr_arg);
+    }
+    set_arguments(list);
+  }
+  double val_real();
+  longlong val_int();
+  String *val_str(String *);
+  my_decimal *val_decimal(my_decimal *);
+  bool fix_fields(THD *thd, Item **ref);
+  void fix_length_and_dec();
+  uint decimal_precision() const;
+  table_map not_null_tables() const { return 0; }
+  enum Item_result result_type () const { return cached_result_type; }
+  const char *func_name() const { return "case"; }
+  void print(String *str);
+  Item *find_item(String *str);
+  CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
+  bool check_partition_func_processor(byte *bool_arg) { return 0;}
+  void cleanup()
+  {
+    DBUG_ENTER("Item_func_case::cleanup");
+    Item_func::cleanup();
+    delete cmp_str;
+    delete cmp_int;
+    delete cmp_real;
+    delete cmp_decimal;
+    cmp_str= 0;
+    cmp_int= 0;
+    cmp_real= 0;
+    cmp_decimal= 0;
+    DBUG_VOID_RETURN;
+  }
+};
+
+
 class Item_func_in :public Item_func_opt_neg
 {
 public:
   Item_result cmp_type;
   in_vector *array;
-  cmp_item *in_item;
   bool have_null;
+  Item_result left_result_type;
+  cmp_item_sort_string *cmp_str;
+  cmp_item_int *cmp_int;
+  cmp_item_real *cmp_real;
+  cmp_item_decimal *cmp_decimal;
+  cmp_item_row *cmp_row;
   DTCollation cmp_collation;
 
   Item_func_in(List<Item> &list)
-    :Item_func_opt_neg(list), array(0), in_item(0), have_null(0)
+    :Item_func_opt_neg(list), array(0), have_null(0),
+    cmp_str(0), cmp_int(0), cmp_real(0), cmp_decimal(0), cmp_row(0)
   {
     allowed_arg_cols= 0;  // Fetch this value from first argument
   }
@@ -963,9 +1001,17 @@
     DBUG_ENTER("Item_func_in::cleanup");
     Item_int_func::cleanup();
     delete array;
-    delete in_item;
+    delete cmp_str;
+    delete cmp_int;
+    delete cmp_real;
+    delete cmp_decimal;
+    delete cmp_row;
     array= 0;
-    in_item= 0;
+    cmp_str= 0;
+    cmp_int= 0;
+    cmp_real= 0;
+    cmp_decimal= 0;
+    cmp_row= 0;
     DBUG_VOID_RETURN;
   }
   optimize_type select_optimize() const

--- 1.28/mysql-test/r/func_in.result	2006-08-10 00:01:59 +04:00
+++ 1.29/mysql-test/r/func_in.result	2006-08-10 00:01:59 +04:00
@@ -343,3 +343,15 @@
 1
 2
 drop table t1;
+create table t1(f1 char(1));
+insert into t1 values ('a'),('b'),('1');
+select f1 from t1 where f1 in ('a',1);
+f1
+a
+1
+select f1, case f1 when 'a' then '+' when 1 then '-' end from t1;
+f1	case f1 when 'a' then '+' when 1 then '-' end
+a	+
+b	NULL
+1	-
+drop table t1;

--- 1.22/mysql-test/t/func_in.test	2006-08-10 00:01:59 +04:00
+++ 1.23/mysql-test/t/func_in.test	2006-08-10 00:01:59 +04:00
@@ -232,3 +232,12 @@
 select some_id from t1 where some_id not in(-4,-1,-4);
 select some_id from t1 where some_id not in(-4,-1,3423534,2342342);
 drop table t1;
+
+#
+# Bug#18360: Type aggregation for IN and CASE may lead to a wrong result 
+#
+create table t1(f1 char(1));
+insert into t1 values ('a'),('b'),('1');
+select f1 from t1 where f1 in ('a',1);
+select f1, case f1 when 'a' then '+' when 1 then '-' end from t1;
+drop table t1;
Thread
bk commit into 5.1 tree (evgen:1.2247) BUG#18360eugene9 Aug