List:Commits« Previous MessageNext Message »
From:Martin Hansson Date:November 2 2009 12:24pm
Subject:bzr commit into mysql-5.1-bugteam branch (martin.hansson:3161) Bug#47925
View as plain text  
#At file:///data0/martin/bzr/bug47925/5.1bt/ based on revid:davi.arnaut@stripped

 3161 Martin Hansson	2009-11-02
      Bug#47925: regression of range optimizer and date comparison in 5.1.39!
      
      When a query was using a DATE or DATETIME value formatted
      using any other separator characters beside hyphen '-', a
      query with a greater-or-equal '>=' condition matching only
      the greatest value in an indexed column, the result was
      empty if index range scan was employed.
      
      The range optimizer got a new feature between 5.1.38 and
      5.1.39 that changes a greater-or-equal condition to a
      greater-than if the value matching that in the query was not
      present in the table. But the value comparison function
      compared the dates as strings instead of dates.
      
      The bug was fixed by splitting the function
      get_date_from_str in two: One part that parses and does
      error checking. This function is now visible outside the
      module. The old get_date_from_str now calls the new
      function.
     @ mysql-test/r/range.result
        Bug#47925: Test result
     @ mysql-test/t/range.test
        Bug#47925: Test case
     @ sql/item.cc
        Bug#47925: Fix + some edit on the comments
     @ sql/item.h
        Bug#47925: Changed function signature
     @ sql/item_cmpfunc.cc
        Bug#47925: Split function in two
     @ sql/item_cmpfunc.h
        Bug#47925: Declaration of new function
     @ sql/opt_range.cc
        Bug#47925: Added THD to function call
     @ sql/time.cc
        Bug#47925: Added microsecond comparison

    modified:
      mysql-test/r/range.result
      mysql-test/t/range.test
      sql/item.cc
      sql/item.h
      sql/item_cmpfunc.cc
      sql/item_cmpfunc.h
      sql/opt_range.cc
      sql/time.cc
=== modified file 'mysql-test/r/range.result'
--- a/mysql-test/r/range.result	2009-10-16 20:19:51 +0000
+++ b/mysql-test/r/range.result	2009-11-02 12:24:07 +0000
@@ -1406,4 +1406,201 @@ INSERT INTO t1 VALUES (1), (NULL);
 SELECT * FROM t1 WHERE a <> NULL and (a <> NULL or a <= NULL);
 a
 DROP TABLE t1;
