MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Evgeny Potemkin Date:November 17 2009 2:06pm
Subject:bzr commit into mysql-5.1-bugteam branch (epotemkin:3165) Bug#43668
View as plain text  
#At file:///work/bzrroot/43668-bug-5.1-bugteam/ based on revid:satya.bn@stripped

 3165 Evgeny Potemkin	2009-11-17
      Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
      
      MySQL manual describes values of the YEAR(2) field type as follows:
      values 00 - 69 mean 2000 - 2069 years and values 70 - 99 mean 1970 - 1999
      years. MIN/MAX and comparison functions was comparing them as int values
      thus producing wrong result.
      
      Now the Arg_comparator class is extended with compare_year function which
      performs correct comparison of the YEAR type.
      The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
      correctly calculate its value.
      To allow Arg_comparator to use func_name() function for Item_func and Item_sum
      objects the func_name declaration is moved to the Item_result_field class.
      A helper function is_owner_equal_func is added to the Arg_comparator class.
      It checks whether the Arg_comparator object owner is the <=> function or not.
      A helper function setup is added to the Item_sum_hybrid class. It sets up
      cache item and comparator.
     @ mysql-test/r/func_group.result
        Added a test case for the bug#43668.
     @ mysql-test/t/func_group.test
        Added a test case for the bug#43668.
     @ sql/item.cc
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        Now Item_cache_int returns the type of cached item.
     @ sql/item.h
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        To allow Arg_comparator to use func_name() function for Item_func and Item_sum
        objects the func_name declaration is moved to the Item_result_field class.
     @ sql/item_cmpfunc.cc
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        The Arg_comparator class is extended with compare_year function which
        performs correct comparison of the YEAR type.
     @ sql/item_cmpfunc.h
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        The year_as_datetime variable is added to the Arg_comparator class.
        It's set to TRUE when YEAR value should be converted to the
        YYYY-00-00 00:00:00 format for correct YEAR-DATETIME comparison.
     @ sql/item_geofunc.cc
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        Item_func_spatial_rel::val_int chenged to use Arg_comparator's string
        buffers.
     @ sql/item_subselect.h
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        Added an implementation of the virtual func_name function.
     @ sql/item_sum.cc
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
        correctly calculate its value.
        A helper function setup is added to the Item_sum_hybrid class. It sets up
        cache item and comparator.
     @ sql/item_sum.h
        Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
        The Item_sum_hybrid class now uses Item_cache and Arg_comparator objects to
        correctly calculate its value.
        Added an implementation of the virtual func_name function.

    modified:
      mysql-test/r/func_group.result
      mysql-test/t/func_group.test
      sql/item.cc
      sql/item.h
      sql/item_cmpfunc.cc
      sql/item_cmpfunc.h
      sql/item_func.h
      sql/item_geofunc.cc
      sql/item_subselect.h
      sql/item_sum.cc
      sql/item_sum.h
=== modified file 'mysql-test/r/func_group.result'
--- a/mysql-test/r/func_group.result	2009-04-01 11:10:03 +0000
+++ b/mysql-test/r/func_group.result	2009-11-17 14:06:46 +0000
@@ -885,7 +885,7 @@ cast(sum(distinct df) as signed)
 3
 select cast(min(df) as signed) from t1;
 cast(min(df) as signed)
-0
+1
 select 1e8 * sum(distinct df) from t1;
 1e8 * sum(distinct df)
 330000000
@@ -1477,3 +1477,196 @@ COUNT(*)
 SET SQL_MODE=default;
 DROP TABLE t1;
 End of 5.0 tests
