summaryrefslogtreecommitdiff
path: root/sql/item_timefunc.cc
diff options
context:
space:
mode:
authorTor Didriksen <tor.didriksen@oracle.com>2012-05-21 10:47:12 +0200
committerTor Didriksen <tor.didriksen@oracle.com>2012-05-21 10:47:12 +0200
commitb0a2b7c1a9e6ec00f2ebf5660f9dfac943e64209 (patch)
tree349ceb63fca12684052b68d06d05ee5b2c53f206 /sql/item_timefunc.cc
parentbed97f20ae6e90ca5a1d9f423564fdd95b5bc145 (diff)
downloadmariadb-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.cc146
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;
}