List:Commits« Previous MessageNext Message »
From:eugene Date:June 13 2006 5:09pm
Subject:bk commit into 4.1 tree (evgen:1.2485) BUG#16377
View as plain text  
Below is the list of changes that have just been committed into a local
4.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
  1.2485 06/06/13 19:09:24 evgen@stripped +9 -0
  Fixed bug#16377: result of DATE/TIME functions were compared as strings which
  can lead to a wrong result.
  
  All date/time functions has the STRING result type thus their results are
  compared as strings. The string date representation allows a user to skip 
  some of leading zeros. This can lead to wrong comparison result if a date/time 
  function result is compared to such a string constant.
  
  The idea behind this bug fix is to compare results of date/time functions
  and data/time constants as ints, because that date/time representation is 
  more exact. To achieve this the agg_cmp_type() is changed to take in the
  account that a date/time field or an date/time item should be compared 
  as ints.
  
  This bug fix is partially back ported from 5.0.
  
  The agg_cmp_type() function now accepts THD as one of parameters. 
  In addition, it now checks if a date/time field/function is present in the
  list. If so, it tries to coerce all constants to INT to make date/time
  comparison return correct result. The field for the constant coercion is
  taken from the Item_field or constructed from the Item_func. In latter case
  the constructed field will be freed after conversion of all constant items.
  Otherwise the result is same as before - aggregated with help of the
  item_cmp_type() function.
  
  From the Item_func_between::fix_length_and_dec() function removed the part
  which was converting date/time constants to int if possible. Now this is 
  done by the agg_cmp_type() function.
  
  The new function result_as_longlong() is added to the Item class. 
  It indicates that the item is a date/time item and result of it can be
  compared as int. Such items are date/time fields/functions.
  
  Correct val_int() methods are implemented for classes Item_date_typecast, 
  Item_func_makedate, Item_time_typecast, Item_datetime_typecast. All these
  classes are derived from Item_str_func and Item_str_func::val_int() converts
  its string value to int without regard to the date/time type of these items.
  
  Arg_comparator::set_compare_func() and Arg_comparator::set_cmp_func()
  functions are changed to substitute result type of an item with the INT_RESULT
  if the item is a date/time item and another item is a constant. This is done
  to get a correct result of comparisons like date_time_function() = string_constant.

  mysql-test/r/func_time.result
    1.39 06/06/13 19:08:21 evgen@stripped +41 -0
    Added test case fot bug#16377: result of DATE/TIME functions were compared as strings
which
    can lead to a wrong result.

  mysql-test/t/func_time.test
    1.34 06/06/13 19:08:10 evgen@stripped +20 -0
    Added test case fot bug#16377: result of DATE/TIME functions were compared as strings
which
    can lead to a wrong result.

  sql/item.h
    1.194 06/06/13 19:07:53 evgen@stripped +16 -0
    The new function result_as_longlong() is added to the Item class. 
    It indicates that the item is a date/time item and result of it can be
    compared as int. Such items are date/time fields/functions.

  sql/item_cmpfunc.cc
    1.209 06/06/13 19:04:46 evgen@stripped +122 -32
    Fixed bug#16377: result of DATE/TIME functions were compared as strings which
    can lead to a wrong result.
    The agg_cmp_type() function now accepts THD as one of parameters. 
    In addition, it now checks if a date/time field/function is present in the
    list. If so, it tries to coerce all constants to INT to make date/time
    comparison return correct result. The field for the constant coercion is
    taken from the Item_field or constructed from the Item_func. In latter case
    the constructed field will be freed after conversion of all constant items.
    Otherwise the result is same as before - aggregated with help of the
    item_cmp_type() function.

  sql/field.cc
    1.230 06/06/13 18:26:08 evgen@stripped +11 -5
    Fixed bug#16377: result of DATE/TIME functions were compared as strings which
    can lead to a wrong result.
    Field::set_warning(), Field::set_datetime_warning() now use current_thd to get thd if