+#
+# Bug#47925: regression of range optimizer and date comparison in 5.1.39!
+#
+CREATE TABLE t1 ( a DATE,     KEY ( a ) );
+CREATE TABLE t2 ( a DATETIME, KEY ( a ) );
+# Make optimizer choose range scan
+INSERT INTO t1 VALUES ('2009-09-22'), ('2009-09-22'), ('2009-09-22');
+INSERT INTO t1 VALUES ('2009-09-23'), ('2009-09-23'), ('2009-09-23');
+INSERT INTO t2 VALUES ('2009-09-22 12:00:00'), ('2009-09-22 12:00:00'),
+('2009-09-22 12:00:00');
+INSERT INTO t2 VALUES ('2009-09-23 12:00:00'), ('2009-09-23 12:00:00'),
+('2009-09-23 12:00:00');
+# DATE vs DATE
+EXPLAIN
+SELECT * FROM t1 WHERE a >= '2009/09/23';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+X	X	X	range	a	a	X	X	X	X
+SELECT * FROM t1 WHERE a >= '2009/09/23';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '20090923';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >=  20090923;
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009-9-23';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009.09.23';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009:09:23';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+# DATE vs DATETIME
+EXPLAIN
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+X	X	X	range	a	a	X	X	X	X
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '20090923';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >=  20090923;
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009-9-23';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009.09.23';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009:09:23';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+# DATETIME vs DATETIME
+EXPLAIN
+SELECT * FROM t2 WHERE a >= '2009/09/23 12:00:00';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+X	X	X	range	a	a	X	X	X	X
+SELECT * FROM t2 WHERE a >= '2009/09/23 12:00:00';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '20090923120000';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >=  20090923120000;
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009-9-23 12:00:00';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009.09.23 12:00:00';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+SELECT * FROM t2 WHERE a >= '2009:09:23 12:00:00';
+a
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+2009-09-23 12:00:00
+# DATETIME vs DATE
+EXPLAIN
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+X	X	X	range	a	a	X	X	X	X
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '20090923000000';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >=  20090923000000;
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009-9-23 00:00:00';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009.09.23 00:00:00';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+SELECT * FROM t1 WHERE a >= '2009:09:23 00:00:00';
+a
+2009-09-23
+2009-09-23
+2009-09-23
+# Test of the new get_date_from_str implementation
+# Behavior differs slightly between the trunk and mysql-pe.
+# The former may give errors for the truncated values, while the latter
+# gives warnings. The purpose of this test is not to interfere, and only
+# preserve existing behavior.
+SELECT str_to_date('2007-10-00', '%Y-%m-%d') >= '' AND 
+str_to_date('2007-10-00', '%Y-%m-%d') <= '2007/10/20';
+str_to_date('2007-10-00', '%Y-%m-%d') >= '' AND 
+str_to_date('2007-10-00', '%Y-%m-%d') <= '2007/10/20'
+1
+Warnings:
+Warning	1292	Truncated incorrect date value: ''
+SELECT str_to_date('2007-20-00', '%Y-%m-%d') >= '2007/10/20' AND 
+str_to_date('2007-20-00', '%Y-%m-%d') <= '';
+str_to_date('2007-20-00', '%Y-%m-%d') >= '2007/10/20' AND 
+str_to_date('2007-20-00', '%Y-%m-%d') <= ''
+NULL
+Warnings:
+Warning	1292	Truncated incorrect date value: ''
+Error	1411	Incorrect datetime value: '2007-20-00' for function str_to_date
+Error	1411	Incorrect datetime value: '2007-20-00' for function str_to_date
+SELECT str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20';
+str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20'
+1
+Warnings:
+Warning	1292	Truncated incorrect datetime value: ''
+SELECT str_to_date('2007-20-00', '%Y-%m-%d') BETWEEN '2007/10/20' AND '';
+str_to_date('2007-20-00', '%Y-%m-%d') BETWEEN '2007/10/20' AND ''
+NULL
+Warnings:
+Error	1411	Incorrect datetime value: '2007-20-00' for function str_to_date
+SELECT str_to_date('', '%Y-%m-%d');
+str_to_date('', '%Y-%m-%d')
+0000-00-00
+DROP TABLE t1, t2;
 End of 5.1 tests

=== modified file 'mysql-test/t/range.test'
--- a/mysql-test/t/range.test	2009-10-16 20:19:51 +0000
+++ b/mysql-test/t/range.test	2009-11-02 12:24:07 +0000
@@ -1181,4 +1181,83 @@ INSERT INTO t1 VALUES (1), (NULL);
 SELECT * FROM t1 WHERE a <> NULL and (a <> NULL or a <= NULL);
 DROP TABLE t1;
 
