Fix for bug #7458 "Microseconds are gobbled from the string result of

STR_TO_DATE() function if there is another format specifier after %f 
in format string". Also small cleanup of STR_TO_DATE() implementation.
(After review version.)
This commit is contained in:
dlenev@brandersnatch.localdomain 2004-12-22 18:58:25 +03:00
parent 2d1458ea5b
commit 7b9fd879f6
3 changed files with 42 additions and 19 deletions

View File

@ -296,6 +296,9 @@ Tuesday 52 2001 %W %V %X 00:00:00
15-01-2001 %d-%m-%Y %H:%i:%S 00:00:00 15-01-2001 %d-%m-%Y %H:%i:%S 00:00:00
15-01-20 %d-%m-%y 00:00:00 15-01-20 %d-%m-%y 00:00:00
15-2001-1 %d-%Y-%c 00:00:00 15-2001-1 %d-%Y-%c 00:00:00
select concat('',str_to_date('8:11:2.123456 03-01-02','%H:%i:%S.%f %y-%m-%d'));
concat('',str_to_date('8:11:2.123456 03-01-02','%H:%i:%S.%f %y-%m-%d'))
2003-01-02 08:11:02.123456
truncate table t1; truncate table t1;
insert into t1 values insert into t1 values
('2003-01-02 10:11:12 PM', '%Y-%m-%d %H:%i:%S %p'), ('2003-01-02 10:11:12 PM', '%Y-%m-%d %H:%i:%S %p'),

View File

@ -166,6 +166,8 @@ select date,format,cast(str_to_date(date, format) as datetime) as datetime from
select date,format,DATE(str_to_date(date, format)) as date2 from t1; select date,format,DATE(str_to_date(date, format)) as date2 from t1;
select date,format,TIME(str_to_date(date, format)) as time from t1; select date,format,TIME(str_to_date(date, format)) as time from t1;
select date,format,concat(TIME(str_to_date(date, format))) as time2 from t1; select date,format,concat(TIME(str_to_date(date, format))) as time2 from t1;
# Test small bug in %f handling
select concat('',str_to_date('8:11:2.123456 03-01-02','%H:%i:%S.%f %y-%m-%d'));
# Test wrong dates or converion specifiers # Test wrong dates or converion specifiers

View File

@ -149,6 +149,9 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
conversion specifiers that can be used in such sub-patterns is limited. conversion specifiers that can be used in such sub-patterns is limited.
Also most of checks are skipped in this case. Also most of checks are skipped in this case.
If one adds new format specifiers to this function he should also
consider adding them to get_date_time_result_type() function.
RETURN RETURN
0 ok 0 ok
1 error 1 error
@ -2595,25 +2598,31 @@ void Item_func_get_format::print(String *str)
/* /*
check_result_type(s, l) returns DATE/TIME type Get type of datetime value (DATE/TIME/...) which will be produced
according to format string according to format string.
s: DATE/TIME format string SYNOPSIS
l: length of s get_date_time_result_type()
Result: date_time_format_types value: format - format string
DATE_TIME_MICROSECOND, DATE_TIME, length - length of format string
TIME_MICROSECOND, TIME_ONLY
We don't process day format's characters('D', 'd', 'e') NOTE
because day may be a member of all date/time types. We don't process day format's characters('D', 'd', 'e') because day
If only day format's character and no time part present may be a member of all date/time types.
the result type is MYSQL_TYPE_DATE
Format specifiers supported by this function should be in sync with
specifiers supported by extract_date_time() function.
RETURN VALUE
One of date_time_format_types values:
DATE_TIME_MICROSECOND, DATE_TIME, DATE_ONLY, TIME_MICROSECOND, TIME_ONLY
*/ */
date_time_format_types check_result_type(const char *format, uint length) static date_time_format_types
get_date_time_result_type(const char *format, uint length)
{ {
const char *time_part_frms= "HISThiklrs"; const char *time_part_frms= "HISThiklrs";
const char *date_part_frms= "MUYWabcjmuyw"; const char *date_part_frms= "MVUXYWabcjmvuxyw";
bool date_part_used= 0, time_part_used= 0, frac_second_used= 0; bool date_part_used= 0, time_part_used= 0, frac_second_used= 0;
const char *val= format; const char *val= format;
@ -2624,22 +2633,30 @@ date_time_format_types check_result_type(const char *format, uint length)
if (*val == '%' && val+1 != end) if (*val == '%' && val+1 != end)
{ {
val++; val++;
if ((frac_second_used= (*val == 'f')) || if (*val == 'f')
(!time_part_used && strchr(time_part_frms, *val))) frac_second_used= time_part_used= 1;
else if (!time_part_used && strchr(time_part_frms, *val))
time_part_used= 1; time_part_used= 1;
else if (!date_part_used && strchr(date_part_frms, *val)) else if (!date_part_used && strchr(date_part_frms, *val))
date_part_used= 1; date_part_used= 1;
if (time_part_used && date_part_used && frac_second_used) if (date_part_used && frac_second_used)
{
/*
frac_second_used implies time_part_used, and thus we already
have all types of date-time components and can end our search.
*/
return DATE_TIME_MICROSECOND; return DATE_TIME_MICROSECOND;
}
} }
} }
/* We don't have all three types of date-time components */
if (frac_second_used)
return TIME_MICROSECOND;
if (time_part_used) if (time_part_used)
{ {
if (date_part_used) if (date_part_used)
return DATE_TIME; return DATE_TIME;
if (frac_second_used)
return TIME_MICROSECOND;
return TIME_ONLY; return TIME_ONLY;
} }
return DATE_ONLY; return DATE_ONLY;
@ -2670,7 +2687,8 @@ void Item_func_str_to_date::fix_length_and_dec()
if ((const_item= args[1]->const_item())) if ((const_item= args[1]->const_item()))
{ {
format= args[1]->val_str(&format_str); format= args[1]->val_str(&format_str);
cached_format_type= check_result_type(format->ptr(), format->length()); cached_format_type= get_date_time_result_type(format->ptr(),
format->length());
switch (cached_format_type) { switch (cached_format_type) {
case DATE_ONLY: case DATE_ONLY:
cached_timestamp_type= MYSQL_TIMESTAMP_DATE; cached_timestamp_type= MYSQL_TIMESTAMP_DATE;