table isn't set.

  sql/item_cmpfunc.h
    1.114 06/06/13 18:24:34 evgen@stripped +10 -4
    Fixed bug#16377: result of DATE/TIME functions were compared as strings which
    can lead to a wrong result.
    Arg_comparator::set_compare_func() and Arg_comparator::set_cmp_func()
    functions are changed to substitute result type of an item with the INT_RESULT
    if the item is a date/time item and another item is a constant.

  sql/item_timefunc.cc
    1.101 06/06/13 18:24:14 evgen@stripped +60 -0
    Fixed bug#16377: result of DATE/TIME functions were compared as strings which
    can lead to a wrong result.Correct val_int() methods are implemented for classes
Item_date_typecast, 
    Item_func_makedate, Item_time_typecast, Item_datetime_typecast.

  sql/item_timefunc.h
    1.57 06/06/13 18:23:53 evgen@stripped +12 -0
    Fixed bug#16377: result of DATE/TIME functions were compared as strings which
    can lead to a wrong result.
    The result_as_longlong() function is set to return TRUE for these classes:
    Item_date, Item_date_func, Item_func_curtime, Item_func_sec_to_time,
    Item_date_typecast, Item_time_typecast, Item_datetime_typecast,
    Item_func_makedate.

  mysql-test/r/cast.result
    1.26 06/06/13 18:23:26 evgen@stripped +1 -1
    Fixed wrong test case result after bug fix#16377.

# 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/16377-bug-4.1-opt-mysql

--- 1.229/sql/field.cc	2006-03-28 17:32:37 +04:00
+++ 1.230/sql/field.cc	2006-06-13 18:26:08 +04:00
@@ -6841,7 +6841,11 @@
 bool 
 Field::set_warning(const uint level, const uint code, int cuted_increment)
 {
-  THD *thd= table->in_use;
+  /*
+    If this field was created only for type conversion purposes it
+    will have table == NULL.
+  */
+  THD *thd= table ? table->in_use : current_thd;
   if (thd->count_cuted_fields)
   {
     thd->cuted_fields+= cuted_increment;
@@ -6876,7 +6880,8 @@
                             timestamp_type ts_type, int cuted_increment)
 {
   if (set_warning(level, code, cuted_increment))
-    make_truncated_value_warning(table->in_use, str, str_length, ts_type);
+    make_truncated_value_warning(table ? table->in_use : current_thd,
+                                 str, str_length, ts_type);
 }
 
 
@@ -6905,8 +6910,8 @@
   {
     char str_nr[22];
     char *str_end= longlong10_to_str(nr, str_nr, -10);
-    make_truncated_value_warning(table->in_use, str_nr, str_end - str_nr, 
-                                 ts_type);
+    make_truncated_value_warning(table ? table->in_use : current_thd,
+                                 str_nr, str_end - str_nr, ts_type);
   }
 }
 
@@ -6935,7 +6940,8 @@
     /* DBL_DIG is enough to print '-[digits].E+###' */
     char str_nr[DBL_DIG + 8];
     uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr));
-    make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type);
+    make_truncated_value_warning(table ? table->in_use : current_thd,
+                                 str_nr, str_len, ts_type);
   }
 }
 

--- 1.193/sql/item.h	2006-04-28 13:23:28 +04:00
+++ 1.194/sql/item.h	2006-06-13 19:07:53 +04:00
@@ -327,6 +327,14 @@
     cleanup();
     delete this;
   }
+  /*
+    result_as_longlong() must return TRUE for Items representing DATE/TIME
+    functions and DATE/TIME table fields.
+    Those Items have result_type()==STRING_RESULT (and not INT_RESULT), but
+    their values should be compared as integers (because the integer
+    representation is more precise than the string one).
+  */
+  virtual bool result_as_longlong() { return FALSE; }
 };
 
 