+--echo #
+--echo # Bug#47925: regression of range optimizer and date comparison in 5.1.39!
+--echo #
+CREATE TABLE t1 ( a DATE,     KEY ( a ) );
+CREATE TABLE t2 ( a DATETIME, KEY ( a ) );
+
+--echo # Make optimizer choose range scan
+INSERT INTO t1 VALUES ('2009-09-22'), ('2009-09-22'), ('2009-09-22');
+INSERT INTO t1 VALUES ('2009-09-23'), ('2009-09-23'), ('2009-09-23');
+
+INSERT INTO t2 VALUES ('2009-09-22 12:00:00'), ('2009-09-22 12:00:00'),
+                      ('2009-09-22 12:00:00');
+INSERT INTO t2 VALUES ('2009-09-23 12:00:00'), ('2009-09-23 12:00:00'),
+                      ('2009-09-23 12:00:00');
+
+--echo # DATE vs DATE
+--replace_column 1 X 2 X 3 X 7 X 8 X 9 X 10 X
+EXPLAIN
+SELECT * FROM t1 WHERE a >= '2009/09/23';
+SELECT * FROM t1 WHERE a >= '2009/09/23';
+SELECT * FROM t1 WHERE a >= '20090923';
+SELECT * FROM t1 WHERE a >=  20090923;
+SELECT * FROM t1 WHERE a >= '2009-9-23';
+SELECT * FROM t1 WHERE a >= '2009.09.23';
+SELECT * FROM t1 WHERE a >= '2009:09:23';
+
+--echo # DATE vs DATETIME
+--replace_column 1 X 2 X 3 X 7 X 8 X 9 X 10 X
+EXPLAIN
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+SELECT * FROM t2 WHERE a >= '2009/09/23';
+SELECT * FROM t2 WHERE a >= '20090923';
+SELECT * FROM t2 WHERE a >=  20090923;
+SELECT * FROM t2 WHERE a >= '2009-9-23';
+SELECT * FROM t2 WHERE a >= '2009.09.23';
+SELECT * FROM t2 WHERE a >= '2009:09:23';
+
+--echo # DATETIME vs DATETIME
+--replace_column 1 X 2 X 3 X 7 X 8 X 9 X 10 X
+EXPLAIN
+SELECT * FROM t2 WHERE a >= '2009/09/23 12:00:00';
+SELECT * FROM t2 WHERE a >= '2009/09/23 12:00:00';
+SELECT * FROM t2 WHERE a >= '20090923120000';
+SELECT * FROM t2 WHERE a >=  20090923120000;
+SELECT * FROM t2 WHERE a >= '2009-9-23 12:00:00';
+SELECT * FROM t2 WHERE a >= '2009.09.23 12:00:00';
+SELECT * FROM t2 WHERE a >= '2009:09:23 12:00:00';
+
+--echo # DATETIME vs DATE
+--replace_column 1 X 2 X 3 X 7 X 8 X 9 X 10 X
+EXPLAIN
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+SELECT * FROM t1 WHERE a >= '2009/09/23 00:00:00';
+SELECT * FROM t1 WHERE a >= '20090923000000';
+SELECT * FROM t1 WHERE a >=  20090923000000;
+SELECT * FROM t1 WHERE a >= '2009-9-23 00:00:00';
+SELECT * FROM t1 WHERE a >= '2009.09.23 00:00:00';
+SELECT * FROM t1 WHERE a >= '2009:09:23 00:00:00';
+
+--echo # Test of the new get_date_from_str implementation
+--echo # Behavior differs slightly between the trunk and mysql-pe.
+--echo # The former may give errors for the truncated values, while the latter
+--echo # gives warnings. The purpose of this test is not to interfere, and only
+--echo # preserve existing behavior.
+SELECT str_to_date('2007-10-00', '%Y-%m-%d') >= '' AND 
+       str_to_date('2007-10-00', '%Y-%m-%d') <= '2007/10/20';
+
+SELECT str_to_date('2007-20-00', '%Y-%m-%d') >= '2007/10/20' AND 
+       str_to_date('2007-20-00', '%Y-%m-%d') <= '';
+
+SELECT str_to_date('2007-10-00', '%Y-%m-%d') BETWEEN '' AND '2007/10/20';
+SELECT str_to_date('2007-20-00', '%Y-%m-%d') BETWEEN '2007/10/20' AND '';
+
+SELECT str_to_date('', '%Y-%m-%d');
+
+DROP TABLE t1, t2;
+
 --echo End of 5.1 tests

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-10-13 04:43:27 +0000
+++ b/sql/item.cc	2009-11-02 12:24:07 +0000
@@ -6866,72 +6866,61 @@ void resolve_const_item(THD *thd, Item *
 }
 
 /**
-  Compare the value stored in field, with the original item.
+  Compare the value stored in field with the expression from the query.
 
-  @param field   field which the item is converted and stored in
-  @param item    original item
+  @param field   Field which the Item is stored in after conversion
+  @param item    Original expression from query
 
-  @return Return an integer greater than, equal to, or less than 0 if
-          the value stored in the field is greater than,  equal to,
-          or less than the original item
+  @return Returns an integer greater than, equal to, or less than 0 if
+          the value stored in the field is greater than, equal to,
+          or less than the original Item. A 0 may also be returned if 
+          out of memory.          
 
   @note We only use this on the range optimizer/partition pruning,
         because in some cases we can't store the value in the field
         without some precision/character loss.
 */
 
