From 1adfc2f2daa6faa0a8bceb758dc8c3997f022e03 Mon Sep 17 00:00:00 2001
From: unknown <igor@igor-inspiron.creware.com>
Date: Thu, 2 Jun 2005 07:27:02 -0700
Subject: [PATCH] item_func.h:   Fixed bug #9939: a wrong conversion of
 arguments   for functions COALESCE and IFNULL.   The str_op virtual method
 was added into Item_func_numhybrid. item_func.cc:   Fixed bug #9939: a wrong
 conversion of arguments   for functions COALESCE and IFNULL.   The str_op
 virtual method was added into Item_func_numhybrid. item_cmpfunc.h,
 item_cmpfunc.cc:   Fixed bug #9939: a wrong conversion of arguments   for
 functions COALESCE and IFNULL.   Item_func_coalesce and Item_func_ifnull now 
  inherit from a modified Item_func_numhybrid. case.test, case.result:   Added
 test cases for bug #9939.

mysql-test/r/case.result:
  Added test cases for bug #9939.
mysql-test/t/case.test:
  Added test cases for bug #9939.
sql/item_cmpfunc.cc:
  Fixed bug #9939: a wrong conversion of arguments
  for functions COALESCE and IFNULL.
  Item_func_coalesce and Item_func_ifnull now
  inherit from a modified Item_func_numhybrid.
sql/item_cmpfunc.h:
  Fixed bug #9939: a wrong conversion of arguments
  for functions COALESCE and IFNULL.
  Item_func_coalesce and Item_func_ifnull now
  inherit from a modified Item_func_numhybrid.
sql/item_func.cc:
  Fixed bug #9939: a wrong conversion of arguments
  for functions COALESCE and IFNULL.
  The str_op virtual method was added into Item_func_numhybrid.
sql/item_func.h:
  Fixed bug #9939: a wrong conversion of arguments
  for functions COALESCE and IFNULL.
  he str_op virtual method was added into Item_func_numhybrid.
BitKeeper/etc/logging_ok:
  Logging to logging@openlogging.org accepted
---
 BitKeeper/etc/logging_ok |  1 +
 mysql-test/r/case.result | 17 +++++++++++++++++
 mysql-test/t/case.test   | 19 +++++++++++++++++++
 sql/item_cmpfunc.cc      | 24 ++++++++++++------------
 sql/item_cmpfunc.h       | 30 +++++++++++++-----------------
 sql/item_func.cc         | 25 +++++++++++++++++++++++++
 sql/item_func.h          |  6 ++++++
 7 files changed, 93 insertions(+), 29 deletions(-)

diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 3230f3c119f..5b842d47fdc 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -87,6 +87,7 @@ hf@deer.mysql.r18.ru
 hf@genie.(none)
 holyfoot@mysql.com
 igor@hundin.mysql.fi
+igor@igor-inspiron.creware.com
 igor@linux.local
 igor@rurik.mysql.com
 ingo@mysql.com
diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result
index 5fb21004469..a854cf4c7b0 100644
--- a/mysql-test/r/case.result
+++ b/mysql-test/r/case.result
@@ -160,3 +160,20 @@ t1	CREATE TABLE `t1` (
   `COALESCE('a' COLLATE latin1_bin,'b')` varchar(1) character set latin1 collate latin1_bin NOT NULL default ''
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 DROP TABLE t1;
+CREATE TABLE t1 (EMPNUM INT);
+INSERT INTO t1 VALUES (0), (2);
+CREATE TABLE t2 (EMPNUM DECIMAL (4, 2));
+INSERT INTO t2 VALUES (0.0), (9.0);
+SELECT COALESCE(t2.EMPNUM,t1.EMPNUM) AS CEMPNUM,
+t1.EMPNUM AS EMPMUM1, t2.EMPNUM AS EMPNUM2
+FROM t1 LEFT JOIN t2 ON t1.EMPNUM=t2.EMPNUM;
+CEMPNUM	EMPMUM1	EMPNUM2
+0.00	0	0.00
+2.00	2	NULL
+SELECT IFNULL(t2.EMPNUM,t1.EMPNUM) AS CEMPNUM,
+t1.EMPNUM AS EMPMUM1, t2.EMPNUM AS EMPNUM2
+FROM t1 LEFT JOIN t2 ON t1.EMPNUM=t2.EMPNUM;
+CEMPNUM	EMPMUM1	EMPNUM2
+0.00	0	0.00
+2.00	2	NULL
+DROP TABLE t1,t2;
diff --git a/mysql-test/t/case.test b/mysql-test/t/case.test
index fed3ff07a13..f2cfce9085d 100644
--- a/mysql-test/t/case.test
+++ b/mysql-test/t/case.test
@@ -110,3 +110,22 @@ explain extended SELECT
  COALESCE('a' COLLATE latin1_bin,'b');
 SHOW CREATE TABLE t1;
 DROP TABLE t1;
+
+#
+# Tests for bug #9939: conversion of the arguments for COALESCE and IFNULL
+#
+
+CREATE TABLE t1 (EMPNUM INT);
+INSERT INTO t1 VALUES (0), (2);
+CREATE TABLE t2 (EMPNUM DECIMAL (4, 2));
+INSERT INTO t2 VALUES (0.0), (9.0);
+
+SELECT COALESCE(t2.EMPNUM,t1.EMPNUM) AS CEMPNUM,
+               t1.EMPNUM AS EMPMUM1, t2.EMPNUM AS EMPNUM2
+  FROM t1 LEFT JOIN t2 ON t1.EMPNUM=t2.EMPNUM;
+
+SELECT IFNULL(t2.EMPNUM,t1.EMPNUM) AS CEMPNUM,
+               t1.EMPNUM AS EMPMUM1, t2.EMPNUM AS EMPNUM2
+  FROM t1 LEFT JOIN t2 ON t1.EMPNUM=t2.EMPNUM;
+
+DROP TABLE t1,t2;
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 66f0bf9c395..d64f1df78ab 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1114,8 +1114,8 @@ Item_func_ifnull::fix_length_and_dec()
   max_length= (max(args[0]->max_length - args[0]->decimals,
                    args[1]->max_length - args[1]->decimals) +
                decimals);
-  agg_result_type(&cached_result_type, args, 2);
-  switch (cached_result_type) {
+  agg_result_type(&hybrid_type, args, 2);
+  switch (hybrid_type) {
   case STRING_RESULT:
     agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV);
     break;
@@ -1153,7 +1153,7 @@ Field *Item_func_ifnull::tmp_table_field(TABLE *table)
 }
 
 double
-Item_func_ifnull::val_real()
+Item_func_ifnull::real_op()
 {
   DBUG_ASSERT(fixed == 1);
   double value= args[0]->val_real();
@@ -1169,7 +1169,7 @@ Item_func_ifnull::val_real()
 }
 
 longlong
-Item_func_ifnull::val_int()
+Item_func_ifnull::int_op()
 {
   DBUG_ASSERT(fixed == 1);
   longlong value=args[0]->val_int();
@@ -1185,7 +1185,7 @@ Item_func_ifnull::val_int()
 }
 
 
-my_decimal *Item_func_ifnull::val_decimal(my_decimal *decimal_value)
+my_decimal *Item_func_ifnull::decimal_op(my_decimal *decimal_value)
 {
   DBUG_ASSERT(fixed == 1);
   my_decimal *value= args[0]->val_decimal(decimal_value);
@@ -1202,7 +1202,7 @@ my_decimal *Item_func_ifnull::val_decimal(my_decimal *decimal_value)
 
 
 String *
-Item_func_ifnull::val_str(String *str)
+Item_func_ifnull::str_op(String *str)
 {
   DBUG_ASSERT(fixed == 1);
   String *res  =args[0]->val_str(str);
@@ -1676,7 +1676,7 @@ void Item_func_case::print(String *str)
   Coalesce - return first not NULL argument.
 */
 
-String *Item_func_coalesce::val_str(String *str)
+String *Item_func_coalesce::str_op(String *str)
 {
   DBUG_ASSERT(fixed == 1);
   null_value=0;
@@ -1690,7 +1690,7 @@ String *Item_func_coalesce::val_str(String *str)
   return 0;
 }
 
-longlong Item_func_coalesce::val_int()
+longlong Item_func_coalesce::int_op()
 {
   DBUG_ASSERT(fixed == 1);
   null_value=0;
@@ -1704,7 +1704,7 @@ longlong Item_func_coalesce::val_int()
   return 0;
 }
 
-double Item_func_coalesce::val_real()
+double Item_func_coalesce::real_op()
 {
   DBUG_ASSERT(fixed == 1);
   null_value=0;
@@ -1719,7 +1719,7 @@ double Item_func_coalesce::val_real()
 }
 
 
-my_decimal *Item_func_coalesce::val_decimal(my_decimal *decimal_value)
+my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value)
 {
   DBUG_ASSERT(fixed == 1);
   null_value= 0;
@@ -1736,8 +1736,8 @@ my_decimal *Item_func_coalesce::val_decimal(my_decimal *decimal_value)
 
 void Item_func_coalesce::fix_length_and_dec()
 {
-  agg_result_type(&cached_result_type, args, arg_count);
-  switch (cached_result_type) {
+  agg_result_type(&hybrid_type, args, arg_count);
+  switch (hybrid_type) {
   case STRING_RESULT:
     count_only_length();
     decimals= NOT_FIXED_DEC;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index a929d509723..7a22e76b217 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -453,23 +453,19 @@ class Item_func_interval :public Item_int_func
 };
 
 
-class Item_func_coalesce :public Item_func
+class Item_func_coalesce :public Item_func_numhybrid
 {
 protected:
-  enum Item_result cached_result_type;
-  Item_func_coalesce(Item *a, Item *b)
-    :Item_func(a, b), cached_result_type(INT_RESULT)
-  {}
+  Item_func_coalesce(Item *a, Item *b) :Item_func_numhybrid(a, b) {}
 public:
-  Item_func_coalesce(List<Item> &list)
-    :Item_func(list),cached_result_type(INT_RESULT)
-  {}
-  double val_real();
-  longlong val_int();
-  String *val_str(String *);
-  my_decimal *val_decimal(my_decimal *);
+  Item_func_coalesce(List<Item> &list) :Item_func_numhybrid(list) {}
+  double real_op();
+  longlong int_op();
+  String *str_op(String *);
+  my_decimal *decimal_op(my_decimal *);
   void fix_length_and_dec();
-  enum Item_result result_type () const { return cached_result_type; }
+  void find_num_type() {}
+  enum Item_result result_type () const { return hybrid_type; }
   const char *func_name() const { return "coalesce"; }
   table_map not_null_tables() const { return 0; }
 };
@@ -482,10 +478,10 @@ class Item_func_ifnull :public Item_func_coalesce
   bool field_type_defined;
 public:
   Item_func_ifnull(Item *a, Item *b) :Item_func_coalesce(a,b) {}
-  double val_real();
-  longlong val_int();
-  String *val_str(String *str);
-  my_decimal *val_decimal(my_decimal *);
+  double real_op();
+  longlong int_op();
+  String *str_op(String *str);
+  my_decimal *decimal_op(my_decimal *);
   enum_field_types field_type() const;
   void fix_length_and_dec();
   const char *func_name() const { return "ifnull"; }
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 47dffa679e9..21837dfbadb 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -815,6 +815,8 @@ String *Item_func_numhybrid::val_str(String *str)
     str->set(nr,decimals,&my_charset_bin);
     break;
   }
+  case STRING_RESULT:
+    return str_op(&str_value);
   default:
     DBUG_ASSERT(0);
   }
@@ -839,6 +841,14 @@ double Item_func_numhybrid::val_real()
     return (double)int_op();
   case REAL_RESULT:
     return real_op();
+  case STRING_RESULT:
+  {
+    char *end_not_used;
+    int err_not_used;
+    String *res= str_op(&str_value);
+    return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
+			     &end_not_used, &err_not_used) : 0.0);
+  }
   default:
     DBUG_ASSERT(0);
   }
