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,
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++;
}
while (str < end && !my_isdigit(cs,*str))
str++;
for (i=0 ; i < count ; i++)
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,
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);
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);
if (get_interval_info(args, str_value, &interval->neg, 2, array, false))
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_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;
}
......
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