+#
+# Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
+#
+create table t1 (f1 year(2), f2 year(4), f3 date, f4 datetime);
+insert into t1 values
+(98,1998,19980101,"1998-01-01 00:00:00"),
+(00,2000,20000101,"2000-01-01 00:00:01"),
+(02,2002,20020101,"2002-01-01 23:59:59"),
+(60,2060,20600101,"2060-01-01 11:11:11"),
+(70,1970,19700101,"1970-11-11 22:22:22"),
+(NULL,NULL,NULL,NULL);
+select min(f1),max(f1) from t1;
+min(f1)	max(f1)
+70	60
+select min(f2),max(f2) from t1;
+min(f2)	max(f2)
+1970	2060
+select min(f3),max(f3) from t1;
+min(f3)	max(f3)
+1970-01-01	2060-01-01
+select min(f4),max(f4) from t1;
+min(f4)	max(f4)
+1970-11-11 22:22:22	2060-01-01 11:11:11
+select a.f1 as a, b.f1 as b, a.f1 > b.f1 as gt,
+a.f1 < b.f1 as lt, a.f1<=>b.f1 as eq
+from t1 a, t1 b;
+a	b	gt	lt	eq
+98	98	0	0	1
+00	98	1	0	0
+02	98	1	0	0
+60	98	1	0	0
+70	98	0	1	0
+NULL	98	NULL	NULL	0
+98	00	0	1	0
+00	00	0	0	1
+02	00	1	0	0
+60	00	1	0	0
+70	00	0	1	0
+NULL	00	NULL	NULL	0
+98	02	0	1	0
+00	02	0	1	0
+02	02	0	0	1
+60	02	1	0	0
+70	02	0	1	0
+NULL	02	NULL	NULL	0
+98	60	0	1	0
+00	60	0	1	0
+02	60	0	1	0
+60	60	0	0	1
+70	60	0	1	0
+NULL	60	NULL	NULL	0
+98	70	1	0	0
+00	70	1	0	0
+02	70	1	0	0
+60	70	1	0	0
+70	70	0	0	1
+NULL	70	NULL	NULL	0
+98	NULL	NULL	NULL	0
+00	NULL	NULL	NULL	0
+02	NULL	NULL	NULL	0
+60	NULL	NULL	NULL	0
+70	NULL	NULL	NULL	0
+NULL	NULL	NULL	NULL	1
+select a.f1 as a, b.f2 as b, a.f1 > b.f2 as gt,
+a.f1 < b.f2 as lt, a.f1<=>b.f2 as eq
+from t1 a, t1 b;
+a	b	gt	lt	eq
+98	1998	0	0	1
+00	1998	1	0	0
+02	1998	1	0	0
+60	1998	1	0	0
+70	1998	0	1	0
+NULL	1998	NULL	NULL	0
+98	2000	0	1	0
+00	2000	0	0	1
+02	2000	1	0	0
+60	2000	1	0	0
+70	2000	0	1	0
+NULL	2000	NULL	NULL	0
+98	2002	0	1	0
+00	2002	0	1	0
+02	2002	0	0	1
+60	2002	1	0	0
+70	2002	0	1	0
+NULL	2002	NULL	NULL	0
+98	2060	0	1	0
+00	2060	0	1	0
+02	2060	0	1	0
+60	2060	0	0	1
+70	2060	0	1	0
+NULL	2060	NULL	NULL	0
+98	1970	1	0	0
+00	1970	1	0	0
+02	1970	1	0	0
+60	1970	1	0	0
+70	1970	0	0	1
+NULL	1970	NULL	NULL	0
+98	NULL	NULL	NULL	0
+00	NULL	NULL	NULL	0
+02	NULL	NULL	NULL	0
+60	NULL	NULL	NULL	0
+70	NULL	NULL	NULL	0
+NULL	NULL	NULL	NULL	1
+select a.f1 as a, b.f3 as b, a.f1 > b.f3 as gt,
+a.f1 < b.f3 as lt, a.f1<=>b.f3 as eq
+from t1 a, t1 b;
+a	b	gt	lt	eq
+98	1998-01-01	0	1	0
+00	1998-01-01	1	0	0
+02	1998-01-01	1	0	0
+60	1998-01-01	1	0	0
+70	1998-01-01	0	1	0
+NULL	1998-01-01	NULL	NULL	0
+98	2000-01-01	0	1	0
+00	2000-01-01	0	1	0
+02	2000-01-01	1	0	0
+60	2000-01-01	1	0	0
+70	2000-01-01	0	1	0
+NULL	2000-01-01	NULL	NULL	0
+98	2002-01-01	0	1	0
+00	2002-01-01	0	1	0
+02	2002-01-01	0	1	0
+60	2002-01-01	1	0	0
+70	2002-01-01	0	1	0
+NULL	2002-01-01	NULL	NULL	0
+98	2060-01-01	0	1	0
+00	2060-01-01	0	1	0
+02	2060-01-01	0	1	0
+60	2060-01-01	0	1	0
+70	2060-01-01	0	1	0
+NULL	2060-01-01	NULL	NULL	0
+98	1970-01-01	1	0	0
+00	1970-01-01	1	0	0
+02	1970-01-01	1	0	0
+60	1970-01-01	1	0	0
+70	1970-01-01	0	1	0
+NULL	1970-01-01	NULL	NULL	0
+98	NULL	NULL	NULL	0
+00	NULL	NULL	NULL	0
+02	NULL	NULL	NULL	0
+60	NULL	NULL	NULL	0
+70	NULL	NULL	NULL	0
+NULL	NULL	NULL	NULL	1
+select a.f1 as a, b.f4 as b, a.f1 > b.f4 as gt,
+a.f1 < b.f4 as lt, a.f1<=>b.f4 as eq
+from t1 a, t1 b;
+a	b	gt	lt	eq
+98	1998-01-01 00:00:00	0	1	0
+00	1998-01-01 00:00:00	1	0	0
+02	1998-01-01 00:00:00	1	0	0
+60	1998-01-01 00:00:00	1	0	0
+70	1998-01-01 00:00:00	0	1	0
+NULL	1998-01-01 00:00:00	NULL	NULL	0
+98	2000-01-01 00:00:01	0	1	0
+00	2000-01-01 00:00:01	0	1	0
+02	2000-01-01 00:00:01	1	0	0
+60	2000-01-01 00:00:01	1	0	0
+70	2000-01-01 00:00:01	0	1	0
+NULL	2000-01-01 00:00:01	NULL	NULL	0
+98	2002-01-01 23:59:59	0	1	0
+00	2002-01-01 23:59:59	0	1	0
+02	2002-01-01 23:59:59	0	1	0
+60	2002-01-01 23:59:59	1	0	0
+70	2002-01-01 23:59:59	0	1	0
+NULL	2002-01-01 23:59:59	NULL	NULL	0
+98	2060-01-01 11:11:11	0	1	0
+00	2060-01-01 11:11:11	0	1	0
+02	2060-01-01 11:11:11	0	1	0
+60	2060-01-01 11:11:11	0	1	0
+70	2060-01-01 11:11:11	0	1	0
+NULL	2060-01-01 11:11:11	NULL	NULL	0
+98	1970-11-11 22:22:22	1	0	0
+00	1970-11-11 22:22:22	1	0	0
+02	1970-11-11 22:22:22	1	0	0
+60	1970-11-11 22:22:22	1	0	0
+70	1970-11-11 22:22:22	0	1	0
+NULL	1970-11-11 22:22:22	NULL	NULL	0
+98	NULL	NULL	NULL	0
+00	NULL	NULL	NULL	0
+02	NULL	NULL	NULL	0
+60	NULL	NULL	NULL	0
+70	NULL	NULL	NULL	0
+NULL	NULL	NULL	NULL	1
+select *, f1 = f2 from t1;
+f1	f2	f3	f4	f1 = f2
+98	1998	1998-01-01	1998-01-01 00:00:00	1
+00	2000	2000-01-01	2000-01-01 00:00:01	1
+02	2002	2002-01-01	2002-01-01 23:59:59	1
+60	2060	2060-01-01	2060-01-01 11:11:11	1
+70	1970	1970-01-01	1970-11-11 22:22:22	1
+NULL	NULL	NULL	NULL	NULL
+drop table t1;
+#

=== modified file 'mysql-test/t/func_group.test'
--- a/mysql-test/t/func_group.test	2009-04-01 11:10:03 +0000
+++ b/mysql-test/t/func_group.test	2009-11-17 14:06:46 +0000
@@ -1006,3 +1006,35 @@ DROP TABLE t1;
 
 ###
 --echo End of 5.0 tests
