diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_func.cc | 4 | ||||
-rw-r--r-- | sql/item_timefunc.h | 70 | ||||
-rw-r--r-- | sql/sql_time.cc | 10 | ||||
-rw-r--r-- | sql/sql_type.cc | 130 | ||||
-rw-r--r-- | sql/sql_type.h | 82 |
5 files changed, 257 insertions, 39 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc index ff315aef458..ccac348e4bc 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2649,13 +2649,13 @@ bool Item_func_min_max::get_time_native(THD *thd, MYSQL_TIME *ltime) { DBUG_ASSERT(fixed == 1); - Time value(thd, args[0]); + Time value(thd, args[0], Time::Options(), decimals); if (!value.is_valid_time()) return (null_value= true); for (uint i= 1; i < arg_count ; i++) { - Time tmp(thd, args[i]); + Time tmp(thd, args[i], Time::Options(), decimals); if (!tmp.is_valid_time()) return (null_value= true); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index ad261635cc0..c1e77eae5bc 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1202,20 +1202,22 @@ public: bool fix_length_and_dec() { THD *thd= current_thd; - uint dec= MY_MAX(args[0]->datetime_precision(thd), - args[1]->time_precision(thd)); - fix_attributes_datetime(dec); + uint dec0= args[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, args[1]); + fix_attributes_datetime(MY_MAX(dec0, dec1)); maybe_null= true; return false; } bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) { Datetime dt(thd, args[0], date_mode_t(0)); - MYSQL_TIME ltime2; - return (null_value= (!dt.is_valid_datetime() || - args[1]->get_time(thd, <ime2) || - Sec6_add(dt.get_mysql_time(), <ime2, 1). - to_datetime(ltime))); + if (!dt.is_valid_datetime()) + return null_value= true; + Interval_DDhhmmssff it(thd, args[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return null_value= true; + return (null_value= Sec6_add(dt.get_mysql_time(), it.get_mysql_time(), 1). + to_datetime(ltime)); } Item *get_copy(THD *thd) { return get_item_copy<Item_func_timestamp>(thd, this); } @@ -1558,20 +1560,23 @@ public: bool fix_length_and_dec(Item_handled_func *item) const { THD *thd= current_thd; - uint dec= MY_MAX(item->arguments()[0]->datetime_precision(thd), - item->arguments()[1]->time_precision(thd)); - item->fix_attributes_datetime(dec); + uint dec0= item->arguments()[0]->datetime_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_datetime(MY_MAX(dec0, dec1)); return false; } bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); - MYSQL_TIME l_time2; Datetime dt(thd, item->arguments()[0], date_mode_t(0)); - return (item->null_value= (!dt.is_valid_datetime() || - item->arguments()[1]->get_time(current_thd, &l_time2) || - Sec6_add(dt.get_mysql_time(), &l_time2, m_sign). + if (!dt.is_valid_datetime()) + return item->null_value= true; + Interval_DDhhmmssff it(thd, item->arguments()[1]); + if (!it.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(dt.get_mysql_time(), + it.get_mysql_time(), m_sign). to_datetime(to))); } }; @@ -1588,20 +1593,23 @@ public: bool fix_length_and_dec(Item_handled_func *item) const { THD *thd= current_thd; - uint dec= MY_MAX(item->arguments()[0]->time_precision(thd), - item->arguments()[1]->time_precision(thd)); - item->fix_attributes_time(dec); + uint dec0= item->arguments()[0]->time_precision(thd); + uint dec1= Interval_DDhhmmssff::fsp(thd, item->arguments()[1]); + item->fix_attributes_time(MY_MAX(dec0, dec1)); return false; } bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to, date_mode_t fuzzy) const { DBUG_ASSERT(item->is_fixed()); - MYSQL_TIME l_time2; Time t(thd, item->arguments()[0]); - return (item->null_value= (!t.is_valid_time() || - item->arguments()[1]->get_time(current_thd, &l_time2) || - Sec6_add(t.get_mysql_time(), &l_time2, m_sign). + if (!t.is_valid_time()) + return item->null_value= true; + Interval_DDhhmmssff i(thd, item->arguments()[1]); + if (!i.is_valid_interval_DDhhmmssff()) + return item->null_value= true; + return (item->null_value= (Sec6_add(t.get_mysql_time(), + i.get_mysql_time(), m_sign). to_time(thd, to, item->decimals))); } }; @@ -1617,8 +1625,9 @@ public: { } bool fix_length_and_dec(Item_handled_func *item) const { - uint dec= MY_MAX(item->arguments()[0]->decimals, - item->arguments()[1]->decimals); + uint dec0= item->arguments()[0]->decimals; + uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]); + uint dec= MY_MAX(dec0, dec1); item->collation.set(item->default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); @@ -1629,12 +1638,15 @@ public: { DBUG_ASSERT(item->is_fixed()); // Detect a proper timestamp type based on the argument values - MYSQL_TIME l_time1, l_time2; - if (item->arguments()[0]->get_time(thd, &l_time1) || - item->arguments()[1]->get_time(thd, &l_time2)) + Temporal_hybrid l_time1(thd, item->arguments()[0], TIME_TIME_ONLY); + if (!l_time1.is_valid_temporal()) + return (item->null_value= true); + Interval_DDhhmmssff l_time2(thd, item->arguments()[1]); + if (!l_time2.is_valid_interval_DDhhmmssff()) return (item->null_value= true); - Sec6_add add(&l_time1, &l_time2, m_sign); - return (item->null_value= (l_time1.time_type == MYSQL_TIMESTAMP_TIME ? + Sec6_add add(l_time1.get_mysql_time(), l_time2.get_mysql_time(), m_sign); + return (item->null_value= (l_time1.get_mysql_time()->time_type == + MYSQL_TIMESTAMP_TIME ? add.to_time(thd, to, item->decimals) : add.to_datetime(to))); } diff --git a/sql/sql_time.cc b/sql/sql_time.cc index ce6eb2047a8..90803b48539 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -395,6 +395,16 @@ bool Temporal::str_to_datetime(MYSQL_TIME_STATUS *status, } +/* 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); + return ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status); +} + + /* Convert a timestamp string to a MYSQL_TIME value and produce a warning if string was truncated during conversion. diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 869c3a1cc2e..1dfaa3a73b0 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -154,6 +154,12 @@ VDec_op::VDec_op(Item_func_hybrid_field_type *item) } +date_mode_t Temporal::sql_mode_for_dates(THD *thd) +{ + return ::sql_mode_for_dates(thd); +} + + bool Dec_ptr::to_datetime_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, Item *item) { @@ -176,9 +182,9 @@ my_decimal *Temporal::bad_to_decimal(my_decimal *to) const } -Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item) +Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate) { - if (item->get_date(thd, this, sql_mode_for_dates(thd))) + if (item->get_date(thd, this, fuzzydate)) time_type= MYSQL_TIMESTAMP_NONE; } @@ -331,6 +337,117 @@ VYear_op::VYear_op(Item_func_hybrid_field_type *item) { } +const LEX_CSTRING Interval_DDhhmmssff::m_type_name= + {STRING_WITH_LEN("INTERVAL DAY TO SECOND")}; + + +Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st, + bool push_warnings, + Item *item, ulong max_hour) +{ + my_time_status_init(st); + switch (item->cmp_type()) { + case ROW_RESULT: + DBUG_ASSERT(0); + time_type= MYSQL_TIMESTAMP_NONE; + break; + case TIME_RESULT: + { + if (item->get_date(thd, this, TIME_TIME_ONLY)) + time_type= MYSQL_TIMESTAMP_NONE; + else if (time_type != MYSQL_TIMESTAMP_TIME) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + push_warning_wrong_or_truncated_value(thd, ErrConvTime(this), + st->warnings); + time_type= MYSQL_TIMESTAMP_NONE; + } + break; + } + case INT_RESULT: + case REAL_RESULT: + case DECIMAL_RESULT: + case STRING_RESULT: + { + StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp; + String *str= item->val_str(&tmp); + if (!str) + time_type= MYSQL_TIMESTAMP_NONE; + else if (str_to_DDhhmmssff(st, str->ptr(), str->length(), str->charset(), + UINT_MAX32)) + { + if (push_warnings) + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, + ErrConvString(str).ptr()); + time_type= MYSQL_TIMESTAMP_NONE; + } + else + { + if (hour > max_hour) + { + st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE; + time_type= MYSQL_TIMESTAMP_NONE; + } + // Warn if hour or nanosecond truncation happened + if (push_warnings) + push_warning_wrong_or_truncated_value(thd, ErrConvString(str), + st->warnings); + } + } + break; + } + DBUG_ASSERT(is_valid_value_slow()); +} + + +void +Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings) +{ + if (warnings & MYSQL_TIME_WARN_OUT_OF_RANGE) + { + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_WARNINGS(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN, + m_type_name.str, str.ptr()); + } + else if (MYSQL_TIME_WARN_HAVE_NOTES(warnings)) + { + thd->push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_NOTE, + m_type_name.str, str.ptr()); + } +} + + +uint Interval_DDhhmmssff::fsp(THD *thd, Item *item) +{ + MYSQL_TIME_STATUS st; + switch (item->cmp_type()) { + case INT_RESULT: + case TIME_RESULT: + return item->decimals; + case REAL_RESULT: + case DECIMAL_RESULT: + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); + case ROW_RESULT: + DBUG_ASSERT(0); + return 0; + case STRING_RESULT: + break; + } + if (!item->const_item() || item->is_expensive()) + return TIME_SECOND_PART_DIGITS; + Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32); + return it.is_valid_interval_DDhhmmssff() ? st.precision : + TIME_SECOND_PART_DIGITS; +} + + void Time::make_from_item(THD *thd, int *warn, Item *item, const Options opt) { *warn= 0; @@ -3487,6 +3604,15 @@ bool Type_handler_temporal_result:: { bool rc= Type_handler::Item_func_min_max_fix_attributes(thd, func, items, nitems); + bool is_time= func->field_type() == MYSQL_TYPE_TIME; + func->decimals= 0; + for (uint i= 0; i < nitems; i++) + { + uint deci= is_time ? items[i]->time_precision(thd) : + items[i]->datetime_precision(thd); + set_if_bigger(func->decimals, deci); + } + if (rc || func->maybe_null) return rc; /* diff --git a/sql/sql_type.h b/sql/sql_type.h index 9708e9b2705..1a6c77008e8 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -446,6 +446,7 @@ public: class Temporal: protected MYSQL_TIME { public: + static date_mode_t sql_mode_for_dates(THD *thd); bool is_valid_temporal() const { DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); @@ -469,6 +470,16 @@ protected: CHARSET_INFO *cs, date_mode_t fuzzydate); bool str_to_datetime(MYSQL_TIME_STATUS *st, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t fuzzydate); + bool has_valid_mmssff() const + { + return minute <= TIME_MAX_MINUTE && + second <= TIME_MAX_SECOND && + second_part <= TIME_MAX_SECOND_PART; + } + bool has_zero_YYYYMMDD() const + { + return year == 0 && month == 0 && day == 0; + } public: static void *operator new(size_t size, MYSQL_TIME *ltime) throw() { @@ -492,8 +503,13 @@ public: class Temporal_hybrid: public Temporal { public: - Temporal_hybrid(THD *thd, Item *item); - Temporal_hybrid(Item *item): Temporal_hybrid(current_thd, item) { } + Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate); + Temporal_hybrid(THD *thd, Item *item) + :Temporal_hybrid(thd, item, sql_mode_for_dates(thd)) + { } + Temporal_hybrid(Item *item) + :Temporal_hybrid(current_thd, item) + { } Temporal_hybrid(MYSQL_TIME_STATUS *st, const char *str, size_t length, CHARSET_INFO *cs, date_mode_t fuzzydate) { @@ -538,6 +554,63 @@ public: }; +class Interval_DDhhmmssff: public Temporal +{ + static const LEX_CSTRING m_type_name; + bool str_to_DDhhmmssff(MYSQL_TIME_STATUS *status, + const char *str, size_t length, CHARSET_INFO *cs, + ulong max_hour); + void push_warning_wrong_or_truncated_value(THD *thd, + const ErrConv &str, + int warnings); + bool is_valid_interval_DDhhmmssff_slow() const + { + return time_type == MYSQL_TIMESTAMP_TIME && + has_zero_YYYYMMDD() && has_valid_mmssff(); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || + is_valid_interval_DDhhmmssff_slow(); + } +public: + // Get fractional second precision from an Item + static uint fsp(THD *thd, Item *item); + /* + Maximum useful HOUR value: + TIMESTAMP'0001-01-01 00:00:00' + '87649415:59:59' = '9999-12-31 23:59:59' + This gives maximum possible interval values: + - '87649415:59:59.999999' (in 'hh:mm:ss.ff' format) + - '3652058 23:59:59.999999' (in 'DD hh:mm:ss.ff' format) + */ + static uint max_useful_hour() + { + return 87649415; + } +public: + Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st, bool push_warnings, + Item *item, ulong max_hour); + Interval_DDhhmmssff(THD *thd, Item *item) + { + MYSQL_TIME_STATUS st; + new(this) Interval_DDhhmmssff(thd, &st, true, item, max_useful_hour()); + } + const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_interval_DDhhmmssff_slow()); + return this; + } + bool is_valid_interval_DDhhmmssff() const + { + return time_type == MYSQL_TIMESTAMP_TIME; + } + bool is_valid_value() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_interval_DDhhmmssff(); + } +}; + + /** Class Time is designed to store valid TIME values. @@ -609,10 +682,7 @@ private: bool is_valid_time_slow() const { return time_type == MYSQL_TIMESTAMP_TIME && - year == 0 && month == 0 && day == 0 && - minute <= TIME_MAX_MINUTE && - second <= TIME_MAX_SECOND && - second_part <= TIME_MAX_SECOND_PART; + has_zero_YYYYMMDD() && has_valid_mmssff(); } void hhmmssff_copy(const MYSQL_TIME *from) { |