List:Commits« Previous MessageNext Message »
From:Alexey Kopytov Date:October 11 2006 9:57am
Subject:bk commit into 5.0 tree (kaa:1.2235) BUG#11655
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of kaa. When kaa does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2006-10-11 13:56:58+04:00, kaa@stripped +7 -0
  Merge of fixes for bug #11655 and #20927 to 5.0
  MERGE: 1.1616.2658.18

  include/my_time.h@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +2 -3
    Manually merged
    MERGE: 1.5.1.3

  mysql-test/r/func_sapdb.result@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +5 -7
    Manually merged
    MERGE: 1.2.1.9

  mysql-test/t/func_time.test@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +0 -0
    Manually merged
    MERGE: 1.21.3.5

  sql-common/my_time.c@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +1 -2
    Manually merged
    MERGE: 1.6.1.10

  sql/field.cc@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +4 -9
    Manually merged
    MERGE: 1.197.14.1

  sql/item_timefunc.cc@stripped, 2006-10-11 13:56:57+04:00, kaa@stripped +1 -2
    Manually merged
    MERGE: 1.51.6.1

  sql/time.cc@stripped, 2006-10-11 13:27:59+04:00, kaa@stripped +0 -0
    Auto merged
    MERGE: 1.49.1.5

# 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:	kaa
# Host:	polly.local
# Root:	/tmp/maint/bug11655/my50-bug11655/RESYNC

--- 1.318/sql/field.cc	2006-10-11 13:57:05 +04:00
+++ 1.319/sql/field.cc	2006-10-11 13:57:05 +04:00
@@ -4759,9 +4759,10 @@ int Field_time::store(const char *from,u
 {
   TIME ltime;
   long tmp;
-  int error;
+  int error= 0;
+  int warning;
 
-  if (str_to_time(from, len, &ltime, &error))
+  if (str_to_time(from, len, &ltime, &warning))
   {
     tmp=0L;
     error= 2;
@@ -4770,29 +4771,27 @@ int Field_time::store(const char *from,u
   }
   else
   {
-    if (error)
+    if (warning & MYSQL_TIME_WARN_TRUNCATED)
       set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                            WARN_DATA_TRUNCATED,
                            from, len, MYSQL_TIMESTAMP_TIME, 1);
-
-    if (ltime.month)
-      ltime.day=0;
-    tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
-    if (tmp > 8385959)
+    if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE)
     {
-      tmp=8385959;
       set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                            ER_WARN_DATA_OUT_OF_RANGE,
                            from, len, MYSQL_TIMESTAMP_TIME, !error);
       error= 1;
     }
+    if (ltime.month)
+      ltime.day=0;
+    tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
     if (error > 1)
       error= 2;
   }
   
   if (ltime.neg)
     tmp= -tmp;
-  error |= Field_time::store((longlong) tmp, FALSE);
+  int3store(ptr,tmp);
   return error;
 }
 
