diff options
Diffstat (limited to 'sql/time.cc')
-rw-r--r-- | sql/time.cc | 151 |
1 files changed, 128 insertions, 23 deletions
diff --git a/sql/time.cc b/sql/time.cc index ba81fcc86c2..4285e02fb65 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -242,6 +242,109 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, } +/** + 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_FUZZY_DATE, etc) + @param str original number, as a Lazy_string. For the warning + @param f_type how to treat the number as. Possible values + MYSQL_TYPE_TIME, MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME + or MYSQL_TYPE_SET (which means auto-set to TIME or + DATETIME depending on fuzzydate) + @param name field name for the warning message; NULL if not a field + + @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, ulong fuzzydate, + const Lazy_string *str, + enum_field_types f_type, + const char *name) +{ + int was_cut; + longlong res; + + if (f_type == MYSQL_TYPE_SET) + f_type= fuzzydate & TIME_TIME_ONLY ? MYSQL_TYPE_TIME + : MYSQL_TYPE_DATETIME; + + switch (f_type) { + case MYSQL_TYPE_YEAR: + bzero(ltime, sizeof(*ltime)); + ltime->year= nr; + ltime->time_type= MYSQL_TIMESTAMP_DATE; + if (nr > 9999 || (fuzzydate & (TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))) + res= -1; + break; + case MYSQL_TYPE_TIME: + res= number_to_time(neg, nr, sec_part, ltime, &was_cut); + break; + default: + res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut); + break; + } + if (res >= 0) + return 0; + + make_truncated_value_warning(current_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, str, + mysql_type_to_time_type(f_type), + name); + return 1; +} + + +bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, + ulong fuzzydate, + enum_field_types f_type, + const char *name) +{ + const Lazy_string_double str(value); + ulonglong nr; + ulong sec_part; + bool neg= value < 0; + + if (neg) + value= -value; + + nr = value > LONGLONG_MAX ? LONGLONG_MAX + : static_cast<ulonglong>(trunc(value)); + sec_part= (ulong)((value - nr)*TIME_SECOND_PART_FACTOR); + return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, + &str, f_type, name); +} + + +bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, + ulong fuzzydate, + enum_field_types f_type, + const char *name) +{ + const Lazy_string_decimal 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, f_type, name); +} + + +bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime, + ulong fuzzydate, + enum_field_types f_type, + const char *name) +{ + const Lazy_string_num str(value); + bool neg= value < 0; + return number_to_time_with_warn(neg, neg ? -value : value, 0, ltime, + fuzzydate, &str, f_type, name); +} + + /* Convert a datetime from broken-down MYSQL_TIME representation to corresponding TIMESTAMP value. @@ -263,23 +366,8 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time, */ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code) { - my_time_t timestamp; - my_bool in_dst_time_gap= 0; - - *error_code= 0; thd->time_zone_used= 1; - - timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, &in_dst_time_gap); - if (timestamp) - { - if (in_dst_time_gap) - *error_code= ER_WARN_INVALID_TIMESTAMP; - return timestamp; - } - - /* If we are here we have range error. */ - *error_code= ER_WARN_DATA_OUT_OF_RANGE; - return 0; + return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code); } @@ -757,6 +845,7 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve (((((X)->day * 24LL + (X)->hour) * 60LL + \ (X)->minute) * 60LL + (X)->second)*1000000LL + \ (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, INTERVAL interval) { @@ -782,7 +871,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter case INTERVAL_DAY: { longlong usec, daynr; - my_bool neg= ltime->neg; + my_bool neg= 0; enum enum_mysql_timestamp_type time_type= ltime->time_type; if (time_type != MYSQL_TIMESTAMP_TIME) @@ -790,17 +879,31 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter usec= COMBINE(ltime) + sign*COMBINE(&interval); - unpack_time(usec, ltime); - ltime->time_type= time_type; + if (usec < 0) + { + neg= 1; + usec= -usec; + } + + ltime->second_part= GET_PART(usec, 1000000); + ltime->second= GET_PART(usec, 60); + ltime->minute= GET_PART(usec, 60); ltime->neg^= neg; if (time_type == MYSQL_TIMESTAMP_TIME) - break; + { + if (usec > TIME_MAX_HOUR) + goto invalid_date; + ltime->hour= usec; + ltime->day= 0; + return 0; + } if (int_type != INTERVAL_DAY) ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date - daynr= usec/1000000/24/60/60; + ltime->hour= GET_PART(usec, 24); + daynr= usec; /* Day number from year 0 to 9999-12-31 */ if ((ulonglong) daynr > MAX_DAY_NUMBER) @@ -846,13 +949,15 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter goto null_date; } - return 0; // Ok + if (ltime->time_type != MYSQL_TIMESTAMP_TIME) + return 0; // Ok invalid_date: push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER(ER_DATETIME_FUNCTION_OVERFLOW), - "datetime"); + ltime->time_type == MYSQL_TIMESTAMP_TIME ? + "time" : "datetime"); null_date: return 1; } |