@@ -450,6 +458,10 @@
   Item *get_tmp_table_item(THD *thd);
   void cleanup();
   inline uint32 max_disp_length() { return field->max_length(); }
+  bool result_as_longlong()
+  {
+    return field->can_be_compared_as_longlong();
+  }
   friend class Item_default_value;
   friend class Item_insert_value;
   friend class st_select_lex_unit;
@@ -973,6 +985,10 @@
   }
   Item *real_item() { return *ref; }
   void print(String *str);
+  bool result_as_longlong()
+  {
+    return (*ref)->result_as_longlong();
+  }
 };
 
 

--- 1.208/sql/item_cmpfunc.cc	2006-05-30 00:35:04 +04:00
+++ 1.209/sql/item_cmpfunc.cc	2006-06-13 19:04:46 +04:00
@@ -25,6 +25,8 @@
 #include <m_ctype.h>
 #include "sql_select.h"
 
+static bool convert_constant_item(THD *thd, Field *field, Item **item);
+
 static Item_result item_store_type(Item_result a,Item_result b)
 {
   if (a == STRING_RESULT || b == STRING_RESULT)
@@ -64,6 +66,7 @@
 
   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
@@ -78,24 +81,133 @@
     If all items are constants the type will be aggregated from all items.
     If there are some non-constant items then only types of non-constant
     items will be used for aggregation.
+    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 orginal
+      result type of any 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.
+
+    Implementation notes:
+      The code is equvalent to:
+      1. Check the list for presense of a STRING field/function.
+         Collect the is_const flag.
+      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 presense of a STRING field/function.
+      The second loop works only if a DATE/TIME field/function is found.
+      It checks presense of a STRING field/function in the rest of the list.
+
+  TODO
+    1) The current implementation can produce false 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
+    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.
 */
-static void agg_cmp_type(Item_result *type, Item **items, uint nitems)
+
+static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems)
 {
   uint i;
-  type[0]= items[0]->result_type();
+  Item::Type res;
+  char *buff= NULL;
+  uchar null_byte;
+  Field *field= NULL;
+
+  /* Search for date/time fields/functions */
+  for (i= 0; i < nitems; i++)
+  {
+    if (!items[i]->result_as_longlong())
+    {
+      /* Do not convert anything if a string field/function is present */
+      if (!items[i]->const_item() && items[i]->result_type() ==
STRING_RESULT)
+      {
+        i= nitems;
+        break;
+      }
+      continue;
+    }
+    if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM)
+    {
+      field= ((Item_field *)items[i]->real_item())->field;
+      break;
+    }
+    else if (res == Item::FUNC_ITEM)
+    {
+      field= items[i]->tmp_table_field_from_field_type(0);
+      if (field)
+        buff= alloc_root(thd->mem_root, field->max_length());
+      if (!buff || !field)
+      {
+        if (field)
+          delete field;
+        if (buff)
+          my_free(buff, MYF(MY_WME));
+        field= 0;
+      }
+      else
+        field->move_field(buff, &null_byte, 0);
+      break;
+    }
+  }
+  if (field)
+  {
+    /* Check the rest of the list for presense of a string field/function. */
+    for (i++ ; i < nitems; i++)
+    {
+      if (!items[i]->const_item() && items[i]->result_type() ==
STRING_RESULT &&
+          !items[i]->result_as_longlong())
+      {
+        field= 0;
+        break;
+      }
+    }
+  }
   /* Reset to 0 on first occurence of non-const item. 1 otherwise */
   bool is_const= items[0]->const_item();
+  /*
+    If the first item is a date/time function then its result should be
+    compared as int
+  */
+  if (field)
+  {
+    /* Suppose we are comparing dates and some non-constant items are present. */
+    type[0]= INT_RESULT;
+    is_const= 0;
+  }
+  else
+    type[0]= items[0]->result_type();
 