@@ -4811,16 +4810,16 @@ int Field_time::store(double nr)
 {
   long tmp;
   int error= 0;
-  if (nr > 8385959.0)
+  if (nr > (double)TIME_MAX_VALUE)
   {
-    tmp=8385959L;
+    tmp= TIME_MAX_VALUE;
     set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
     error= 1;
   }
-  else if (nr < -8385959.0)
+  else if (nr < (double)-TIME_MAX_VALUE)
   {
-    tmp= -8385959L;
+    tmp= -TIME_MAX_VALUE;
     set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
     error= 1;
@@ -4848,17 +4847,17 @@ int Field_time::store(longlong nr, bool 
 {
   long tmp;
   int error= 0;
-  if (nr < (longlong) -8385959L && !unsigned_val)
+  if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val)
   {
-    tmp= -8385959L;
+    tmp= -TIME_MAX_VALUE;
     set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_DATA_OUT_OF_RANGE, nr,
                          MYSQL_TIMESTAMP_TIME, 1);
     error= 1;
   }
-  else if (nr > (longlong) 8385959 || nr < 0 && unsigned_val)
+  else if (nr > (longlong) TIME_MAX_VALUE || nr < 0 && unsigned_val)
   {
-    tmp=8385959L;
+    tmp= TIME_MAX_VALUE;
     set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_DATA_OUT_OF_RANGE, nr,
                          MYSQL_TIMESTAMP_TIME, 1);

--- 1.120/sql/item_timefunc.cc	2006-10-11 13:57:05 +04:00
+++ 1.121/sql/item_timefunc.cc	2006-10-11 13:57:05 +04:00
@@ -96,6 +96,124 @@ static bool make_datetime(date_time_form
 
 
 /*
+  Wrapper over make_datetime() with validation of the input TIME value
+
+  NOTE
+    see make_datetime() for more information
+
+  RETURN
+    1    if there was an error during converion
+    0    otherwise
+*/
+
+static bool make_datetime_with_warn(date_time_format_types format, TIME *ltime,
+                                    String *str)
+{
+  int warning= 0;
+  bool rc;
+
+  if (make_datetime(format, ltime, str))
+    return 1;
+  if (check_time_range(ltime, &warning))
+    return 1;
+  if (!warning)
+    return 0;
+
+  make_truncated_value_warning(current_thd, str->ptr(), str->length(),
+                               MYSQL_TIMESTAMP_TIME);
+  return make_datetime(format, ltime, str);
+}
+
+
+/*
+  Wrapper over make_time() with validation of the input TIME value
+
+  NOTE
+    see make_time() for more info
+
+  RETURN
+    1    if there was an error during conversion
+    0    otherwise
+*/
+
+static bool make_time_with_warn(const DATE_TIME_FORMAT *format,
+                                TIME *l_time, String *str)
+{
+  int warning= 0;
+  make_time(format, l_time, str);
+  if (check_time_range(l_time, &warning))
+    return 1;
+  if (warning)
+  {
+    make_truncated_value_warning(current_thd, str->ptr(), str->length(),
+                                 MYSQL_TIMESTAMP_TIME);
+    make_time(format, l_time, str);
+  }
+
+  return 0;
+}
+
+
+/*
+  Convert seconds to TIME value with overflow checking
+
+  SYNOPSIS:
+    sec_to_time()
+    seconds          number of seconds
+    unsigned_flag    1, if 'seconds' is unsigned, 0, otherwise
+    ltime            output TIME value
+
+  DESCRIPTION
+    If the 'seconds' argument is inside TIME data range, convert it to a
+    corresponding value.
+    Otherwise, truncate the resulting value to the nearest endpoint, and
+    produce a warning message.
+
+  RETURN
+    1                if the value was truncated during conversion
+    0                otherwise
+*/
+  
+static bool sec_to_time(longlong seconds, bool unsigned_flag, TIME *ltime)
+{
+  uint sec;
+
+  bzero((char *)ltime, sizeof(*ltime));
+  
+  if (seconds < 0)
+  {
+    if (unsigned_flag)
+      goto overflow;
+    ltime->neg= 1;
+    if (seconds < -3020399)
+      goto overflow;
+    seconds= -seconds;
+  }
+  else if (seconds > 3020399)
+    goto overflow;
+  
+  sec= (uint) ((ulonglong) seconds % 3600);
+  ltime->hour= (uint) (seconds/3600);
+  ltime->minute= sec/60;
+  ltime->second= sec % 60;
+
+  return 0;
+
+overflow:
+  ltime->hour= TIME_MAX_HOUR;
+  ltime->minute= TIME_MAX_MINUTE;
+  ltime->second= TIME_MAX_SECOND;
+
+  char buf[22];
+  int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10)
+                 - buf);
+  make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME);
+  
+  return 1;
+}
+
+
+/*
   Date formats corresponding to compound %r and %T conversion specifiers
 
   Note: We should init at least first element of "positions" array
@@ -1570,8 +1688,6 @@ int Item_func_sysdate_local::save_in_fie
 String *Item_func_sec_to_time::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
-  longlong seconds=(longlong) args[0]->val_int();
-  uint sec;
   TIME ltime;
 
   if ((null_value=args[0]->null_value) || str->alloc(19))
@@ -1580,19 +1696,8 @@ String *Item_func_sec_to_time::val_str(S
     return (String*) 0;
   }
 
-  ltime.neg= 0;
-  if (seconds < 0)
-  {
-    seconds= -seconds;
-    ltime.neg= 1;
-  }
-
-  sec= (uint) ((ulonglong) seconds % 3600);
-  ltime.day= 0;
-  ltime.hour= (uint) (seconds/3600);
-  ltime.minute= sec/60;
-  ltime.second= sec % 60;
-
+  sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
+  
   make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
   return str;
 }
@@ -1601,16 +1706,15 @@ String *Item_func_sec_to_time::val_str(S
 longlong Item_func_sec_to_time::val_int()
 {
   DBUG_ASSERT(fixed == 1);
-  longlong seconds=args[0]->val_int();
-  longlong sign=1;
+  TIME ltime;
+  
   if ((null_value=args[0]->null_value))
     return 0;
-  if (seconds < 0)
-  {
-    seconds= -seconds;
-    sign= -1;
-  }
-  return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60));
+
+  sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
+
+  return (ltime.neg ? -1 : 1) *
+    ((ltime.hour)*10000 + ltime.minute*100 + ltime.second);
 }
 
 
@@ -2690,7 +2794,9 @@ String *Item_func_add_time::val_str(Stri
   }
   if (l_time1.neg != l_time2.neg)
     l_sign= -l_sign;
-
+  
+  bzero((char *)&l_time3, sizeof(l_time3));
+  
   l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
 			      &seconds, &microseconds);
 
@@ -2719,9 +2825,9 @@ String *Item_func_add_time::val_str(Stri
   }
   
   l_time3.hour+= days*24;
-  if (!make_datetime(l_time1.second_part || l_time2.second_part ?
-		     TIME_MICROSECOND : TIME_ONLY,
-		     &l_time3, str))
+  if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
+                               TIME_MICROSECOND : TIME_ONLY,
+                               &l_time3, str))
     return str;
 
 null_date:
@@ -2776,6 +2882,8 @@ String *Item_func_timediff::val_str(Stri
   if (l_time1.neg != l_time2.neg)
     l_sign= -l_sign;
 
+  bzero((char *)&l_time3, sizeof(l_time3));
+  
   l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign,
 			      &seconds, &microseconds);
 
@@ -2789,9 +2897,9 @@ String *Item_func_timediff::val_str(Stri
 
   calc_time_from_sec(&l_time3, (long) seconds, microseconds);
 
-  if (!make_datetime(l_time1.second_part || l_time2.second_part ?
-		     TIME_MICROSECOND : TIME_ONLY,
-		     &l_time3, str))
+  if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
+                               TIME_MICROSECOND : TIME_ONLY,
+                               &l_time3, str))
     return str;
 
 null_date:
@@ -2809,29 +2917,57 @@ String *Item_func_maketime::val_str(Stri
 {
   DBUG_ASSERT(fixed == 1);
   TIME ltime;
+  bool overflow= 0;
 
-  long hour=   (long) args[0]->val_int();
-  long minute= (long) args[1]->val_int();
-  long second= (long) args[2]->val_int();
+  longlong hour=   args[0]->val_int();
+  longlong minute= args[1]->val_int();
+  longlong second= args[2]->val_int();
 
   if ((null_value=(args[0]->null_value || 
-		   args[1]->null_value ||
-		   args[2]->null_value || 
-		   minute > 59 || minute < 0 || 
-		   second > 59 || second < 0 ||
-		   str->alloc(19))))
+                   args[1]->null_value ||
+                   args[2]->null_value ||
+                   minute < 0 || minute > 59 ||
+                   second < 0 || second > 59 ||
+                   str->alloc(19))))
     return 0;
 
+  bzero((char *)&ltime, sizeof(ltime));
   ltime.neg= 0;
+
+  /* Check for integer overflows */
   if (hour < 0)
   {
-    ltime.neg= 1;
-    hour= -hour;
+    if (args[0]->unsigned_flag)
+      overflow= 1;
+    else
+      ltime.neg= 1;
+  }
+  if (-hour > UINT_MAX || hour > UINT_MAX)
+    overflow= 1;
+
+  if (!overflow)
+  {
+    ltime.hour=   (uint) ((hour < 0 ? -hour : hour));
+    ltime.minute= (uint) minute;
+    ltime.second= (uint) second;
+  }
+  else
+  {
+    ltime.hour= TIME_MAX_HOUR;
+    ltime.minute= TIME_MAX_MINUTE;
+    ltime.second= TIME_MAX_SECOND;
+    char buf[28];
+    char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
+    int len = (int)(ptr - buf) +
+      my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second));
+    make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME);
+  }
+  
+  if (make_time_with_warn((DATE_TIME_FORMAT *) 0, &ltime, str))
+  {
+    null_value= 1;
+    return 0;
   }
