Commit a73d2ae0 authored by Tor Didriksen's avatar Tor Didriksen

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.
parent f436b188
...@@ -865,28 +865,43 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, ...@@ -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: 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. DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds.
@param length: length of str @param args item expression which we convert to an ASCII string
@param cs: charset of str @param str_value string buffer
@param values: array of results @param is_negative set to true if interval is prefixed by '-'
@param count: count of elements in result array @param count: count of elements in result array
@param values: array of results
@param transform_msec: if value is true we suppose @param transform_msec: if value is true we suppose
that the last part of string value is microseconds that the last part of string value is microseconds
and we should transform value to six digit value. and we should transform value to six digit value.
For example, '1.1' -> '1.100000' 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, uint count, ulonglong *values,
bool transform_msec) bool transform_msec)
{ {
const char *end=str+length; String *res;
uint i; if (!(res= args->val_str_ascii(str_value)))
long msec_length= 0; 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++; 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; longlong value;
const char *start= str; const char *start= str;
...@@ -1427,45 +1442,24 @@ longlong Item_func_time_to_sec::val_int() ...@@ -1427,45 +1442,24 @@ longlong Item_func_time_to_sec::val_int()
To make code easy, allow interval objects without separators. To make code easy, allow interval objects without separators.
*/ */
bool get_interval_value(Item *args,interval_type int_type, bool get_interval_value(Item *args, interval_type int_type,
String *str_value, INTERVAL *interval) String *str_value, INTERVAL *interval)
{ {
ulonglong array[5]; ulonglong array[5];
longlong UNINIT_VAR(value); 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)); bzero((char*) interval,sizeof(*interval));
if ((int) int_type <= INTERVAL_MICROSECOND) if ((int) int_type <= INTERVAL_MICROSECOND)
{ {
value= args->val_int(); value= args->val_int();
if (args->null_value) if (args->null_value)
return 1; return true;
if (value < 0) if (value < 0)
{ {
interval->neg=1; interval->neg= true;
value= -value; 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) { switch (int_type) {
case INTERVAL_YEAR: case INTERVAL_YEAR:
...@@ -1486,88 +1480,88 @@ bool get_interval_value(Item *args,interval_type int_type, ...@@ -1486,88 +1480,88 @@ bool get_interval_value(Item *args,interval_type int_type,
case INTERVAL_HOUR: case INTERVAL_HOUR:
interval->hour= (ulong) value; interval->hour= (ulong) value;
break; break;
case INTERVAL_MICROSECOND:
interval->second_part=value;
break;
case INTERVAL_MINUTE: case INTERVAL_MINUTE:
interval->minute=value; interval->minute=value;
break; break;
case INTERVAL_SECOND: case INTERVAL_SECOND:
interval->second=value; interval->second=value;
break; break;
case INTERVAL_MICROSECOND:
interval->second_part=value;
break;
case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
if (get_interval_info(str,length,cs,2,array,0)) if (get_interval_info(args, str_value, &interval->neg, 2, array, false))
return (1); return true;
interval->year= (ulong) array[0]; interval->year= (ulong) array[0];
interval->month= (ulong) array[1]; interval->month= (ulong) array[1];
break; break;
case INTERVAL_DAY_HOUR: case INTERVAL_DAY_HOUR:
if (get_interval_info(str,length,cs,2,array,0)) if (get_interval_info(args, str_value, &interval->neg, 2, array, false))
return (1); return true;
interval->day= (ulong) array[0]; interval->day= (ulong) array[0];
interval->hour= (ulong) array[1]; interval->hour= (ulong) array[1];
break; 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: case INTERVAL_DAY_MINUTE:
if (get_interval_info(str,length,cs,3,array,0)) if (get_interval_info(args, str_value, &interval->neg, 3, array, false))
return (1); return true;
interval->day= (ulong) array[0]; interval->day= (ulong) array[0];
interval->hour= (ulong) array[1]; interval->hour= (ulong) array[1];
interval->minute= array[2]; interval->minute= array[2];
break; break;
case INTERVAL_DAY_SECOND: case INTERVAL_DAY_SECOND:
if (get_interval_info(str,length,cs,4,array,0)) if (get_interval_info(args, str_value, &interval->neg, 4, array, false))
return (1); return true;
interval->day= (ulong) array[0]; interval->day= (ulong) array[0];
interval->hour= (ulong) array[1]; interval->hour= (ulong) array[1];
interval->minute= array[2]; interval->minute= array[2];
interval->second= array[3]; interval->second= array[3];
break; 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: case INTERVAL_HOUR_MINUTE:
if (get_interval_info(str,length,cs,2,array,0)) if (get_interval_info(args, str_value, &interval->neg, 2, array, false))
return (1); return true;
interval->hour= (ulong) array[0]; interval->hour= (ulong) array[0];
interval->minute= array[1]; interval->minute= array[1];
break; break;
case INTERVAL_HOUR_SECOND: case INTERVAL_HOUR_SECOND:
if (get_interval_info(str,length,cs,3,array,0)) if (get_interval_info(args, str_value, &interval->neg, 3, array, false))
return (1); return true;
interval->hour= (ulong) array[0]; interval->hour= (ulong) array[0];
interval->minute= array[1]; interval->minute= array[1];
interval->second= array[2]; interval->second= array[2];
break; break;
case INTERVAL_MINUTE_MICROSECOND: case INTERVAL_MINUTE_SECOND:
if (get_interval_info(str,length,cs,3,array,1)) if (get_interval_info(args, str_value, &interval->neg, 2, array, false))
return (1); return true;
interval->minute= array[0]; interval->minute= array[0];
interval->second= array[1]; interval->second= array[1];
interval->second_part= array[2];
break; break;
case INTERVAL_MINUTE_SECOND: case INTERVAL_DAY_MICROSECOND:
if (get_interval_info(str,length,cs,2,array,0)) if (get_interval_info(args, str_value, &interval->neg, 5, array, true))
return (1); 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->minute= array[0];
interval->second= array[1]; interval->second= array[1];
interval->second_part= array[2];
break; break;
case INTERVAL_SECOND_MICROSECOND: case INTERVAL_SECOND_MICROSECOND:
if (get_interval_info(str,length,cs,2,array,1)) if (get_interval_info(args, str_value, &interval->neg, 2, array, true))
return (1); return true;
interval->second= array[0]; interval->second= array[0];
interval->second_part= array[1]; interval->second_part= array[1];
break; break;
...@@ -1575,7 +1569,7 @@ bool get_interval_value(Item *args,interval_type int_type, ...@@ -1575,7 +1569,7 @@ bool get_interval_value(Item *args,interval_type int_type,
DBUG_ASSERT(0); DBUG_ASSERT(0);
break; /* purecov: end */ break; /* purecov: end */
} }
return 0; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment