diff options
Diffstat (limited to 'sql/sql_time.cc')
-rw-r--r-- | sql/sql_time.cc | 368 |
1 files changed, 110 insertions, 258 deletions
diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 739a95ce9c8..fea23020d7e 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -175,7 +175,7 @@ int calc_weekday(long daynr,bool sunday_first_day_of_week) next week is week 1. */ -uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) +uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year) { uint days; ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); @@ -289,14 +289,14 @@ ulong convert_month_to_period(ulong month) bool -check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, - timestamp_type ts_type) +check_date_with_warn(THD *thd, const MYSQL_TIME *ltime, + date_conv_mode_t fuzzydate, timestamp_type ts_type) { int unused; - if (check_date(ltime, fuzzy_date, &unused)) + if (check_date(ltime, fuzzydate, &unused)) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, &str, ts_type, 0, 0); return true; } @@ -305,7 +305,7 @@ check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, bool -adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) +adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec) { MYSQL_TIME copy= *ltime; ErrConvTime str(©); @@ -313,8 +313,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, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_TIME, 0, NullS); + thd->push_warning_truncated_wrong_value("time", str.ptr()); return false; } @@ -352,33 +351,70 @@ to_ascii(CHARSET_INFO *cs, } -/* Character set-aware version of str_to_time() */ -bool -str_to_time(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) +class TemporalAsciiBuffer: public LEX_CSTRING { char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) +public: + TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs) { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; + if ((cs->state & MY_CS_NONASCII) != 0) + { + LEX_CSTRING::str= cnv; + LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv)); + } + else + { + LEX_CSTRING::str= str; + LEX_CSTRING::length= length; + } } - return str_to_time(str, length, l_time, fuzzydate, status); +}; + + +/* Character set-aware version of ascii_to_datetime_or_date_or_time() */ +bool Temporal::str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st, + const char *str, size_t length, + CHARSET_INFO *cs, + date_mode_t fuzzydate) +{ + TemporalAsciiBuffer tmp(str, length, cs); + return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate)|| + add_nanoseconds(thd, &st->warnings, fuzzydate, st->nanoseconds); } -/* Character set-aware version of str_to_datetime() */ -bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, - MYSQL_TIME *l_time, ulonglong flags, - MYSQL_TIME_STATUS *status) +/* Character set-aware version of str_to_datetime_or_date() */ +bool Temporal::str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t length, + CHARSET_INFO *cs, + date_mode_t flags) { - char cnv[32]; - if ((cs->state & MY_CS_NONASCII) != 0) - { - length= to_ascii(cs, str, length, cnv, sizeof(cnv)); - str= cnv; - } - return str_to_datetime(str, length, l_time, flags, status); + TemporalAsciiBuffer tmp(str, length, cs); + return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags) || + add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds); +} + + +/* Character set-aware version of ascii_to_temporal() */ +bool Temporal::str_to_temporal(THD *thd, MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + date_mode_t flags) +{ + TemporalAsciiBuffer tmp(str, length, cs); + return ascii_to_temporal(status, tmp.str, tmp.length, flags) || + add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds); +} + + +/* Character set-aware version of str_to_DDhhmmssff() */ +bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, + CHARSET_INFO *cs, ulong max_hour) +{ + TemporalAsciiBuffer tmp(str, length, cs); + bool rc= ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status); + DBUG_ASSERT(status->warnings || !rc); + return rc; } @@ -387,125 +423,53 @@ bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, if string was truncated during conversion. NOTE - See description of str_to_datetime() for more information. + See description of str_to_datetime_xxx() for more information. */ bool -str_to_datetime_with_warn(CHARSET_INFO *cs, - const char *str, size_t length, MYSQL_TIME *l_time, - ulonglong flags) +str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs, + const char *str, size_t length, MYSQL_TIME *to, + date_mode_t mode) { - MYSQL_TIME_STATUS status; - THD *thd= current_thd; - 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 : l_time->time_type, 0, NullS); - DBUG_EXECUTE_IF("str_to_datetime_warn", - push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_YES, str);); - return ret_val; + Temporal::Warn_push warn(thd, NULL, NullS, to, mode); + Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, str, length, cs, mode); + return !t->is_valid_temporal(); } -/** - converts a pair of numbers (integer part, microseconds) to MYSQL_TIME - - @param neg sign of the time value - @param nr integer part of the number to convert - @param sec_part microsecond part of the number - @param ltime converted value will be written here - @param fuzzydate conversion flags (TIME_INVALID_DATE, etc) - @param str original number, as an ErrConv. For the warning - @param field_name field name or NULL if not a field. For the warning - - @returns 0 for success, 1 for a failure -*/ -static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part, - MYSQL_TIME *ltime, ulonglong fuzzydate, - const ErrConv *str, - const TABLE_SHARE *s, const char *field_name) -{ - int was_cut; - longlong res; - enum_mysql_timestamp_type ts_type; - bool have_warnings; - - if (fuzzydate & TIME_TIME_ONLY) - { - fuzzydate= TIME_TIME_ONLY; // clear other flags - ts_type= MYSQL_TIMESTAMP_TIME; - res= number_to_time(neg, nr, sec_part, ltime, &was_cut); - have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut); - } - else - { - ts_type= MYSQL_TIMESTAMP_DATETIME; - if (neg) - { - res= -1; - } - else - { - res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut); - have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE); - } - } - - if (res < 0 || have_warnings) - { - make_truncated_value_warning(current_thd, - Sql_condition::WARN_LEVEL_WARN, str, - res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type, - s, field_name); - } - return res < 0; -} - - -bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime, + date_mode_t fuzzydate, const TABLE_SHARE *s, const char *field_name) { - const ErrConvDouble str(value); - bool neg= value < 0; - - if (neg) - value= -value; - - if (value > LONGLONG_MAX) - value= static_cast<double>(LONGLONG_MAX); - - longlong nr= static_cast<ulonglong>(floor(value)); - uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR); - return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, - s, field_name); + Temporal::Warn_push warn(thd, s, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate); + return !t->is_valid_temporal(); } -bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const TABLE_SHARE *s, const char *field_name) { - const ErrConvDecimal str(value); - ulonglong nr; - ulong sec_part; - bool neg= my_decimal2seconds(value, &nr, &sec_part); - return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, - s, field_name); + Temporal::Warn_push warn(thd, s, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate); + return !t->is_valid_temporal(); } -bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, - ulonglong fuzzydate, +bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr, + MYSQL_TIME *ltime, + date_mode_t fuzzydate, const TABLE_SHARE *s, const char *field_name) { - const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg); - return number_to_time_with_warn(neg, value, 0, ltime, - fuzzydate, &str, s, field_name); + /* + Note: conversion from an integer to TIME can overflow to '838:59:59.999999', + so the conversion result can have fractional digits. + */ + Temporal::Warn_push warn(thd, s, field_name, ltime, fuzzydate); + Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, nr, fuzzydate); + return !t->is_valid_temporal(); } @@ -552,7 +516,7 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) } -void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) +void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds) { long t_seconds; // to->neg is not cleared, it may already be set to a useful value @@ -935,50 +899,10 @@ void make_truncated_value_warning(THD *thd, timestamp_type time_type, const TABLE_SHARE *s, const char *field_name) { - char warn_buff[MYSQL_ERRMSG_SIZE]; - const char *type_str; - CHARSET_INFO *cs= &my_charset_latin1; - - switch (time_type) { - case MYSQL_TIMESTAMP_DATE: - type_str= "date"; - break; - case MYSQL_TIMESTAMP_TIME: - type_str= "time"; - break; - case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH - default: - type_str= "datetime"; - break; - } - if (field_name) - { - const char *db_name= s->db.str; - const char *table_name= s->error_table_name(); - - if (!db_name) - db_name= ""; - if (!table_name) - table_name= ""; - - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type_str, sval->ptr(), - db_name, table_name, field_name, - (ulong) thd->get_stmt_da()->current_row_for_warning()); - } - else - { - if (time_type > MYSQL_TIMESTAMP_ERROR) - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), - type_str, sval->ptr()); - else - cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), - ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr()); - } - push_warning(thd, level, - ER_TRUNCATED_WRONG_VALUE, warn_buff); + const char *type_str= Temporal::type_name_by_timestamp_type(time_type); + return thd->push_warning_wrong_or_truncated_value + (level, time_type <= MYSQL_TIMESTAMP_ERROR, type_str, sval->ptr(), + s, field_name); } @@ -989,7 +913,7 @@ void make_truncated_value_warning(THD *thd, (X)->second_part) #define GET_PART(X, N) X % N ## LL; X/= N ## LL -bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, +bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type, const INTERVAL &interval) { long period, sign; @@ -1104,7 +1028,6 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, invalid_date: { - THD *thd= current_thd; push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), @@ -1144,7 +1067,7 @@ null_date: bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, longlong *seconds_out, long *microseconds_out) + int l_sign, ulonglong *seconds_out, ulong *microseconds_out) { long days; bool neg; @@ -1171,10 +1094,10 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, } microseconds= ((longlong)days * SECONDS_IN_24H + - (longlong)(l_time1->hour*3600L + + (longlong)(l_time1->hour*3600LL + l_time1->minute*60L + l_time1->second) - - l_sign*(longlong)(l_time2->hour*3600L + + l_sign*(longlong)(l_time2->hour*3600LL + l_time2->minute*60L + l_time2->second)) * 1000000LL + (longlong)l_time1->second_part - @@ -1186,17 +1109,17 @@ calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, microseconds= -microseconds; neg= 1; } - *seconds_out= microseconds/1000000L; - *microseconds_out= (long) (microseconds%1000000L); + *seconds_out= (ulonglong) microseconds/1000000L; + *microseconds_out= (ulong) (microseconds%1000000L); return neg; } bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, - int l_sign, MYSQL_TIME *l_time3, ulonglong fuzzydate) + int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate) { - longlong seconds; - long microseconds; + ulonglong seconds; + ulong microseconds; bzero((char *) l_time3, sizeof(*l_time3)); l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign, &seconds, µseconds); @@ -1215,7 +1138,7 @@ bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, ("invalid" means > TIME_MAX_SECOND) */ set_if_smaller(seconds, INT_MAX32); - calc_time_from_sec(l_time3, (long) seconds, microseconds); + calc_time_from_sec(l_time3, (ulong) seconds, microseconds); return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) && (microseconds == 0)); } @@ -1272,56 +1195,6 @@ bool time_to_datetime(MYSQL_TIME *ltime) } -/** - Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME. - If ltime is TIME, it's first converted to DATETIME. - If ts_type is DATE, hhmmss is set to zero. - The date part of the result is checked against fuzzy_date. - - @param ltime The value to convert. - @param fuzzy_date Flags to check date. - @param ts_type The type to convert to. - @return false on success, true of error (negative time).*/ -bool -make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, - timestamp_type ts_type) -{ - DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE || - ts_type == MYSQL_TIMESTAMP_DATETIME); - if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime)) - { - /* e.g. negative time */ - ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, - &str, ts_type, 0, 0); - return true; - } - if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE) - ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; - return check_date_with_warn(ltime, fuzzy_date, ts_type); -} - - -/* - Convert a TIME value to DAY-TIME interval, e.g. for extraction: - EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. - Moves full days from ltime->hour to ltime->day. - Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that - the structure is not used for anything else other than extraction: - non-extraction TIME functions expect zero day value! -*/ -void time_to_daytime_interval(MYSQL_TIME *ltime) -{ - DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); - DBUG_ASSERT(ltime->year == 0); - DBUG_ASSERT(ltime->month == 0); - DBUG_ASSERT(ltime->day == 0); - ltime->day= ltime->hour / 24; - ltime->hour%= 24; - ltime->time_type= MYSQL_TIMESTAMP_NONE; -} - - /*** Conversion from TIME to DATETIME ***/ /* @@ -1349,8 +1222,8 @@ 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; + ulonglong seconds; + ulong days, useconds; int sign= ltime->neg ? 1 : -1; ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds); @@ -1440,7 +1313,7 @@ time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) bool time_to_datetime_with_warn(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to, - ulonglong fuzzydate) + date_conv_mode_t fuzzydate) { int warn= 0; DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); @@ -1456,34 +1329,13 @@ time_to_datetime_with_warn(THD *thd, check_date(to, fuzzydate, &warn))) { ErrConvTime str(from); - make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, - &str, MYSQL_TIMESTAMP_DATETIME, 0, 0); + thd->push_warning_truncated_wrong_value("datetime", str.ptr()); return true; } return false; } -bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt, - MYSQL_TIME *tm, uint dec) -{ - if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) - { - *tm= *dt; - datetime_to_time(tm); - return false; - } - else /* new mode */ - { - MYSQL_TIME current_date; - set_current_date(thd, ¤t_date); - calc_time_diff(dt, ¤t_date, 1, tm, 0); - } - int warnings= 0; - return check_time_range(tm, dec, &warnings); -} - - longlong pack_time(const MYSQL_TIME *my_time) { return ((((((my_time->year * 13ULL + |