+
+--echo #
+--echo # Bug#43668: Wrong comparison and MIN/MAX for YEAR(2)
+--echo #
+create table t1 (f1 year(2), f2 year(4), f3 date, f4 datetime);
+insert into t1 values
+  (98,1998,19980101,"1998-01-01 00:00:00"),
+  (00,2000,20000101,"2000-01-01 00:00:01"),
+  (02,2002,20020101,"2002-01-01 23:59:59"),
+  (60,2060,20600101,"2060-01-01 11:11:11"),
+  (70,1970,19700101,"1970-11-11 22:22:22"),
+  (NULL,NULL,NULL,NULL);
+select min(f1),max(f1) from t1;
+select min(f2),max(f2) from t1;
+select min(f3),max(f3) from t1;
+select min(f4),max(f4) from t1;
+select a.f1 as a, b.f1 as b, a.f1 > b.f1 as gt,
+       a.f1 < b.f1 as lt, a.f1<=>b.f1 as eq
+from t1 a, t1 b;
+select a.f1 as a, b.f2 as b, a.f1 > b.f2 as gt,
+       a.f1 < b.f2 as lt, a.f1<=>b.f2 as eq
+from t1 a, t1 b;
+select a.f1 as a, b.f3 as b, a.f1 > b.f3 as gt,
+       a.f1 < b.f3 as lt, a.f1<=>b.f3 as eq
+from t1 a, t1 b;
+select a.f1 as a, b.f4 as b, a.f1 > b.f4 as gt,
+       a.f1 < b.f4 as lt, a.f1<=>b.f4 as eq
+from t1 a, t1 b;
+select *, f1 = f2 from t1;
+drop table t1;
+--echo #
+

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-09-29 02:23:38 +0000
+++ b/sql/item.cc	2009-11-17 14:06:46 +0000
@@ -6940,7 +6940,7 @@ Item_cache* Item_cache::get_cache(const 
 {
   switch (item->result_type()) {
   case INT_RESULT:
-    return new Item_cache_int();
+    return new Item_cache_int(item->field_type());
   case REAL_RESULT:
     return new Item_cache_real();
   case DECIMAL_RESULT:

=== modified file 'sql/item.h'
--- a/sql/item.h	2009-08-28 10:55:59 +0000
+++ b/sql/item.h	2009-11-17 14:06:46 +0000
@@ -2135,6 +2135,23 @@ public:
     save_in_field(result_field, no_conversions);
   }
   void cleanup();
+  /*
+    This method is used for debug purposes to print the name of an
+    item to the debug log. The second use of this method is as
+    a helper function of print() and error messages, where it is
+    applicable. To suit both goals it should return a meaningful,
+    distinguishable and sintactically correct string. This method
+    should not be used for runtime type identification, use enum
+    {Sum}Functype and Item_func::functype()/Item_sum::sum_func()
+    instead.
+    Added here, to the parent class of both Item_func and Item_sum_func.
+
+    NOTE: for Items inherited from Item_sum, func_name() return part of
+    function name till first argument (including '(') to make difference in
+    names for functions with 'distinct' clause and without 'distinct' and
+    also to make printing of items inherited from Item_sum uniform.
+  */
+  virtual const char *func_name() const= 0;
 };
 
 

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2009-10-05 05:27:36 +0000
+++ b/sql/item_cmpfunc.cc	2009-11-17 14:06:46 +0000
@@ -30,6 +30,9 @@
 #include "sql_select.h"
 
 static bool convert_constant_item(THD *, Item_field *, Item **);
+static longlong
+get_year_value(THD *thd, Item ***item_arg, Item **cache_arg,
+               Item *warn_item, bool *is_null);
 
 static Item_result item_store_type(Item_result a, Item *item,
                                    my_bool unsigned_flag)
@@ -533,11 +536,12 @@ void Item_bool_func2::fix_length_and_dec
 }
 
 
-int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
+int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
 {
   owner= item;
   func= comparator_matrix[type]
-                         [test(owner->functype() == Item_func::EQUAL_FUNC)];
+                         [is_owner_equal_func()];
+
   switch (type) {
   case ROW_RESULT:
   {
@@ -557,7 +561,8 @@ int Arg_comparator::set_compare_func(Ite
 	my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
 	return 1;
       }
-      if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i)))
+      if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i),
+                                      set_null))
         return 1;
     }
     break;
@@ -571,7 +576,8 @@ int Arg_comparator::set_compare_func(Ite
     if (cmp_collation.set((*a)->collation, (*b)->collation) || 
 	cmp_collation.derivation == DERIVATION_NONE)
     {
-      my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name());
+      my_coll_agg_error((*a)->collation, (*b)->collation,
+                        owner->func_name());
       return 1;
     }
     if (cmp_collation.collation == &my_charset_bin)
@@ -849,7 +855,7 @@ get_time_value(THD *thd, Item ***item_ar
 }
 
 
-int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
+int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
                                         Item **a1, Item **a2,
                                         Item_result type)
 {
@@ -857,11 +863,11 @@ int Arg_comparator::set_cmp_func(Item_bo
   ulonglong const_value= (ulonglong)-1;
   a= a1;
   b= a2;
+  owner= owner_arg;
+  thd= current_thd;
 
   if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))
   {
-    thd= current_thd;
-    owner= owner_arg;
     a_type= (*a)->field_type();
     b_type= (*b)->field_type();
     a_cache= 0;
@@ -885,22 +891,22 @@ int Arg_comparator::set_cmp_func(Item_bo
         b= (Item **)&b_cache;
       }
     }
-    is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
+    is_nulls_eq= is_owner_equal_func();
     func= &Arg_comparator::compare_datetime;
-    get_value_func= &get_datetime_value;
+    get_value_a_func= &get_datetime_value;
+    get_value_b_func= &get_datetime_value;
     return 0;
   }
   else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME &&
            (*b)->field_type() == MYSQL_TYPE_TIME)
   {
     /* Compare TIME values as integers. */
-    thd= current_thd;
-    owner= owner_arg;
     a_cache= 0;
     b_cache= 0;
-    is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
+    is_nulls_eq= is_owner_equal_func();
     func= &Arg_comparator::compare_datetime;
-    get_value_func= &get_time_value;
+    get_value_a_func= &get_time_value;
+    get_value_b_func= &get_time_value;
     return 0;
   }
   else if (type == STRING_RESULT &&
@@ -909,20 +915,39 @@ int Arg_comparator::set_cmp_func(Item_bo
   {
     DTCollation coll;
     coll.set((*a)->collation.collation);
-    if (agg_item_set_converter(coll, owner_arg->func_name(),
+    if (agg_item_set_converter(coll, owner->func_name(),
                                b, 1, MY_COLL_CMP_CONV, 1))
       return 1;
-  }
+  } else if (type != ROW_RESULT && ((*a)->field_type() == MYSQL_TYPE_YEAR ||
+             (*b)->field_type() == MYSQL_TYPE_YEAR))
+  {
+    is_nulls_eq= is_owner_equal_func();
+    if ((*a)->is_datetime())
+    {
+      year_as_datetime= TRUE;
+      get_value_a_func= &get_datetime_value;
+    } else if ((*a)->field_type() == MYSQL_TYPE_YEAR)
+      get_value_a_func= &get_year_value;
 
+    if ((*b)->is_datetime())
+    {
+      year_as_datetime= TRUE;
+      get_value_b_func= &get_datetime_value;
+    } else if ((*b)->field_type() == MYSQL_TYPE_YEAR)
+      get_value_b_func= &get_year_value;
+
+    func= &Arg_comparator::compare_year;
+    return 0;
+  }
   return set_compare_func(owner_arg, type);
 }
 
 
-void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
+void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg,
+                                           Item **a1, Item **b1)
 {
   thd= current_thd;
-  /* A caller will handle null values by itself. */
-  owner= NULL;
+  owner= owner_arg;
   a= a1;
   b= b1;
   a_type= (*a)->field_type();
@@ -931,7 +956,8 @@ void Arg_comparator::set_datetime_cmp_fu
   b_cache= 0;
   is_nulls_eq= FALSE;
   func= &Arg_comparator::compare_datetime;
-  get_value_func= &get_datetime_value;
+  get_value_a_func= &get_datetime_value;
+  get_value_b_func= &get_datetime_value;
 }
 
 
@@ -1031,6 +1057,51 @@ get_datetime_value(THD *thd, Item ***ite
   return value;
 }
 
+
+/*
+  Retrieves YEAR value of 19XX form from given item.
+
+  SYNOPSIS
+    get_year_value()
+    thd                 thread handle
+    item_arg   [in/out] item to retrieve YEAR value from
+    cache_arg  [in/out] pointer to place to store the caching item to
+    warn_item  [in]     item for issuing the conversion warning
+    is_null    [out]    TRUE <=> the item_arg is null
+
+  DESCRIPTION
+    Retrieves the YEAR value of 19XX form from given item for comparison by the
+    compare_year() function.
+
+  RETURN
+    obtained value
+*/
+
+static longlong
+get_year_value(THD *thd, Item ***item_arg, Item **cache_arg,
+               Item *warn_item, bool *is_null)
+{
+  longlong value= 0;
+  Item *item= **item_arg;
+
+  value= item->val_int();
+  *is_null= item->null_value;
+  if (*is_null)
+    return ~(ulonglong) 0;
+
+  /*
+    Coerce value to the 19XX form in order to correctly compare
+    YEAR(2) & YEAR(4) types.
+  */
+  if (value < 70)
+    value+= 100;
+  if (value <= 1900)
+    value+= 1900;
+
+  return value;
+}
+
+
 /*
   Compare items values as dates.
 
@@ -1063,25 +1134,25 @@ int Arg_comparator::compare_datetime()
   longlong a_value, b_value;
 
   /* Get DATE/DATETIME/TIME value of the 'a' item. */
-  a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null);
+  a_value= (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null);
   if (!is_nulls_eq && a_is_null)
   {
-    if (owner)
+    if (set_null)
       owner->null_value= 1;
     return -1;
   }
 
   /* Get DATE/DATETIME/TIME value of the 'b' item. */