-int stored_field_cmp_to_item(Field *field, Item *item)
+int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
 {
-
   Item_result res_type=item_cmp_type(field->result_type(),
 				     item->result_type());
   if (res_type == STRING_RESULT)
   {
     char item_buff[MAX_FIELD_WIDTH];
     char field_buff[MAX_FIELD_WIDTH];
-    String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result;
+    
+    String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin);
     String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin);
-    enum_field_types field_type;
-    item_result=item->val_str(&item_tmp);
+    String *item_result= item->val_str(&item_tmp);
+    /*
+      Some implementations of Item::val_str(String*) actually modify
+      the field Item::null_value, hence we can't check it earlier.
+    */
     if (item->null_value)
       return 0;
-    field->val_str(&field_tmp);
+    String *field_result= field->val_str(&field_tmp);
 
-    /*
-      If comparing DATE with DATETIME, append the time-part to the DATE.
-      So that the strings are equally formatted.
-      A DATE converted to string is 10 (MAX_DATE_WIDTH) characters, 
-      and a DATETIME converted to string is 19 (MAX_DATETIME_WIDTH) characters.
-    */
-    field_type= field->type();
-    uint32 item_length= item_result->length();
-    if (field_type == MYSQL_TYPE_DATE &&
-        item_length == MAX_DATETIME_WIDTH)
-      field_tmp.append(" 00:00:00");
-    else if (field_type == MYSQL_TYPE_DATETIME)
+    enum_field_types field_type= field->type();
+
+    if (field_type == MYSQL_TYPE_DATE || field_type == MYSQL_TYPE_DATETIME)
     {
-      if (item_length == MAX_DATE_WIDTH)
-        item_result->append(" 00:00:00");
-      else if (item_length > MAX_DATETIME_WIDTH)
-      {
-        /*
-          We don't store microsecond part of DATETIME in field
-          but item_result contains it. As we compare DATETIMEs as strings
-          we must trim trailing 0's in item_result's microsecond part
-          to ensure "YYYY-MM-DD HH:MM:SS" == "YYYY-MM-DD HH:MM:SS.0000"
-        */
-        char *end= (char *) item_result->ptr() + item_length - 1;
-        /* Trim trailing 0's */
-        while (*end == '0')
-          end--;
-        /* Trim '.' if no microseconds */
-        if (*end == '.')
-          end--;
-        DBUG_ASSERT(end - item_result->ptr() + 1 >= MAX_DATETIME_WIDTH);
-        item_result->length(end - item_result->ptr() + 1);
-      }
+      enum_mysql_timestamp_type type= MYSQL_TIMESTAMP_ERROR;
+
+      if (field_type == MYSQL_TYPE_DATE)
+        type= MYSQL_TIMESTAMP_DATE;
+
+      if (field_type == MYSQL_TYPE_DATETIME)
+        type= MYSQL_TIMESTAMP_DATETIME;
+        
+      const char *field_name= field->field_name;
+      MYSQL_TIME field_time, item_time;
+      get_mysql_time_from_str(thd, field_result, type, field_name, &field_time);
+      get_mysql_time_from_str(thd, item_result, type, field_name,  &item_time);
+
+      return my_time_compare(&field_time, &item_time);
     }
-    return stringcmp(&field_tmp,item_result);
+    return stringcmp(field_result, item_result);
   }
   if (res_type == INT_RESULT)
     return 0;					// Both are of type int

