diff options
author | Tor Didriksen <tor.didriksen@oracle.com> | 2012-05-21 10:47:12 +0200 |
---|---|---|
committer | Tor Didriksen <tor.didriksen@oracle.com> | 2012-05-21 10:47:12 +0200 |
commit | b0a2b7c1a9e6ec00f2ebf5660f9dfac943e64209 (patch) | |
tree | 349ceb63fca12684052b68d06d05ee5b2c53f206 /sql/item_timefunc.cc | |
parent | bed97f20ae6e90ca5a1d9f423564fdd95b5bc145 (diff) | |
download | mariadb-git-b0a2b7c1a9e6ec00f2ebf5660f9dfac943e64209.tar.gz |
Bug#13986705 CRASH IN GET_INTERVAL_VALUE() WITH DATE CALCULATION WITH UTF32 INTERVALS
This is a followup to the fix for Bug#12340997
get_interval_value() was trying to parse the input string,
looking for leading '-' while skipping whitespace.
The macro my_isspace() does not work for utf32 character set,
since my_charset_utf32_general_ci.ctype == NULL.
Solution: convert input to ASCII before parsing,
and use the character set of the returned ASCII string.
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r-- | sql/item_timefunc.cc | 146 |
1 files changed, 70 insertions, 76 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6fc85a01668..840c8e55efe 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -865,28 +865,43 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, from the high end. This allows one to give: DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds. - @param length: length of str - @param cs: charset of str - @param values: array of results + @param args item expression which we convert to an ASCII string + @param str_value string buffer + @param is_negative set to true if interval is prefixed by '-' @param count: count of elements in result array + @param values: array of results @param transform_msec: if value is true we suppose that the last part of string value is microseconds and we should transform value to six digit value. For example, '1.1' -> '1.100000' */ -static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, +static bool get_interval_info(Item *args, + String *str_value, + bool *is_negative, uint count, ulonglong *values, bool transform_msec) { - const char *end=str+length; - uint i; - long msec_length= 0; + String *res; + if (!(res= args->val_str_ascii(str_value))) + return true; - while (str != end && !my_isdigit(cs,*str)) + CHARSET_INFO *cs= res->charset(); + const char *str= res->ptr(); + const char *end= str + res->length(); + + str+= cs->cset->scan(cs, str, end, MY_SEQ_SPACES); + if (str < end && *str == '-') + { + *is_negative= true; str++; + } - for (i=0 ; i < count ; i++) + while (str < end && !my_isdigit(cs,*str)) + str++; + + long msec_length= 0; + for (uint i=0 ; i < count ; i++) { longlong value; const char *start= str; @@ -1427,45 +1442,24 @@ longlong Item_func_time_to_sec::val_int() To make code easy, allow interval objects without separators. */ -bool get_interval_value(Item *args,interval_type int_type, - String *str_value, INTERVAL *interval) +bool get_interval_value(Item *args, interval_type int_type, + String *str_value, INTERVAL *interval) { ulonglong array[5]; longlong UNINIT_VAR(value); - const char *UNINIT_VAR(str); - size_t UNINIT_VAR(length); - CHARSET_INFO *cs=str_value->charset(); bzero((char*) interval,sizeof(*interval)); if ((int) int_type <= INTERVAL_MICROSECOND) { value= args->val_int(); if (args->null_value) - return 1; + return true; if (value < 0) { - interval->neg=1; + interval->neg= true; value= -value; } } - else - { - String *res; - if (!(res= args->val_str_ascii(str_value))) - return (1); - - /* record negative intervalls in interval->neg */ - str=res->ptr(); - const char *end=str+res->length(); - while (str != end && my_isspace(cs,*str)) - str++; - if (str != end && *str == '-') - { - interval->neg=1; - str++; - } - length= (size_t) (end-str); // Set up pointers to new str - } switch (int_type) { case INTERVAL_YEAR: @@ -1486,88 +1480,88 @@ bool get_interval_value(Item *args,interval_type int_type, case INTERVAL_HOUR: interval->hour= (ulong) value; break; - case INTERVAL_MICROSECOND: - interval->second_part=value; - break; case INTERVAL_MINUTE: interval->minute=value; break; case INTERVAL_SECOND: interval->second=value; break; + case INTERVAL_MICROSECOND: + interval->second_part=value; + break; case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM - if (get_interval_info(str,length,cs,2,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 2, array, false)) + return true; interval->year= (ulong) array[0]; interval->month= (ulong) array[1]; break; case INTERVAL_DAY_HOUR: - if (get_interval_info(str,length,cs,2,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 2, array, false)) + return true; interval->day= (ulong) array[0]; interval->hour= (ulong) array[1]; break; - case INTERVAL_DAY_MICROSECOND: - if (get_interval_info(str,length,cs,5,array,1)) - return (1); - interval->day= (ulong) array[0]; - interval->hour= (ulong) array[1]; - interval->minute= array[2]; - interval->second= array[3]; - interval->second_part= array[4]; - break; case INTERVAL_DAY_MINUTE: - if (get_interval_info(str,length,cs,3,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 3, array, false)) + return true; interval->day= (ulong) array[0]; interval->hour= (ulong) array[1]; interval->minute= array[2]; break; case INTERVAL_DAY_SECOND: - if (get_interval_info(str,length,cs,4,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 4, array, false)) + return true; interval->day= (ulong) array[0]; interval->hour= (ulong) array[1]; interval->minute= array[2]; interval->second= array[3]; break; - case INTERVAL_HOUR_MICROSECOND: - if (get_interval_info(str,length,cs,4,array,1)) - return (1); - interval->hour= (ulong) array[0]; - interval->minute= array[1]; - interval->second= array[2]; - interval->second_part= array[3]; - break; case INTERVAL_HOUR_MINUTE: - if (get_interval_info(str,length,cs,2,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 2, array, false)) + return true; interval->hour= (ulong) array[0]; interval->minute= array[1]; break; case INTERVAL_HOUR_SECOND: - if (get_interval_info(str,length,cs,3,array,0)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 3, array, false)) + return true; interval->hour= (ulong) array[0]; interval->minute= array[1]; interval->second= array[2]; break; - case INTERVAL_MINUTE_MICROSECOND: - if (get_interval_info(str,length,cs,3,array,1)) - return (1); + case INTERVAL_MINUTE_SECOND: + if (get_interval_info(args, str_value, &interval->neg, 2, array, false)) + return true; interval->minute= array[0]; interval->second= array[1]; - interval->second_part= array[2]; break; - case INTERVAL_MINUTE_SECOND: - if (get_interval_info(str,length,cs,2,array,0)) - return (1); + case INTERVAL_DAY_MICROSECOND: + if (get_interval_info(args, str_value, &interval->neg, 5, array, true)) + return true; + interval->day= (ulong) array[0]; + interval->hour= (ulong) array[1]; + interval->minute= array[2]; + interval->second= array[3]; + interval->second_part= array[4]; + break; + case INTERVAL_HOUR_MICROSECOND: + if (get_interval_info(args, str_value, &interval->neg, 4, array, true)) + return true; + interval->hour= (ulong) array[0]; + interval->minute= array[1]; + interval->second= array[2]; + interval->second_part= array[3]; + break; + case INTERVAL_MINUTE_MICROSECOND: + if (get_interval_info(args, str_value, &interval->neg, 3, array, true)) + return true; interval->minute= array[0]; interval->second= array[1]; + interval->second_part= array[2]; break; case INTERVAL_SECOND_MICROSECOND: - if (get_interval_info(str,length,cs,2,array,1)) - return (1); + if (get_interval_info(args, str_value, &interval->neg, 2, array, true)) + return true; interval->second= array[0]; interval->second_part= array[1]; break; @@ -1575,7 +1569,7 @@ bool get_interval_value(Item *args,interval_type int_type, DBUG_ASSERT(0); break; /* purecov: end */ } - return 0; + return false; } |