diff options
Diffstat (limited to 'sql/sql_time.cc')
-rw-r--r-- | sql/sql_time.cc | 238 |
1 files changed, 199 insertions, 39 deletions
diff --git a/sql/sql_time.cc b/sql/sql_time.cc index c5c65391758..b55b1d76b99 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -17,11 +17,11 @@ /* Functions to handle date and time */ +#include <my_global.h> #include "sql_priv.h" -#include "unireg.h" // REQUIRED by other includes #include "sql_time.h" -#include "tztime.h" // struct Time_zone -#include "sql_class.h" // THD, MODE_INVALID_DATES, MODE_NO_ZERO_DATE +#include "tztime.h" // struct Time_zone +#include "sql_class.h" // THD #include <m_ctype.h> @@ -106,9 +106,9 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) uint days; ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); ulong first_daynr=calc_daynr(l_time->year,1,1); - bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST); - bool week_year= test(week_behaviour & WEEK_YEAR); - bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY); + bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST); + bool week_year= MY_TEST(week_behaviour & WEEK_YEAR); + bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY); uint weekday=calc_weekday(first_daynr, !monday_first); *year=l_time->year; @@ -218,11 +218,11 @@ bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, timestamp_type ts_type) { - int dummy_warnings; - if (check_date(ltime, fuzzy_date, &dummy_warnings)) + int unused; + if (check_date(ltime, fuzzy_date, &unused)) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, ts_type, 0); return true; } @@ -239,7 +239,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) if (check_time_range(ltime, dec, &warnings)) return true; if (warnings) - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, MYSQL_TIMESTAMP_TIME, NullS); return false; } @@ -279,9 +279,9 @@ to_ascii(CHARSET_INFO *cs, /* Character set-aware version of str_to_time() */ -timestamp_type +bool str_to_time(CHARSET_INFO *cs, const char *str,uint length, - MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning) + MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) { char cnv[32]; if ((cs->state & MY_CS_NONASCII) != 0) @@ -289,14 +289,14 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length, length= to_ascii(cs, str, length, cnv, sizeof(cnv)); str= cnv; } - return str_to_time(str, length, l_time, fuzzydate, warning); + return str_to_time(str, length, l_time, fuzzydate, status); } /* Character set-aware version of str_to_datetime() */ -timestamp_type str_to_datetime(CHARSET_INFO *cs, - const char *str, uint length, - MYSQL_TIME *l_time, ulonglong flags, int *was_cut) +bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length, + MYSQL_TIME *l_time, ulonglong flags, + MYSQL_TIME_STATUS *status) { char cnv[32]; if ((cs->state & MY_CS_NONASCII) != 0) @@ -304,7 +304,7 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs, length= to_ascii(cs, str, length, cnv, sizeof(cnv)); str= cnv; } - return str_to_datetime(str, length, l_time, flags, was_cut); + return str_to_datetime(str, length, l_time, flags, status); } @@ -316,26 +316,24 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs, See description of str_to_datetime() for more information. */ -timestamp_type +bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, uint length, MYSQL_TIME *l_time, ulonglong flags) { - int was_cut; + MYSQL_TIME_STATUS status; THD *thd= current_thd; - timestamp_type ts_type; - - ts_type= str_to_datetime(cs, str, length, l_time, - (flags | (sql_mode_for_dates(thd))), - &was_cut); - if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) - make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status); + if (ret_val || status.warnings) + make_truncated_value_warning(thd, + ret_val ? Sql_condition::WARN_LEVEL_WARN : + Sql_condition::time_warn_level(status.warnings), str, length, flags & TIME_TIME_ONLY ? - MYSQL_TIMESTAMP_TIME : ts_type, NullS); + MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS); DBUG_EXECUTE_IF("str_to_datetime_warn", - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, str);); - return ts_type; + return ret_val; } @@ -386,7 +384,7 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part, if (res < 0 || have_warnings) { make_truncated_value_warning(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, str, + Sql_condition::WARN_LEVEL_WARN, str, res < 0 ? MYSQL_TIMESTAMP_ERROR : mysql_type_to_time_type(f_type), field_name); @@ -838,8 +836,25 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, } } + +/** + Convert TIME/DATE/DATETIME value to String. + @param l_time DATE value + @param OUT str String to convert to + @param dec Number of fractional digits. +*/ +bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec) +{ + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) + return true; + str->set_charset(&my_charset_numeric); + str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec)); + return false; +} + + void make_truncated_value_warning(THD *thd, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const ErrConv *sval, timestamp_type time_type, const char *field_name) @@ -864,7 +879,7 @@ void make_truncated_value_warning(THD *thd, cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), type_str, sval->ptr(), field_name, - (ulong) thd->warning_info->current_row_for_warning()); + (ulong) thd->get_stmt_da()->current_row_for_warning()); else { if (time_type > MYSQL_TIMESTAMP_ERROR) @@ -915,6 +930,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, my_bool neg= 0; enum enum_mysql_timestamp_type time_type= ltime->time_type; + if ((ulong) interval.day > MAX_DAY_NUMBER) + goto invalid_date; + if (time_type != MYSQL_TIMESTAMP_TIME) ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1; @@ -993,7 +1011,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, return 0; // Ok invalid_date: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER(ER_DATETIME_FUNCTION_OVERFLOW), ltime->time_type == MYSQL_TIMESTAMP_TIME ? @@ -1030,8 +1048,8 @@ null_date: */ bool -calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out, - long *microseconds_out) +calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, + int l_sign, longlong *seconds_out, long *microseconds_out) { long days; bool neg; @@ -1057,13 +1075,13 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s (uint) l_time2->day); } - microseconds= ((longlong)days*LL(86400) + + microseconds= ((longlong)days * SECONDS_IN_24H + (longlong)(l_time1->hour*3600L + l_time1->minute*60L + l_time1->second) - l_sign*(longlong)(l_time2->hour*3600L + l_time2->minute*60L + - l_time2->second)) * LL(1000000) + + l_time2->second)) * 1000000LL + (longlong)l_time1->second_part - l_sign*(longlong)l_time2->second_part; @@ -1095,7 +1113,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s */ -int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) +int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b) { ulonglong a_t= pack_time(a); ulonglong b_t= pack_time(b); @@ -1150,7 +1168,7 @@ make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, { /* e.g. negative time */ ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, ts_type, 0); return true; } @@ -1178,3 +1196,145 @@ void time_to_daytime_interval(MYSQL_TIME *ltime) ltime->hour%= 24; ltime->time_type= MYSQL_TIMESTAMP_NONE; } + + +/*** Conversion from TIME to DATETIME ***/ + +/* + Simple case: TIME is within normal 24 hours internal. + Mix DATE part of ldate and TIME part of ltime together. +*/ +static void +mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || + ldate->time_type == MYSQL_TIMESTAMP_DATETIME); + ldate->hour= ltime->hour; + ldate->minute= ltime->minute; + ldate->second= ltime->second; + ldate->second_part= ltime->second_part; + ldate->time_type= MYSQL_TIMESTAMP_DATETIME; +} + + +/* + Complex case: TIME is negative or outside of the 24 hour interval. +*/ +static void +mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || + ldate->time_type == MYSQL_TIMESTAMP_DATETIME); + longlong seconds; + long days, useconds; + int sign= ltime->neg ? 1 : -1; + ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds); + + DBUG_ASSERT(!ldate->neg); + DBUG_ASSERT(ldate->year > 0); + + days= (long) (seconds / SECONDS_IN_24H); + calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds); + get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day); + ldate->time_type= MYSQL_TIMESTAMP_DATETIME; +} + + +/** + Mix a date value and a time value. + + @param IN/OUT ldate Date value. + @param ltime Time value. +*/ +static void +mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from) +{ + if (!from->neg && from->hour < 24) + mix_date_and_time_simple(to, from); + else + mix_date_and_time_complex(to, from); +} + + +/** + Get current date in DATE format +*/ +void set_current_date(THD *thd, MYSQL_TIME *to) +{ + thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start()); + thd->time_zone_used= 1; + datetime_to_date(to); +} + + +/** + 5.5 compatible conversion from TIME to DATETIME +*/ +static bool +time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) +{ + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + + if (from->neg) + return true; + + /* Set the date part */ + uint day= from->hour / 24; + to->day= day % 31; + to->month= day / 31; + to->year= 0; + /* Set the time part */ + to->hour= from->hour % 24; + to->minute= from->minute; + to->second= from->second; + to->second_part= from->second_part; + /* set sign and type */ + to->neg= 0; + to->time_type= MYSQL_TIMESTAMP_DATETIME; + return false; +} + + +/** + Convert time to datetime. + + The time value is added to the current datetime value. + @param IN ltime Time value to convert from. + @param OUT ltime2 Datetime value to convert to. +*/ +bool +time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) +{ + if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) + return time_to_datetime_old(thd, from, to); + set_current_date(thd, to); + mix_date_and_time(to, from); + return false; +} + + +bool +time_to_datetime_with_warn(THD *thd, + const MYSQL_TIME *from, MYSQL_TIME *to, + ulonglong fuzzydate) +{ + int warn= 0; + DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); + /* + After time_to_datetime() we need to do check_date(), as + the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE. + Note, the SQL standard time->datetime conversion mode always returns + a valid date based on CURRENT_DATE. So we need to do check_date() + only in the old mode. + */ + if (time_to_datetime(thd, from, to) || + ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && + check_date(to, fuzzydate, &warn))) + { + ErrConvTime str(from); + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_DATETIME, 0); + return true; + } + return false; +} |