=== modified file 'sql/item.h'
--- a/sql/item.h	2009-08-28 10:55:59 +0000
+++ b/sql/item.h	2009-11-02 12:24:07 +0000
@@ -3125,4 +3125,4 @@ void mark_select_range_as_dependent(THD 
 extern Cached_item *new_Cached_item(THD *thd, Item *item);
 extern Item_result item_cmp_type(Item_result a,Item_result b);
 extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item);
-extern int stored_field_cmp_to_item(Field *field, Item *item);
+extern int stored_field_cmp_to_item(THD *thd, Field *field, Item *item);

=== 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-02 12:24:07 +0000
@@ -636,56 +636,51 @@ int Arg_comparator::set_compare_func(Ite
   return 0;
 }
 
-
 /**
-  @brief Convert date provided in a string to the int representation.
+  Parse date provided in a string to a MYSQL_TIME.
 
-  @param[in]   thd        thread handle
-  @param[in]   str        a string to convert
-  @param[in]   warn_type  type of the timestamp for issuing the warning
-  @param[in]   warn_name  field name for issuing the warning
-  @param[out]  error_arg  could not extract a DATE or DATETIME
-
-  @details Convert date provided in the string str to the int
-    representation.  If the string contains wrong date or doesn't
-    contain it at all then a warning is issued.  The warn_type and
-    the warn_name arguments are used as the name and the type of the
-    field when issuing the warning.  If any input was discarded
-    (trailing or non-timestampy characters), was_cut will be non-zero.
-    was_type will return the type str_to_datetime() could correctly
-    extract.
-
-  @return
-    converted value. 0 on error and on zero-dates -- check 'failure'
+  @param[in]   thd        Thread handle
+  @param[in]   str        A string to convert
+  @param[in]   warn_type  Type of the timestamp for issuing the warning
+  @param[in]   warn_name  Field name for issuing the warning
+  @param[out]  l_time     The MYSQL_TIME objects is initialized.
+
+  Parses a date provided in the string str into a MYSQL_TIME object. If the
+  string contains an incorrect date or doesn't correspond to a date at all
+  then a warning is issued. The warn_type and the warn_name arguments are used
+  as the name and the type of the field when issuing the warning. If any input
+  was discarded (trailing or non-timestamp-y characters), return value will be
+  TRUE.
+
+  @return Status flag
+  @retval FALSE Success.
+  @retval True Indicates failure.
 */
 