-  for (i= 1 ; i < nitems ; i++)
+  for (i= 0; i < nitems ; i++)
   {
     if (!items[i]->const_item())
     {
-      type[0]= is_const ? items[i]->result_type() :
-                 item_cmp_type(type[0], items[i]->result_type());
+      Item_result result= field && items[i]->result_as_longlong() ?
+                            INT_RESULT : items[i]->result_type();
+      type[0]= is_const ? result : item_cmp_type(type[0], result);
       is_const= 0;
     }
     else if (is_const)
       type[0]= item_cmp_type(type[0], items[i]->result_type());
+    else if (field)
+      convert_constant_item(thd, field, &items[i]);
+  }
+
+  if (res == Item::FUNC_ITEM && field)
+  {
+    delete field;
+    my_free(buff, MYF(MY_WME));
   }
 }
 
@@ -929,31 +1041,9 @@
   */
   if (!args[0] || !args[1] || !args[2])
     return;
-  agg_cmp_type(&cmp_type, args, 3);
-  if (cmp_type == STRING_RESULT &&
-      agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV))
-    return;
-
-  /*
-    Make a special case of compare with date/time and longlong fields.
-    They are compared as integers, so for const item this time-consuming
-    conversion can be done only once, not for every single comparison
-  */
-  if (args[0]->type() == FIELD_ITEM)
-  {
-    Field *field=((Item_field*) args[0])->field;
-    if (field->can_be_compared_as_longlong())
-    {
-      /*
-        The following can't be recoded with || as convert_constant_item
-        changes the argument
-      */
-      if (convert_constant_item(thd, field,&args[1]))
-	cmp_type=INT_RESULT;			// Works for all types.
-      if (convert_constant_item(thd, field,&args[2]))
-	cmp_type=INT_RESULT;			// Works for all types.
-    }
-  }
+  agg_cmp_type(thd, &cmp_type, args, 3);
+  if (cmp_type == STRING_RESULT)
+      agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV);
 }
 
 
@@ -1477,7 +1567,7 @@
     for (nagg= 0; nagg < ncases/2 ; nagg++)
       agg[nagg+1]= args[nagg*2];
     nagg++;
-    agg_cmp_type(&cmp_type, agg, nagg);
+    agg_cmp_type(current_thd, &cmp_type, agg, nagg);
     if ((cmp_type == STRING_RESULT) &&
         agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV))
     return;
@@ -1958,7 +2048,7 @@
   uint const_itm= 1;
   THD *thd= current_thd;
   
-  agg_cmp_type(&cmp_type, args, arg_count);
+  agg_cmp_type(thd, &cmp_type, args, arg_count);
 
   if (cmp_type == STRING_RESULT &&
       agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV))

--- 1.113/sql/item_cmpfunc.h	2006-01-14 04:55:01 +03:00
+++ 1.114/sql/item_cmpfunc.h	2006-06-13 18:24:34 +04:00
@@ -43,8 +43,11 @@
   int set_compare_func(Item_bool_func2 *owner, Item_result type);
   inline int set_compare_func(Item_bool_func2 *owner_arg)
   {
-    return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
-						     (*b)->result_type()));
+    Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ?
+                      INT_RESULT : (*a)->result_type();
+    Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ?
+                      INT_RESULT : (*b)->result_type();
+    return set_compare_func(owner_arg, item_cmp_type(ar, br));
   }
   inline int set_cmp_func(Item_bool_func2 *owner_arg,
 			  Item **a1, Item **a2,
@@ -57,8 +60,11 @@
   inline int set_cmp_func(Item_bool_func2 *owner_arg,
 			  Item **a1, Item **a2)
   {
-    return set_cmp_func(owner_arg, a1, a2, item_cmp_type((*a1)->result_type(),
-							 (*a2)->result_type()));
+    Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ?
+                      INT_RESULT : (*a1)->result_type();
+    Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ?
+                      INT_RESULT : (*a2)->result_type();
+    return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br));
   }
   inline int compare() { return (this->*func)(); }
 