-  ltime.hour=   (ulong) hour;
-  ltime.minute= (ulong) minute;
-  ltime.second= (ulong) second;
-  make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
   return str;
 }
 
@@ -3172,7 +3308,7 @@ bool Item_func_str_to_date::get_date(TIM
     goto null_date;
 
   null_value= 0;
-  bzero((char*) ltime, sizeof(ltime));
+  bzero((char*) ltime, sizeof(*ltime));
   date_time_format.format.str=    (char*) format->ptr();
   date_time_format.format.length= format->length();
   if (extract_date_time(&date_time_format, val->ptr(), val->length(),

--- 1.59/sql/time.cc	2006-10-11 13:57:05 +04:00
+++ 1.60/sql/time.cc	2006-10-11 13:57:05 +04:00
@@ -254,9 +254,9 @@ my_time_t TIME_to_timestamp(THD *thd, co
 bool
 str_to_time_with_warn(const char *str, uint length, TIME *l_time)
 {
-  int was_cut;
-  bool ret_val= str_to_time(str, length, l_time, &was_cut);
-  if (was_cut)
+  int warning;
+  bool ret_val= str_to_time(str, length, l_time, &warning);
+  if (ret_val || warning)
     make_truncated_value_warning(current_thd, str, length,
                                  MYSQL_TIMESTAMP_TIME, NullS);
   return ret_val;

--- 1.18/mysql-test/r/func_sapdb.result	2006-10-11 13:57:05 +04:00
+++ 1.19/mysql-test/r/func_sapdb.result	2006-10-11 13:57:05 +04:00
@@ -107,7 +107,9 @@ subtime("02:01:01.999999", "01:01:01.999
 01:00:00.000000
 select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002");
 timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002")
-8807:59:59.999999
+838:59:59
+Warnings:
+Warning	1292	Truncated incorrect time value: '8807:59:59.999999'
 select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002");
 timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002")
 46:58:57.999999
@@ -219,13 +221,16 @@ SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF
 TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
 ttt	qqq	eee	rrr
 -744:00:00	NULL	NULL	NULL
-26305:01:02	22:58:58	-22:58:58	NULL
--26305:01:02	-22:58:58	22:58:58	NULL
+838:59:59	22:58:58	-22:58:58	NULL
+-838:59:59	-22:58:58	22:58:58	NULL
 NULL	26:02:02	-26:02:02	NULL
 00:00:00	-26:02:02	26:02:02	NULL
 NULL	NULL	NULL	NULL
 NULL	NULL	NULL	NULL
 00:00:00	-24:00:00	24:00:00	NULL
+Warnings:
+Warning	1292	Truncated incorrect time value: '26305:01:02'
+Warning	1292	Truncated incorrect time value: '-26305:01:02'
 drop table t1, test;
 select addtime("-01:01:01.01", "-23:59:59.1") as a;
 a
@@ -235,7 +240,9 @@ a
 10000
 select microsecond(19971231235959.01) as a;
 a
-10000
+0
+Warnings:
+Warning	1292	Truncated incorrect time value: '19971231235959.01'
 select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a;
 a
 1997-12-31 00:00:10.090000

--- 1.11/include/my_time.h	2006-10-11 13:57:05 +04:00
+++ 1.12/include/my_time.h	2006-10-11 13:57:05 +04:00
@@ -49,6 +49,16 @@ typedef long my_time_t;
 #define TIME_NO_ZERO_DATE	(TIME_NO_ZERO_IN_DATE*2)
 #define TIME_INVALID_DATES	(TIME_NO_ZERO_DATE*2)
 
+#define MYSQL_TIME_WARN_TRUNCATED    1
+#define MYSQL_TIME_WARN_OUT_OF_RANGE 2
+
+/* Limits for the TIME data type */
+#define TIME_MAX_HOUR 838
+#define TIME_MAX_MINUTE 59
+#define TIME_MAX_SECOND 59
+#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + \
+                        TIME_MAX_SECOND)
+
 enum enum_mysql_timestamp_type
 str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
                 uint flags, int *was_cut);
@@ -61,7 +71,9 @@ ulonglong TIME_to_ulonglong(const MYSQL_
 
 
 my_bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
-                    int *was_cut);
+                    int *warning);
+
+int check_time_range(struct st_mysql_time *time, int *warning);
 
 long calc_daynr(uint year,uint month,uint day);
 uint calc_days_in_year(uint year);

--- 1.19/sql-common/my_time.c	2006-10-11 13:57:05 +04:00
+++ 1.20/sql-common/my_time.c	2006-10-11 13:57:05 +04:00
@@ -463,8 +463,10 @@ err:
                         There may be an optional [.second_part] after seconds
    length               Length of str
    l_time               Store result here
-   was_cut              Set to 1 if value was cut during conversion or to 0
-                        otherwise.
+   warning              Set MYSQL_TIME_WARN_TRUNCATED flag if the input string
+                        was cut during conversion, and/or
+                        MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is
+                        out of range.
 
    NOTES
      Because of the extra days argument, this function can only
@@ -476,15 +478,16 @@ err:
 */
 
 my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
-                    int *was_cut)
+                    int *warning)
 {
-  long date[5],value;
+  ulong date[5];
+  ulonglong value;
   const char *end=str+length, *end_of_days;
   my_bool found_days,found_hours;
   uint state;
 
   l_time->neg=0;
-  *was_cut= 0;
+  *warning= 0;
   for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
     length--;
   if (str != end && *str == '-')
@@ -499,13 +502,16 @@ my_bool str_to_time(const char *str, uin
   /* Check first if this is a full TIMESTAMP */
   if (length >= 12)
   {                                             /* Probably full timestamp */
+    int was_cut;
     enum enum_mysql_timestamp_type
       res= str_to_datetime(str, length, l_time,
-                           (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut);
+                           (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
     if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
+    {
+      if (was_cut)
+        *warning|= MYSQL_TIME_WARN_TRUNCATED;
       return res == MYSQL_TIMESTAMP_ERROR;
-    /* We need to restore was_cut flag since str_to_datetime can modify it */
-    *was_cut= 0;
+    }
   }
 
   /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
@@ -585,7 +591,7 @@ fractional:
     if (field_length > 0)
       value*= (long) log_10_int[field_length];
     else if (field_length < 0)
-      *was_cut= 1;
+      *warning|= MYSQL_TIME_WARN_TRUNCATED;
     date[4]=value;
   }
   else
@@ -599,10 +605,7 @@ fractional:
        ((str[1] == '-' || str[1] == '+') &&
         (end - str) > 2 &&
         my_isdigit(&my_charset_latin1, str[2]))))
-  {
-    *was_cut= 1;
     return 1;
-  }
 
   if (internal_format_positions[7] != 255)
   {
@@ -621,12 +624,12 @@ fractional:
     }
   }
 
-  /* Some simple checks */
-  if (date[2] >= 60 || date[3] >= 60)
-  {
-    *was_cut= 1;
+  /* Integer overflow checks */
+  if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
+      date[2] > UINT_MAX || date[3] > UINT_MAX ||
+      date[4] > UINT_MAX)
     return 1;
-  }
+  
   l_time->year=         0;                      /* For protocol::store_time */
   l_time->month=        0;
   l_time->day=          date[0];