-static ulonglong
-get_date_from_str(THD *thd, String *str, timestamp_type warn_type,
-                  char *warn_name, bool *error_arg)
+bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, 
+                             const char *warn_name, MYSQL_TIME *l_time)
 {
-  ulonglong value= 0;
+  bool value;
   int error;
-  MYSQL_TIME l_time;
-  enum_mysql_timestamp_type ret;
+  enum_mysql_timestamp_type timestamp_type;
 
-  ret= str_to_datetime(str->ptr(), str->length(), &l_time,
-                       (TIME_FUZZY_DATE | MODE_INVALID_DATES |
-                        (thd->variables.sql_mode &
-                         (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))),
-                       &error);
+  timestamp_type= 
+    str_to_datetime(str->ptr(), str->length(), l_time,
+                    (TIME_FUZZY_DATE | MODE_INVALID_DATES |
+                     (thd->variables.sql_mode &
+                      (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))),
+                    &error);
 
-  if (ret == MYSQL_TIMESTAMP_DATETIME || ret == MYSQL_TIMESTAMP_DATE)
-  {
+  if (timestamp_type == MYSQL_TIMESTAMP_DATETIME || 
+      timestamp_type == MYSQL_TIMESTAMP_DATE)
     /*
       Do not return yet, we may still want to throw a "trailing garbage"
       warning.
     */
-    *error_arg= FALSE;
-    value= TIME_to_ulonglong_datetime(&l_time);
-  }
+    value= FALSE;
   else
   {
-    *error_arg= TRUE;
+    value= TRUE;
     error= 1;                                   /* force warning */
   }
 
@@ -698,6 +693,37 @@ get_date_from_str(THD *thd, String *str,
 }
 
 
+/**
+  @brief Convert date provided in a string to the int representation.
+
+  @param[in]   thd        thread handle
+  @param[in]   str        a string to convert
+  @param[in]   warn_type  type of the timestamp for issuing the warning
+  @param[in]   warn_name  field name for issuing the warning
+  @param[out]  error_arg  could not extract a DATE or DATETIME
+
+  @details Convert date provided in the string str to the int
+    representation.  If the string contains wrong date or doesn't
+    contain it at all then a warning is issued.  The warn_type and
+    the warn_name arguments are used as the name and the type of the
+    field when issuing the warning.
+
+  @return
+    converted value. 0 on error and on zero-dates -- check 'failure'
+*/
+static ulonglong get_date_from_str(THD *thd, String *str, 
+                                   timestamp_type warn_type, 
+                                   const char *warn_name, bool *error_arg)
+{
+  MYSQL_TIME l_time;
+  *error_arg= get_mysql_time_from_str(thd, str, warn_type, warn_name, &l_time);
+
+  if (*error_arg)
+    return 0;
+  return TIME_to_ulonglong_datetime(&l_time);
+}
+
+
 /*
   Check whether compare_datetime() can be used to compare items.
 

=== 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-02 12:24:07 +0000
@@ -1721,3 +1721,6 @@ inline Item *and_conds(Item *a, Item *b)
 }
 
 Item *and_expressions(Item *a, Item *b, Item **org_item);
+
+bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, 
+                             const char *warn_name, MYSQL_TIME *l_time);

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2009-10-23 18:37:57 +0000
+++ b/sql/opt_range.cc	2009-11-02 12:24:07 +0000
@@ -5968,7 +5968,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
 
   switch (type) {
   case Item_func::LT_FUNC:
-    if (stored_field_cmp_to_item(field,value) == 0)
+    if (stored_field_cmp_to_item(param->thd, field, value) == 0)
       tree->max_flag=NEAR_MAX;
     /* fall through */
   case Item_func::LE_FUNC:
@@ -5983,14 +5983,14 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
   case Item_func::GT_FUNC:
     /* Don't use open ranges for partial key_segments */
     if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
-        (stored_field_cmp_to_item(field, value) <= 0))
+        (stored_field_cmp_to_item(param->thd, field, value) <= 0))
       tree->min_flag=NEAR_MIN;
     tree->max_flag= NO_MAX_RANGE;
     break;
   case Item_func::GE_FUNC:
     /* Don't use open ranges for partial key_segments */
     if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
-        (stored_field_cmp_to_item(field,value) < 0))
+        (stored_field_cmp_to_item(param->thd, field, value) < 0))
       tree->min_flag= NEAR_MIN;
     tree->max_flag=NO_MAX_RANGE;
     break;

=== modified file 'sql/time.cc'
--- a/sql/time.cc	2009-06-17 14:56:44 +0000
+++ b/sql/time.cc	2009-11-02 12:24:07 +0000
@@ -965,20 +965,22 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQ
     0   - a == b
     1   - a > b
 
-  NOTES
-    TIME.second_part is not considered during comparison
 */
 
-int
-my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
+int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
 {
-  my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
-  my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
+  ulonglong a_t= TIME_to_ulonglong_datetime(a);
+  ulonglong b_t= TIME_to_ulonglong_datetime(b);
 
+  if (a_t < b_t)
+    return -1;
   if (a_t > b_t)
     return 1;
-  else if (a_t < b_t)
+
+  if (a->second_part < b->second_part)
     return -1;
+  if (a->second_part > b->second_part)
+    return 1;
 
   return 0;
 }


Attachment: [text/bzr-bundle] bzr/martin.hansson@sun.com-20091102122407-krzh4h0i052lbwr5.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (martin.hansson:3161) Bug#47925Martin Hansson2 Nov