--- 1.100/sql/item_timefunc.cc	2006-05-10 12:27:43 +04:00
+++ 1.101/sql/item_timefunc.cc	2006-06-13 18:24:14 +04:00
@@ -2306,6 +2306,20 @@
 }
 
 
+longlong Item_datetime_typecast::val_int()
+{
+  DBUG_ASSERT(fixed == 1);
+  TIME ltime;
+  if (get_arg0_date(&ltime,1))
+  {
+    null_value= 1;
+    return 0;
+  }
+
+  return TIME_to_ulonglong_datetime(&ltime);
+}
+
+
 bool Item_time_typecast::get_time(TIME *ltime)
 {
   bool res= get_arg0_time(ltime);
@@ -2320,6 +2334,17 @@
 }
 
 
+longlong Item_time_typecast::val_int()
+{
+  TIME ltime;
+  if (get_time(&ltime))
+  {
+    null_value= 1;
+    return 0;
+  }
+  return ltime.hour * 10000L + ltime.minute * 100 + ltime.second;
+}
+
 String *Item_time_typecast::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
@@ -2359,6 +2384,14 @@
   return 0;
 }
 
+longlong Item_date_typecast::val_int()
+{
+  DBUG_ASSERT(fixed == 1);
+  TIME ltime;
+  if (args[0]->get_date(&ltime, TIME_FUZZY_DATE))
+    return 0;
+  return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day);
+}
 
 /*
   MAKEDATE(a,b) is a date function that creates a date value 
@@ -2391,6 +2424,33 @@
 
 err:
   null_value=1;
+  return 0;
+}
+
+
+longlong Item_func_makedate::val_int()
+{
+  DBUG_ASSERT(fixed == 1);
+  TIME l_time;
+  long daynr=  (long) args[1]->val_int();
+  long yearnr= (long) args[0]->val_int();
+  long days;
+
+  if (args[0]->null_value || args[1]->null_value ||
+      yearnr < 0 || daynr <= 0)
+    goto err;
+
+  days= calc_daynr(yearnr,1,1) + daynr - 1;
+  /* Day number from year 0 to 9999-12-31 */
+  if (days >= 0 && days < MAX_DAY_NUMBER)
+  {
+    null_value=0;
+    get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day);
+    return (longlong) (l_time.year * 10000L + l_time.month * 100 + l_time.day);
+  }
+
+err:
+  null_value= 1;
   return 0;
 }
 

--- 1.56/sql/item_timefunc.h	2006-04-13 09:55:41 +04:00
+++ 1.57/sql/item_timefunc.h	2006-06-13 18:23:53 +04:00
@@ -339,6 +339,7 @@
   {
     return (new Field_date(maybe_null, name, t_arg, &my_charset_bin));
   }  
+  bool result_as_longlong() { return TRUE; }
 };
 
 
@@ -354,6 +355,7 @@
   {
     return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
   }
+  bool result_as_longlong() { return TRUE; }
 };
 
 
@@ -383,6 +385,7 @@
     TIME representation using UTC-SYSTEM or per-thread time zone.
   */
   virtual void store_now_in_TIME(TIME *now_time)=0;
+  bool result_as_longlong() { return TRUE; }
 };
 
 
@@ -588,6 +591,7 @@
   {
     return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
   }