@@ -863,6 +873,15 @@ longlong Item_func_numhybrid::val_int()
     return int_op();
   case REAL_RESULT:
     return (longlong)real_op();
+  case STRING_RESULT:
+  {
+    char *end_not_used;
+    int err_not_used;
+    String *res= str_op(&str_value);
+    CHARSET_INFO *cs= str_value.charset();
+    return (res ? (*(cs->cset->strtoll10))(cs, res->ptr(), &end_not_used,
+                                           &err_not_used) : 0);
+  }
   default:
     DBUG_ASSERT(0);
   }
@@ -891,6 +910,12 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value)
     break;
   }
   case STRING_RESULT:
+  {
+    String *res= str_op(&str_value);
+    str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(),
+                   res->length(), res->charset(), decimal_value);
+    break;
+  }  
   case ROW_RESULT:
   default:
     DBUG_ASSERT(0);
diff --git a/sql/item_func.h b/sql/item_func.h
index b53f2a0b9c6..797aec952f9 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -194,6 +194,9 @@ class Item_func_numhybrid: public Item_func
   Item_func_numhybrid(Item *a,Item *b)
     :Item_func(a,b),hybrid_type(REAL_RESULT)
   {}
+  Item_func_numhybrid(List<Item> &list)
+    :Item_func(list),hybrid_type(REAL_RESULT)
+  {}
 
   enum Item_result result_type () const { return hybrid_type; }
   void fix_length_and_dec();
@@ -208,6 +211,7 @@ class Item_func_numhybrid: public Item_func
   virtual longlong int_op()= 0;
   virtual double real_op()= 0;
   virtual my_decimal *decimal_op(my_decimal *)= 0;
+  virtual String *str_op(String *)= 0;
   bool is_null() { (void) val_real(); return null_value; }
 };
 
@@ -220,6 +224,7 @@ class Item_func_num1: public Item_func_numhybrid
 
   void fix_num_length_and_dec();
   void find_num_type();
+  String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
 };
 
 
@@ -231,6 +236,7 @@ class Item_num_op :public Item_func_numhybrid
   virtual void result_precision()= 0;
   void print(String *str) { print_op(str); }
   void find_num_type();
+  String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
 };
 
 
-- 
2.30.9