@@ -636,6 +639,10 @@ fractional:
   l_time->second_part=  date[4];
   l_time->time_type= MYSQL_TIMESTAMP_TIME;
 
+  /* Check if the value is valid and fits into TIME range */
+  if (check_time_range(l_time, warning))
+    return 1;
+  
   /* Check if there is garbage at end of the TIME specification */
   if (str != end)
   {
@@ -643,7 +650,7 @@ fractional:
     {
       if (!my_isspace(&my_charset_latin1,*str))
       {
-        *was_cut= 1;
+        *warning|= MYSQL_TIME_WARN_TRUNCATED;
         break;
       }
     } while (++str != end);
@@ -653,6 +660,47 @@ fractional:
 
 
 /*
+  Check 'time' value to lie in the TIME range
+
+  SYNOPSIS:
+    check_time_range()
+    time     pointer to TIME value
+    warning  set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
+
+  DESCRIPTION
+  If the time value lies outside of the range [-838:59:59, 838:59:59],
+  set it to the closest endpoint of the range and set
+  MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
+
+  RETURN
+    0        time value is valid, but was possibly truncated
+    1        time value is invalid
+*/
+
+int check_time_range(struct st_mysql_time *time, int *warning) 
+{
+  longlong hour;
+
+  if (time->minute >= 60 || time->second >= 60)
+    return 1;
+
+  hour= time->hour + (24*time->day);
+  if (hour <= TIME_MAX_HOUR &&
+      (hour != TIME_MAX_HOUR || time->minute != TIME_MAX_MINUTE ||
+       time->second != TIME_MAX_SECOND || !time->second_part))
+    return 0;
+
+  time->day= 0;
+  time->hour= TIME_MAX_HOUR;
+  time->minute= TIME_MAX_MINUTE;
+  time->second= TIME_MAX_SECOND;
+  time->second_part= 0;
+  *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+  return 0;
+}
+
+
+/*
   Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
 
   SYNOPSIS
@@ -838,7 +886,7 @@ void set_zero_time(MYSQL_TIME *tm, enum 
 int my_time_to_str(const MYSQL_TIME *l_time, char *to)
 {
   uint extra_hours= 0;
-  return my_sprintf(to, (to, "%s%02d:%02d:%02d",
+  return my_sprintf(to, (to, "%s%02u:%02u:%02u",
                          (l_time->neg ? "-" : ""),
                          extra_hours+ l_time->hour,
                          l_time->minute,
@@ -847,7 +895,7 @@ int my_time_to_str(const MYSQL_TIME *l_t
 
 int my_date_to_str(const MYSQL_TIME *l_time, char *to)
 {
-  return my_sprintf(to, (to, "%04d-%02d-%02d",
+  return my_sprintf(to, (to, "%04u-%02u-%02u",
                          l_time->year,
                          l_time->month,
                          l_time->day));
@@ -855,7 +903,7 @@ int my_date_to_str(const MYSQL_TIME *l_t
 
 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to)
 {
-  return my_sprintf(to, (to, "%04d-%02d-%02d %02d:%02d:%02d",
+  return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u",
                          l_time->year,
                          l_time->month,
                          l_time->day,

--- 1.50/mysql-test/t/func_time.test	2006-10-11 13:57:05 +04:00
+++ 1.51/mysql-test/t/func_time.test	2006-10-11 13:57:05 +04:00
@@ -415,7 +415,50 @@ create table t1 select now() - now(), cu
 show create table t1;
 drop table t1;
 
-# End of 4.1 tests
+#
+# Bug #11655: Wrong time is returning from nested selects - maximum time exists
+#
+# check if SEC_TO_TIME() handles out-of-range values correctly
+SELECT SEC_TO_TIME(3300000);
+SELECT SEC_TO_TIME(3300000)+0;
+SELECT SEC_TO_TIME(3600 * 4294967296);
+
+# check if TIME_TO_SEC() handles out-of-range values correctly
+SELECT TIME_TO_SEC('916:40:00');
+
+# check if ADDTIME() handles out-of-range values correctly
+SELECT ADDTIME('500:00:00', '416:40:00');
+SELECT ADDTIME('916:40:00', '416:40:00');
+
+# check if SUBTIME() handles out-of-range values correctly
+SELECT SUBTIME('916:40:00', '416:40:00');
+SELECT SUBTIME('-916:40:00', '416:40:00');
+
+# check if MAKETIME() handles out-of-range values correctly
+SELECT MAKETIME(916,0,0);
+SELECT MAKETIME(4294967296, 0, 0);
+SELECT MAKETIME(-4294967296, 0, 0);
+SELECT MAKETIME(0, 4294967296, 0);
+SELECT MAKETIME(0, 0, 4294967296);
+SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
+
+# check if EXTRACT() handles out-of-range values correctly
+SELECT EXTRACT(HOUR FROM '100000:02:03');
+
+# check if we get proper warnings if both input string truncation
+# and out-of-range value occur
+CREATE TABLE t1(f1 TIME);
+INSERT INTO t1 VALUES('916:00:00 a');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+#
+# Bug #20927: sec_to_time treats big unsigned as signed
+#
+# check if SEC_TO_TIME() handles BIGINT UNSIGNED values correctly
+SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED));
+
+--echo End of 4.1 tests
 
 explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1,
 			timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2;
Thread
bk commit into 5.0 tree (kaa:1.2235) BUG#11655Alexey Kopytov11 Oct