+  bool result_as_longlong() { return TRUE; }
 };
 
 /*
@@ -715,6 +719,8 @@
     max_length= 10;
     maybe_null= 1;
   }
+  bool result_as_longlong() { return TRUE; }
+  longlong val_int();
 };
 
 
@@ -731,6 +737,8 @@
   {
     return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
   }
+  bool result_as_longlong() { return TRUE; }
+  longlong val_int();
 };
 
 
@@ -746,6 +754,8 @@
   {
     return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
   }
+  bool result_as_longlong() { return TRUE; }
+  longlong val_int();
 };
 
 class Item_func_makedate :public Item_str_func
@@ -764,6 +774,8 @@
   {
     return (new Field_date(maybe_null, name, t_arg, &my_charset_bin));
   }
+  bool result_as_longlong() { return TRUE; }
+  longlong val_int();
 };
 
 

--- 1.38/mysql-test/r/func_time.result	2006-04-25 13:34:13 +04:00
+++ 1.39/mysql-test/r/func_time.result	2006-06-13 19:08:21 +04:00
@@ -630,3 +630,44 @@
 monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m'));
 monthname(str_to_date(null, '%m'))	monthname(str_to_date(null,
'%m'))	monthname(str_to_date(1, '%m'))	monthname(str_to_date(0, '%m'))
 NULL	NULL	January	NULL
+create table t1(f1 date, f2 time, f3 datetime);
+insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01");
+insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02");
+select f1 from t1 where f1 between "2006-1-1" and 20060101;
+f1
+2006-01-01
+select f1 from t1 where f1 between "2006-1-1" and "2006.1.1";
+f1
+2006-01-01
+select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1";
+f1
+2006-01-01
+select f2 from t1 where f2 between "12:1:2" and "12:2:2";
+f2
+12:01:02
+select f2 from t1 where time(f2) between "12:1:2" and "12:2:2";
+f2
+12:01:02
+select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2";
+f3
+2006-01-01 12:01:01
+select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2";
+f3
+2006-01-01 12:01:01
+select f1 from t1 where "2006-1-1" between f1 and f3;
+f1
+2006-01-01
+select f1 from t1 where "2006-1-1" between date(f1) and date(f3);
+f1
+2006-01-01
+select f1 from t1 where "2006-1-1" between f1 and 'zzz';
+f1
+Warnings:
+Warning	1292	Truncated incorrect date value: 'zzz'
+select f1 from t1 where makedate(2006,1) between date(f1) and date(f3);
+f1
+2006-01-01
+select f1 from t1 where makedate(2006,2) between date(f1) and date(f3);
+f1
+2006-01-02
+drop table t1;

--- 1.33/mysql-test/t/func_time.test	2006-04-25 13:34:13 +04:00
+++ 1.34/mysql-test/t/func_time.test	2006-06-13 19:08:10 +04:00
@@ -322,4 +322,24 @@
 select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')),
        monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m'));
 
+#
+# Bug#16377 result of DATE/TIME functions were compared as strings which
+#           can lead to a wrong result.
+#
+create table t1(f1 date, f2 time, f3 datetime);
+insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01");
+insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02");
+select f1 from t1 where f1 between "2006-1-1" and 20060101;
+select f1 from t1 where f1 between "2006-1-1" and "2006.1.1";
+select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1";
+select f2 from t1 where f2 between "12:1:2" and "12:2:2";
+select f2 from t1 where time(f2) between "12:1:2" and "12:2:2";
+select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2";
+select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2";
+select f1 from t1 where "2006-1-1" between f1 and f3;
+select f1 from t1 where "2006-1-1" between date(f1) and date(f3);
+select f1 from t1 where "2006-1-1" between f1 and 'zzz';
+select f1 from t1 where makedate(2006,1) between date(f1) and date(f3);
+select f1 from t1 where makedate(2006,2) between date(f1) and date(f3);
+drop table t1;
 # End of 4.1 tests

--- 1.25/mysql-test/r/cast.result	2005-12-06 15:51:12 +03:00
+++ 1.26/mysql-test/r/cast.result	2006-06-13 18:23:26 +04:00
@@ -192,7 +192,7 @@
 1
 select cast("1:2:3" as TIME) = "1:02:03";
 cast("1:2:3" as TIME) = "1:02:03"
-0
+1
 select cast(NULL as DATE);
 cast(NULL as DATE)
 NULL
Thread
bk commit into 4.1 tree (evgen:1.2485) BUG#16377eugene13 Jun