-  b_value= (*get_value_func)(thd, &b, &b_cache, *a, &b_is_null);
+  b_value= (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null);
   if (a_is_null || b_is_null)
   {
-    if (owner)
+    if (set_null)
       owner->null_value= is_nulls_eq ? 0 : 1;
     return is_nulls_eq ? (a_is_null == b_is_null) : -1;
   }
 
   /* Here we have two not-NULL values. */
-  if (owner)
+  if (set_null)
     owner->null_value= 0;
 
   /* Compare values. */
@@ -1094,15 +1165,17 @@ int Arg_comparator::compare_datetime()
 int Arg_comparator::compare_string()
 {
   String *res1,*res2;
-  if ((res1= (*a)->val_str(&owner->tmp_value1)))
+  if ((res1= (*a)->val_str(&value1)))
   {
-    if ((res2= (*b)->val_str(&owner->tmp_value2)))
+    if ((res2= (*b)->val_str(&value2)))
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       return sortcmp(res1,res2,cmp_collation.collation);
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1121,18 +1194,20 @@ int Arg_comparator::compare_string()
 int Arg_comparator::compare_binary_string()
 {
   String *res1,*res2;
-  if ((res1= (*a)->val_str(&owner->tmp_value1)))
+  if ((res1= (*a)->val_str(&value1)))
   {
-    if ((res2= (*b)->val_str(&owner->tmp_value2)))
+    if ((res2= (*b)->val_str(&value2)))
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       uint res1_length= res1->length();
       uint res2_length= res2->length();
       int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length));
       return cmp ? cmp : (int) (res1_length - res2_length);
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1145,8 +1220,8 @@ int Arg_comparator::compare_binary_strin
 int Arg_comparator::compare_e_string()
 {
   String *res1,*res2;
-  res1= (*a)->val_str(&owner->tmp_value1);
-  res2= (*b)->val_str(&owner->tmp_value2);
+  res1= (*a)->val_str(&value1);
+  res2= (*b)->val_str(&value2);
   if (!res1 || !res2)
     return test(res1 == res2);
   return test(sortcmp(res1, res2, cmp_collation.collation) == 0);
@@ -1156,8 +1231,8 @@ int Arg_comparator::compare_e_string()
 int Arg_comparator::compare_e_binary_string()
 {
   String *res1,*res2;
-  res1= (*a)->val_str(&owner->tmp_value1);
-  res2= (*b)->val_str(&owner->tmp_value2);
+  res1= (*a)->val_str(&value1);
+  res2= (*b)->val_str(&value2);
   if (!res1 || !res2)
     return test(res1 == res2);
   return test(stringcmp(res1, res2) == 0);
@@ -1178,13 +1253,15 @@ int Arg_comparator::compare_real()
     val2= (*b)->val_real();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (val1 < val2)	return -1;
       if (val1 == val2) return 0;
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1198,11 +1275,13 @@ int Arg_comparator::compare_decimal()
     my_decimal *val2= (*b)->val_decimal(&value2);
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       return my_decimal_cmp(val1, val2);
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1240,7 +1319,8 @@ int Arg_comparator::compare_real_fixed()
     val2= (*b)->val_real();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (val1 == val2 || fabs(val1 - val2) < precision)
         return 0;
       if (val1 < val2)
@@ -1248,7 +1328,8 @@ int Arg_comparator::compare_real_fixed()
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1271,13 +1352,15 @@ int Arg_comparator::compare_int_signed()
     longlong val2= (*b)->val_int();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (val1 < val2)	return -1;
       if (val1 == val2)   return 0;
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1294,13 +1377,15 @@ int Arg_comparator::compare_int_unsigned
     ulonglong val2= (*b)->val_int();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (val1 < val2)	return -1;
       if (val1 == val2)   return 0;
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1317,7 +1402,8 @@ int Arg_comparator::compare_int_signed_u
     ulonglong uval2= (ulonglong)(*b)->val_int();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (sval1 < 0 || (ulonglong)sval1 < uval2)
         return -1;
       if ((ulonglong)sval1 == uval2)
@@ -1325,7 +1411,8 @@ int Arg_comparator::compare_int_signed_u
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1342,7 +1429,8 @@ int Arg_comparator::compare_int_unsigned
     longlong sval2= (*b)->val_int();
     if (!(*b)->null_value)
     {
-      owner->null_value= 0;
+      if (set_null)
+        owner->null_value= 0;
       if (sval2 < 0)
         return 1;
       if (uval1 < (ulonglong)sval2)
@@ -1352,7 +1440,8 @@ int Arg_comparator::compare_int_unsigned
       return 1;
     }
   }
-  owner->null_value= 1;
+  if (set_null)
+    owner->null_value= 1;
   return -1;
 }
 
@@ -1388,10 +1477,11 @@ int Arg_comparator::compare_row()
   for (uint i= 0; i<n; i++)
   {
     res= comparators[i].compare();
-    if (owner->null_value)
+    /* Aggregate functions don't need special null handling. */
+    if (owner->null_value && owner->type() == Item::FUNC_ITEM)
     {
       // NULL was compared
-      switch (owner->functype()) {
+      switch (((Item_func*)owner)->functype()) {
       case Item_func::NE_FUNC:
         break; // NE never aborts on NULL even if abort_on_null is set
       case Item_func::LT_FUNC:
@@ -1400,7 +1490,7 @@ int Arg_comparator::compare_row()
       case Item_func::GE_FUNC:
         return -1; // <, <=, > and >= always fail on NULL
       default: // EQ_FUNC
-        if (owner->abort_on_null)
+        if (((Item_bool_func2*)owner)->abort_on_null)
           return -1; // We do not need correct NULL returning
       }
       was_null= 1;
@@ -1437,6 +1527,67 @@ int Arg_comparator::compare_e_row()
 }
 
 
+/**
+  Compare values as YEAR.
+
+  @details
+    Compare items as YEAR for EQUAL_FUNC and for other comparison functions.
+    The YEAR values of form 19XX are obtained with help of the get_year_value()
+    function.
+    If one of arguments is of DATE/DATETIME type its value is obtained
+    with help of the get_datetime_value function. In this case YEAR values
+    prior to comparison are converted to the ulonglong YYYY-00-00 00:00:00
+    DATETIME form.
+    If an argument type neither YEAR nor DATE/DATEIME then val_int function
+    is used to obtain value for comparison.
+
+  RETURN
+    If is_nulls_eq is TRUE:
+       1    if items are equal or both are null
+       0    otherwise
+    If is_nulls_eq is FALSE:
+      -1   a < b
+       0   a == b or at least one of items is null
+       1   a > b
+    See the table:
+    is_nulls_eq | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
+    a_is_null   | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+    b_is_null   | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
+    result      | 1 | 0 | 0 |0/1| 0 | 0 | 0 |-1/0/1|
+*/
+
+int Arg_comparator::compare_year()
+{
+  bool a_is_null, b_is_null;
+  ulonglong val1= get_value_a_func ?
+                    (*get_value_a_func)(thd, &a, &a_cache, *b, &a_is_null) :
+                    (*a)->val_int();
+  ulonglong val2= get_value_b_func ?
+                    (*get_value_b_func)(thd, &b, &b_cache, *a, &b_is_null) :
+                    (*b)->val_int();
+  if (!(*a)->null_value)
+  {
+    if (!(*b)->null_value)
+    {
+      if (set_null)
+        owner->null_value= 0;
+      /* Convert year to DATETIME of form YYYY-00-00 00:00:00 when necessary. */
+      if((*a)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime)
+        val1*= 10000000000LL;
+      if((*b)->field_type() == MYSQL_TYPE_YEAR && year_as_datetime)
+        val2*= 10000000000LL;
+
+      if (val1 < val2)	return is_nulls_eq ? 0 : -1;
+      if (val1 == val2)   return is_nulls_eq ? 1 : 0;
+      return is_nulls_eq ? 0 : 1;
+    }
+  }
+  if (set_null)
+    owner->null_value= is_nulls_eq ? 0 : 1;
+  return (is_nulls_eq && (*a)->null_value == (*b)->null_value) ? 1 : 0;
+}
+
+
 void Item_func_truth::fix_length_and_dec()
 {
   maybe_null= 0;
@@ -1711,8 +1862,8 @@ longlong Item_func_lt::val_int()
 longlong Item_func_strcmp::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  String *a=args[0]->val_str(&tmp_value1);
-  String *b=args[1]->val_str(&tmp_value2);
+  String *a=args[0]->val_str(&cmp.value1);
+  String *b=args[1]->val_str(&cmp.value2);
   if (!a || !b)
   {
     null_value=1;
@@ -1995,8 +2146,8 @@ void Item_func_between::fix_length_and_d
 
   if (compare_as_dates)
   {
-    ge_cmp.set_datetime_cmp_func(args, args + 1);
-    le_cmp.set_datetime_cmp_func(args, args + 2);
+    ge_cmp.set_datetime_cmp_func(this, args, args + 1);
+    le_cmp.set_datetime_cmp_func(this, args, args + 2);
   }
   else if (time_items_found == 3)
   {
@@ -4324,13 +4475,13 @@ void Item_func_isnotnull::print(String *
 longlong Item_func_like::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  String* res = args[0]->val_str(&tmp_value1);
+  String* res = args[0]->val_str(&cmp.value1);
   if (args[0]->null_value)
   {
     null_value=1;
     return 0;
   }
-  String* res2 = args[1]->val_str(&tmp_value2);
+  String* res2 = args[1]->val_str(&cmp.value2);
   if (args[1]->null_value)
   {
     null_value=1;
@@ -4354,7 +4505,7 @@ Item_func::optimize_type Item_func_like:
 {
   if (args[1]->const_item())
   {
-    String* res2= args[1]->val_str((String *)&tmp_value2);
+    String* res2= args[1]->val_str((String *)&cmp.value2);
 
     if (!res2)
       return OPTIMIZE_NONE;
@@ -4385,7 +4536,7 @@ bool Item_func_like::fix_fields(THD *thd
   if (escape_item->const_item())
   {
     /* If we are on execution stage */
-    String *escape_str= escape_item->val_str(&tmp_value1);
+    String *escape_str= escape_item->val_str(&cmp.value1);
     if (escape_str)
     {
       if (escape_used_in_parsing && (
@@ -4440,7 +4591,7 @@ bool Item_func_like::fix_fields(THD *thd
     if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
        !(specialflag & SPECIAL_NO_NEW_FUNC))
     {
-      String* res2 = args[1]->val_str(&tmp_value2);
+      String* res2 = args[1]->val_str(&cmp.value2);
       if (!res2)
         return FALSE;				// Null argument
       

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2008-12-12 11:13:11 +0000
+++ b/sql/item_cmpfunc.h	2009-11-17 14:06:46 +0000
@@ -32,7 +32,7 @@ class Arg_comparator: public Sql_alloc
 {
   Item **a, **b;
   arg_cmp_func func;
-  Item_bool_func2 *owner;
+  Item_result_field *owner;
   Arg_comparator *comparators;   // used only for compare_row()
   double precision;
   /* Fields used in DATE/DATETIME comparison. */
@@ -40,30 +40,42 @@ class Arg_comparator: public Sql_alloc
   enum_field_types a_type, b_type; // Types of a and b items
   Item *a_cache, *b_cache;         // Cached values of a and b items
   bool is_nulls_eq;                // TRUE <=> compare for the EQUAL_FUNC
+  bool set_null;                   // TRUE <=> set owner->null_value
+                                   //   when one of arguments is NULL.
+  bool year_as_datetime;           // TRUE <=> convert YEAR value to
+                                   // the YYYY-00-00 00:00:00 DATETIME
+                                   // format. See compare_year.
   enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE,
                             CMP_DATE_WITH_STR, CMP_STR_WITH_DATE };
-  longlong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg,
-                             Item *warn_item, bool *is_null);
+  longlong (*get_value_a_func)(THD *thd, Item ***item_arg, Item **cache_arg,
+                               Item *warn_item, bool *is_null);
+  longlong (*get_value_b_func)(THD *thd, Item ***item_arg, Item **cache_arg,
+                               Item *warn_item, bool *is_null);
 public:
   DTCollation cmp_collation;
+  /* Allow owner function to use string buffers. */
+  String value1, value2;
 
-  Arg_comparator(): thd(0), a_cache(0), b_cache(0) {};
+  Arg_comparator(): thd(0), a_cache(0), b_cache(0), set_null(0),
+    year_as_datetime(0), get_value_a_func(0), get_value_b_func(0) {};
   Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0),
-    a_cache(0), b_cache(0) {};
+    a_cache(0), b_cache(0), set_null(0), year_as_datetime(0),
+    get_value_a_func(0), get_value_b_func(0) {};
 
-  int set_compare_func(Item_bool_func2 *owner, Item_result type);
-  inline int set_compare_func(Item_bool_func2 *owner_arg)
+  int set_compare_func(Item_result_field *owner, Item_result type);
+  inline int set_compare_func(Item_result_field *owner_arg)
   {
     return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
                                                      (*b)->result_type()));
   }
-  int set_cmp_func(Item_bool_func2 *owner_arg,
+  int set_cmp_func(Item_result_field *owner_arg,
 			  Item **a1, Item **a2,
 			  Item_result type);
 
-  inline int set_cmp_func(Item_bool_func2 *owner_arg,
-			  Item **a1, Item **a2)
+  inline int set_cmp_func(Item_result_field *owner_arg,
+			  Item **a1, Item **a2, bool set_null_arg)
   {
+    set_null= set_null_arg;
     return set_cmp_func(owner_arg, a1, a2,
                         item_cmp_type((*a1)->result_type(),
                                       (*a2)->result_type()));
@@ -89,12 +101,18 @@ public:
   int compare_real_fixed();
   int compare_e_real_fixed();
   int compare_datetime();        // compare args[0] & args[1] as DATETIMEs
+  int compare_year();
 
   static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b,
                                                       ulonglong *const_val_arg);
 
-  void set_datetime_cmp_func(Item **a1, Item **b1);
+  void set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1);
   static arg_cmp_func comparator_matrix [5][2];
+  inline bool is_owner_equal_func()
+  {
+    return (owner->type() == Item::FUNC_ITEM &&
+           ((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC);
+  }
 
   friend class Item_func;
 };
@@ -324,7 +342,6 @@ class Item_bool_func2 :public Item_int_f
 {						/* Bool with 2 string args */
 protected:
   Arg_comparator cmp;
-  String tmp_value1,tmp_value2;
   bool abort_on_null;
 
 public:
@@ -333,7 +350,7 @@ public:
   void fix_length_and_dec();
   void set_cmp_func()
   {
-    cmp.set_cmp_func(this, tmp_arg, tmp_arg+1);
+    cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE);
   }
   optimize_type select_optimize() const { return OPTIMIZE_OP; }
   virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }

=== modified file 'sql/item_func.h'
--- a/sql/item_func.h	2009-08-24 19:47:08 +0000
+++ b/sql/item_func.h	2009-11-17 14:06:46 +0000
@@ -124,17 +124,6 @@ public:
   virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; }
   virtual bool have_rev_func() const { return 0; }
   virtual Item *key_item() const { return args[0]; }
-  /*
-    This method is used for debug purposes to print the name of an
-    item to the debug log. The second use of this method is as
-    a helper function of print(), where it is applicable.
-    To suit both goals it should return a meaningful,
-    distinguishable and sintactically correct string.  This method
-    should not be used for runtime type identification, use enum
-    {Sum}Functype and Item_func::functype()/Item_sum::sum_func()
-    instead.
-  */
-  virtual const char *func_name() const= 0;
   virtual bool const_item() const { return const_item_cache; }
   inline Item **arguments() const { return args; }
   void set_arguments(List<Item> &list);

=== modified file 'sql/item_geofunc.cc'
--- a/sql/item_geofunc.cc	2009-07-10 23:12:13 +0000
+++ b/sql/item_geofunc.cc	2009-11-17 14:06:46 +0000
@@ -506,8 +506,8 @@ err:
 longlong Item_func_spatial_rel::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  String *res1= args[0]->val_str(&tmp_value1);
-  String *res2= args[1]->val_str(&tmp_value2);
+  String *res1= args[0]->val_str(&cmp.value1);
+  String *res2= args[1]->val_str(&cmp.value2);
   Geometry_buffer buffer1, buffer2;
   Geometry *g1, *g2;
   MBR mbr1, mbr2;

=== modified file 'sql/item_subselect.h'
--- a/sql/item_subselect.h	2008-02-22 10:30:33 +0000
+++ b/sql/item_subselect.h	2009-11-17 14:06:46 +0000
@@ -132,6 +132,7 @@ public:
     @return the SELECT_LEX structure associated with this Item
   */
   st_select_lex* get_select_lex();
+  const char *func_name() const { DBUG_ASSERT(0); return "subselect"; }
 
   friend class select_subselect;
   friend class Item_in_optimizer;

=== modified file 'sql/item_sum.cc'
--- a/sql/item_sum.cc	2009-08-24 19:47:08 +0000
+++ b/sql/item_sum.cc	2009-11-17 14:06:46 +0000
@@ -614,35 +614,6 @@ Item_sum_num::fix_fields(THD *thd, Item 
 }
 
 
-Item_sum_hybrid::Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
-  :Item_sum(thd, item), value(item->value), hybrid_type(item->hybrid_type),
-  hybrid_field_type(item->hybrid_field_type), cmp_sign(item->cmp_sign),
-  was_values(item->was_values)
-{
-  /* copy results from old value */
-  switch (hybrid_type) {
-  case INT_RESULT:
-    sum_int= item->sum_int;
-    break;
-  case DECIMAL_RESULT:
-    my_decimal2decimal(&item->sum_dec, &sum_dec);
-    break;
-  case REAL_RESULT:
-    sum= item->sum;
-    break;
-  case STRING_RESULT:
-    /*
-      This can happen with ROLLUP. Note that the value is already
-      copied at function call.
-    */
-    break;
-  case ROW_RESULT:
-  default:
-    DBUG_ASSERT(0);
-  }
-  collation.set(item->collation);
-}
-
 bool
 Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
 {
@@ -662,15 +633,12 @@ Item_sum_hybrid::fix_fields(THD *thd, It
   switch (hybrid_type= item->result_type()) {
   case INT_RESULT:
     max_length= 20;
-    sum_int= 0;
     break;
   case DECIMAL_RESULT:
     max_length= item->max_length;
-    my_decimal_set_zero(&sum_dec);
     break;
   case REAL_RESULT:
     max_length= float_length(decimals);
-    sum= 0.0;
     break;
   case STRING_RESULT:
     max_length= item->max_length;
@@ -679,10 +647,10 @@ Item_sum_hybrid::fix_fields(THD *thd, It
   default:
     DBUG_ASSERT(0);
   };
+  setup(args[0], NULL);
   /* MIN/MAX can return NULL for empty set indepedent of the used column */
   maybe_null= 1;
   unsigned_flag=item->unsigned_flag;
-  collation.set(item->collation);
   result_field=0;
   null_value=1;
   fix_length_and_dec();
@@ -700,6 +668,31 @@ Item_sum_hybrid::fix_fields(THD *thd, It
   return FALSE;
 }
 
+
+/**
+  MIN/MAX function setup.
+
+  @param item       argument of MIN/MAX function
+  @param value_arg  calculated value of MIN/MAX function
+
+  @details
+    Setup cache/comparator of MIN/MAX functions. When called by the
+    copy_or_same function value_arg parameter contains calculated value
+    of the original MIN/MAX object and it is saved in this object's cache.
+*/
+
+void Item_sum_hybrid::setup(Item *item, Item *value_arg)
+{
+  value= Item_cache::get_cache(item);
+  value->setup(item);
+  if (value_arg)
+    value->store(value_arg);
+  cmp= new Arg_comparator();
+  cmp->set_cmp_func(this, args, (Item**)&value, FALSE);
+  collation.set(item->collation);
+}
+
+
 Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
 					 uint convert_blob_length)
 {
@@ -1591,19 +1584,7 @@ void Item_sum_variance::update_field()
 
 void Item_sum_hybrid::clear()
 {
-  switch (hybrid_type) {
-  case INT_RESULT:
-    sum_int= 0;
-    break;
-  case DECIMAL_RESULT:
-    my_decimal_set_zero(&sum_dec);
-    break;
-  case REAL_RESULT:
-    sum= 0.0;
-    break;
-  default:
-    value.length(0);
-  }
+  value->null_value= 1;
   null_value= 1;
 }
 
@@ -1612,30 +1593,7 @@ double Item_sum_hybrid::val_real()
   DBUG_ASSERT(fixed == 1);
   if (null_value)
     return 0.0;
-  switch (hybrid_type) {
-  case STRING_RESULT:
-  {
-    char *end_not_used;
-    int err_not_used;
-    String *res;  res=val_str(&str_value);
-    return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
-			     &end_not_used, &err_not_used) : 0.0);
-  }
-  case INT_RESULT:
-    if (unsigned_flag)
-      return ulonglong2double(sum_int);
-    return (double) sum_int;
-  case DECIMAL_RESULT:
-    my_decimal2double(E_DEC_FATAL_ERROR, &sum_dec, &sum);
-    return sum;
-  case REAL_RESULT:
-    return sum;
-  case ROW_RESULT:
-  default:
-    // This case should never be choosen
-    DBUG_ASSERT(0);
-    return 0;
-  }
+  return value->val_real();
 }
 
 longlong Item_sum_hybrid::val_int()
@@ -1643,18 +1601,7 @@ longlong Item_sum_hybrid::val_int()
   DBUG_ASSERT(fixed == 1);
   if (null_value)
     return 0;
-  switch (hybrid_type) {
-  case INT_RESULT:
-    return sum_int;
-  case DECIMAL_RESULT:
-  {
-    longlong result;
-    my_decimal2int(E_DEC_FATAL_ERROR, &sum_dec, unsigned_flag, &result);
-    return sum_int;
-  }
-  default:
-    return (longlong) rint(Item_sum_hybrid::val_real());
-  }
+  return value->val_int();
 }
 
 
@@ -1663,26 +1610,7 @@ my_decimal *Item_sum_hybrid::val_decimal
   DBUG_ASSERT(fixed == 1);
   if (null_value)
     return 0;
-  switch (hybrid_type) {
-  case STRING_RESULT:
-    string2my_decimal(E_DEC_FATAL_ERROR, &value, val);
-    break;
-  case REAL_RESULT:
-    double2my_decimal(E_DEC_FATAL_ERROR, sum, val);
-    break;
-  case DECIMAL_RESULT:
-    val= &sum_dec;
-    break;
-  case INT_RESULT:
-    int2my_decimal(E_DEC_FATAL_ERROR, sum_int, unsigned_flag, val);
-    break;
-  case ROW_RESULT:
-  default:
-    // This case should never be choosen
-    DBUG_ASSERT(0);
-    break;
-  }
-  return val;					// Keep compiler happy
+  return value->val_decimal(val);
 }
 
 
@@ -1692,25 +1620,7 @@ Item_sum_hybrid::val_str(String *str)
   DBUG_ASSERT(fixed == 1);
   if (null_value)
     return 0;
-  switch (hybrid_type) {
-  case STRING_RESULT:
-    return &value;
-  case REAL_RESULT:
-    str->set_real(sum,decimals, &my_charset_bin);
-    break;
-  case DECIMAL_RESULT:
-    my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str);
-    return str;
-  case INT_RESULT:
-    str->set_int(sum_int, unsigned_flag, &my_charset_bin);
-    break;
-  case ROW_RESULT:
-  default:
-    // This case should never be choosen
-    DBUG_ASSERT(0);
-    break;
-  }
-  return str;					// Keep compiler happy
+  return value->val_str(str);
 }
 
 
@@ -1719,7 +1629,9 @@ void Item_sum_hybrid::cleanup()
   DBUG_ENTER("Item_sum_hybrid::cleanup");
   Item_sum::cleanup();
   forced_const= FALSE;
-
+  if (cmp)
+    delete cmp;
+  cmp= 0;
   /*
     by default it is TRUE to avoid TRUE reporting by
     Item_func_not_all/Item_func_nop_all if this item was never called.
@@ -1740,63 +1652,21 @@ void Item_sum_hybrid::no_rows_in_result(
 
 Item *Item_sum_min::copy_or_same(THD* thd)
 {
-  return new (thd->mem_root) Item_sum_min(thd, this);
+  Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this);
+  item->setup(args[0], value);
+  return item;
 }
 
 
 bool Item_sum_min::add()
 {
-  switch (hybrid_type) {
-  case STRING_RESULT:
+  /* args[0] < value */
+  int res= cmp->compare();
+  if (!args[0]->null_value &&
+      (null_value || res < 0))
   {
-    String *result=args[0]->val_str(&tmp_value);
-    if (!args[0]->null_value &&
-	(null_value || sortcmp(&value,result,collation.collation) > 0))
-    {
-      value.copy(*result);
-      null_value=0;
-    }
-  }
-  break;
-  case INT_RESULT:
-  {
-    longlong nr=args[0]->val_int();
-    if (!args[0]->null_value && (null_value ||
-				 (unsigned_flag && 
-				  (ulonglong) nr < (ulonglong) sum_int) ||
-				 (!unsigned_flag && nr < sum_int)))
-    {
-      sum_int=nr;
-      null_value=0;
-    }
-  }
-  break;
-  case DECIMAL_RESULT:
-  {
-    my_decimal value_buff, *val= args[0]->val_decimal(&value_buff);
-    if (!args[0]->null_value &&
-        (null_value || (my_decimal_cmp(&sum_dec, val) > 0)))
-    {
-      my_decimal2decimal(val, &sum_dec);
-      null_value= 0;
-    }
-  }
-  break;
-  case REAL_RESULT:
-  {
-    double nr= args[0]->val_real();
-    if (!args[0]->null_value && (null_value || nr < sum))
-    {
-      sum=nr;
-      null_value=0;
-    }
-  }
-  break;
-  case ROW_RESULT:
-  default:
-    // This case should never be choosen
-    DBUG_ASSERT(0);
-    break;
+    value->store(args[0]);
+    null_value= 0;
   }
   return 0;
 }
@@ -1804,63 +1674,21 @@ bool Item_sum_min::add()
 
 Item *Item_sum_max::copy_or_same(THD* thd)
 {
-  return new (thd->mem_root) Item_sum_max(thd, this);
+  Item_sum_max *item= new (thd->mem_root) Item_sum_max(thd, this);
+  item->setup(args[0], value);
+  return item;
 }
 
 
 bool Item_sum_max::add()
 {
-  switch (hybrid_type) {
-  case STRING_RESULT:
-  {
-    String *result=args[0]->val_str(&tmp_value);
-    if (!args[0]->null_value &&
-	(null_value || sortcmp(&value,result,collation.collation) < 0))
-    {
-      value.copy(*result);
-      null_value=0;
-    }
-  }
-  break;
-  case INT_RESULT:
-  {
-    longlong nr=args[0]->val_int();
-    if (!args[0]->null_value && (null_value ||
-				 (unsigned_flag && 
-				  (ulonglong) nr > (ulonglong) sum_int) ||
-				 (!unsigned_flag && nr > sum_int)))
-    {
-      sum_int=nr;
-      null_value=0;
-    }
-  }
-  break;
-  case DECIMAL_RESULT:
-  {
-    my_decimal value_buff, *val= args[0]->val_decimal(&value_buff);
-    if (!args[0]->null_value &&
-        (null_value || (my_decimal_cmp(val, &sum_dec) > 0)))
-    {
-      my_decimal2decimal(val, &sum_dec);
-      null_value= 0;
-    }
-  }
-  break;
-  case REAL_RESULT:
+  /* args[0] > value */
+  int res= cmp->compare();
+  if (!args[0]->null_value &&
+      (null_value || res > 0))
   {
-    double nr= args[0]->val_real();
-    if (!args[0]->null_value && (null_value || nr > sum))
-    {
-      sum=nr;
-      null_value=0;
-    }
-  }
-  break;
-  case ROW_RESULT:
-  default:
-    // This case should never be choosen
-    DBUG_ASSERT(0);
-    break;
+    value->store(args[0]);
+    null_value= 0;
   }
   return 0;
 }
@@ -2225,14 +2053,15 @@ void Item_sum_hybrid::update_field()
 void
 Item_sum_hybrid::min_max_update_str_field()
 {
-  String *res_str=args[0]->val_str(&value);
+  DBUG_ASSERT(cmp);
+  String *res_str=args[0]->val_str(&cmp->value1);
 
   if (!args[0]->null_value)
   {
-    result_field->val_str(&tmp_value);
+    result_field->val_str(&cmp->value2);
 
     if (result_field->is_null() ||
-	(cmp_sign * sortcmp(res_str,&tmp_value,collation.collation)) < 0)
+	(cmp_sign * sortcmp(res_str,&cmp->value2,collation.collation)) < 0)
       result_field->store(res_str->ptr(),res_str->length(),res_str->charset());
     result_field->set_notnull();
   }

=== modified file 'sql/item_sum.h'
--- a/sql/item_sum.h	2009-06-11 16:21:32 +0000
+++ b/sql/item_sum.h	2009-11-17 14:06:46 +0000
@@ -323,22 +323,6 @@ public:  
   virtual void update_field()=0;
   virtual bool keep_field_type(void) const { return 0; }
   virtual void fix_length_and_dec() { maybe_null=1; null_value=1; }
-  /*
-    This method is used for debug purposes to print the name of an
-    item to the debug log. The second use of this method is as
-    a helper function of print(), where it is applicable.
-    To suit both goals it should return a meaningful,
-    distinguishable and sintactically correct string.  This method
-    should not be used for runtime type identification, use enum
-    {Sum}Functype and Item_func::functype()/Item_sum::sum_func()
-    instead.
-
-    NOTE: for Items inherited from Item_sum, func_name() return part of
-    function name till first argument (including '(') to make difference in
-    names for functions with 'distinct' clause and without 'distinct' and
-    also to make printing of items inherited from Item_sum uniform.
-  */
-  virtual const char *func_name() const= 0;
   virtual Item *result_item(Field *field)
     { return new Item_field(field); }
   table_map used_tables() const { return used_tables_cache; }
@@ -664,6 +648,7 @@ public:
   }
   void fix_length_and_dec() {}
   enum Item_result result_type () const { return hybrid_type; }
+  const char *func_name() const { DBUG_ASSERT(0); return "avg_field"; }
 };
 
 
@@ -732,6 +717,7 @@ public:
   }
   void fix_length_and_dec() {}
   enum Item_result result_type () const { return hybrid_type; }
+  const char *func_name() const { DBUG_ASSERT(0); return "variance_field"; }
 };
 
 
@@ -807,6 +793,7 @@ public:
   my_decimal *val_decimal(my_decimal *);
   enum Item_result result_type () const { return REAL_RESULT; }
   enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;}
+  const char *func_name() const { DBUG_ASSERT(0); return "std_field"; }
 };
 
 /*
@@ -832,14 +819,13 @@ class Item_sum_std :public Item_sum_vari
 };
 
 // This class is a string or number function depending on num_func
-
+class Arg_comparator;
+class Item_cache;
 class Item_sum_hybrid :public Item_sum
 {
 protected:
-  String value,tmp_value;
-  double sum;
-  longlong sum_int;
-  my_decimal sum_dec;
+  Item_cache *value;
+  Arg_comparator *cmp;
   Item_result hybrid_type;
   enum_field_types hybrid_field_type;
   int cmp_sign;
@@ -847,12 +833,17 @@ protected:
 
   public:
   Item_sum_hybrid(Item *item_par,int sign)
-    :Item_sum(item_par), sum(0.0), sum_int(0),
+    :Item_sum(item_par), value(0), cmp(0),
     hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG),
     cmp_sign(sign), was_values(TRUE)
   { collation.set(&my_charset_bin); }
-  Item_sum_hybrid(THD *thd, Item_sum_hybrid *item);
+  Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
+    :Item_sum(thd, item), value(item->value), hybrid_type(item->hybrid_type),
+    hybrid_field_type(item->hybrid_field_type), cmp_sign(item->cmp_sign),
+    was_values(item->was_values)
+  { }
   bool fix_fields(THD *, Item **);
+  void setup(Item *item, Item *value_arg);
   void clear();
   double val_real();
   longlong val_int();


Attachment: [text/bzr-bundle] bzr/epotemkin@mysql.com-20091117140646-sl7lzgrfedwaqxr2.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (epotemkin:3165) Bug#43668Evgeny Potemkin17 Nov
  • Re: bzr commit into mysql-5.1-bugteam branch (epotemkin:3165)Bug#43668Sergei Golubchik19 Nov