From c45050d25322a6f6e9c87b5038eafd4543cd5e1c Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 10 Aug 2018 14:25:58 +0400 Subject: [PATCH] MDEV-16935 Change the parameter of Field_xxx::store_TIME_with_dec() to const Datetime* and const Time* --- sql/field.cc | 196 ++++++++++++++++++++---------------------------- sql/field.h | 34 ++++++--- sql/sql_type.cc | 6 +- sql/sql_type.h | 168 ++++++++++++++++++++++++++++++++++------- 4 files changed, 250 insertions(+), 154 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index d98f96c03d5..b2a57e2f475 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5020,13 +5020,12 @@ my_time_t Field_timestamp::get_timestamp(const uchar *pos, } -int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, +int Field_timestamp::store_TIME_with_warning(THD *thd, const Datetime *dt, const ErrConv *str, int was_cut) { ASSERT_COLUMN_MARKED_FOR_WRITE; uint error = 0; - - if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !l_time) + if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !dt->is_valid_datetime()) { error= 1; set_datetime_warning(WARN_DATA_TRUNCATED, @@ -5038,10 +5037,12 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, str, MYSQL_TIMESTAMP_DATETIME, 1); } + /* Only convert a correct date (not a zero date) */ - if (l_time && l_time->month) + if (dt->is_valid_datetime() && dt->get_mysql_time()->month) { uint conversion_error; + const MYSQL_TIME *l_time= dt->get_mysql_time(); my_time_t timestamp= TIME_to_timestamp(thd, l_time, &conversion_error); if (timestamp == 0 && l_time->second_part == 0) conversion_error= ER_WARN_DATA_OUT_OF_RANGE; @@ -5074,46 +5075,37 @@ int Field_timestamp::store_time_dec(const MYSQL_TIME *ltime, uint dec) ErrConvTime str(ltime); THD *thd= get_thd(); Datetime dt(thd, &warn, ltime, sql_mode_for_timestamp(thd)); - ltime= dt.is_valid_datetime() ? dt.get_mysql_time() : NULL; - return store_TIME_with_warning(thd, const_cast(ltime), - &str, warn); + return store_TIME_with_warning(thd, &dt, &str, warn); } int Field_timestamp::store(const char *from,size_t len,CHARSET_INFO *cs) { - MYSQL_TIME l_time; - MYSQL_TIME_STATUS status; ErrConvString str(from, len, cs); THD *thd= get_thd(); - bool rc= str_to_datetime(cs, from, len, &l_time, - sql_mode_for_timestamp(thd), &status); - return store_TIME_with_warning(thd, rc ? NULL : &l_time, &str, - status.warnings); + int error; + Datetime dt(&error, from, len, cs, sql_mode_for_timestamp(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp::store(double nr) { - MYSQL_TIME l_time; int error; ErrConvDouble str(nr); THD *thd= get_thd(); - bool rc= Sec6(nr).to_datetime(&l_time, sql_mode_for_timestamp(thd), &error); - return store_TIME_with_warning(thd, rc ? NULL : &l_time, &str, error); + Datetime dt(&error, nr, sql_mode_for_timestamp(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp::store(longlong nr, bool unsigned_val) { - MYSQL_TIME l_time; int error; ErrConvInteger str(nr, unsigned_val); THD *thd= get_thd(); - bool rc= Sec6(nr, unsigned_val).to_datetime(&l_time, - sql_mode_for_timestamp(thd), - &error); - return store_TIME_with_warning(thd, rc ? NULL : &l_time, &str, error); + Datetime dt(&error, nr, sql_mode_for_timestamp(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); } @@ -5410,11 +5402,10 @@ my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d) int Field_timestamp::store_decimal(const my_decimal *d) { int error; - MYSQL_TIME ltime; THD *thd= get_thd(); ErrConvDecimal str(d); - bool rc= Sec6(d).to_datetime(<ime, sql_mode_for_timestamp(thd), &error); - return store_TIME_with_warning(thd, rc ? NULL : <ime, &str, error); + Datetime dt(&error, d, sql_mode_for_timestamp(thd)); + return store_TIME_with_warning(thd, &dt, &str, error); } int Field_timestamp_with_dec::set_time() @@ -5538,91 +5529,78 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level, 3 Datetime value that was cut (warning level NOTE) This is used by opt_range.cc:get_mm_leaf(). */ -int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime, +int Field_temporal_with_date::store_TIME_with_warning(const Datetime *dt, const ErrConv *str, int was_cut) { Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN; - int ret; + timestamp_type ts_type= type_handler()->mysql_timestamp_type(); ASSERT_COLUMN_MARKED_FOR_WRITE; - - if (was_cut == 0 && !ltime) // special case: zero date + // Handle totally bad values + if (!dt->is_valid_datetime()) { - was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE; - store_TIME(const_cast(Datetime().get_mysql_time())); - ret= 2; + static const Datetime zero; + store_TIME(zero.get_mysql_time()); + if (was_cut == 0) // special case: zero date + { + set_warnings(trunc_level, str, MYSQL_TIME_WARN_OUT_OF_RANGE, ts_type); + return 2; + } + set_warnings(trunc_level, str, MYSQL_TIME_WARN_TRUNCATED, ts_type); + return 1; } - else if (!ltime) + // Adjust and store the value + if (ts_type == MYSQL_TIMESTAMP_DATE) { - was_cut= MYSQL_TIME_WARN_TRUNCATED; - store_TIME(const_cast(Datetime().get_mysql_time())); - ret= 1; + if (!dt->hhmmssff_is_zero()) + was_cut|= MYSQL_TIME_NOTE_TRUNCATED; + store_TIME(dt->get_mysql_time()); } - else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && - (MYSQL_TIME_WARN_HAVE_NOTES(was_cut) || - (type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATE && - (ltime->hour || ltime->minute || ltime->second || ltime->second_part)))) + else if (dt->fraction_remainder(decimals())) { - trunc_level= Sql_condition::WARN_LEVEL_NOTE; - was_cut|= MYSQL_TIME_WARN_TRUNCATED; - store_TIME(ltime); - ret= 3; + Datetime truncated(dt->trunc(decimals())); + store_TIME(truncated.get_mysql_time()); } else - { - store_TIME(ltime); - ret= was_cut ? 2 : 0; - } - set_warnings(trunc_level, str, was_cut, - type_handler()->mysql_timestamp_type()); - return ret; + store_TIME(dt->get_mysql_time()); + // Caclulate return value and send warnings if needed + return store_TIME_return_code_with_warnings(was_cut, str, ts_type); } int Field_temporal_with_date::store(const char *from, size_t len, CHARSET_INFO *cs) { - MYSQL_TIME ltime; - MYSQL_TIME_STATUS status; - THD *thd= get_thd(); + int error; ErrConvString str(from, len, cs); - bool rc= str_to_datetime(cs, from, len, <ime, sql_mode_for_dates(thd), - &status); - return store_TIME_with_warning(rc ? NULL : <ime, &str, status.warnings); + Datetime dt(&error, from, len, cs, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); } - int Field_temporal_with_date::store(double nr) { - int error= 0; - MYSQL_TIME ltime; - THD *thd= get_thd(); + int error; ErrConvDouble str(nr); - bool rc= Sec6(nr).to_datetime(<ime, sql_mode_for_dates(thd), &error); - return store_TIME_with_warning(rc ? NULL : <ime, &str, error); + Datetime dt(&error, nr, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); } int Field_temporal_with_date::store(longlong nr, bool unsigned_val) { int error; - MYSQL_TIME ltime; - THD *thd= get_thd(); ErrConvInteger str(nr, unsigned_val); - bool rc= Sec6(nr, unsigned_val).to_datetime(<ime, sql_mode_for_dates(thd), - &error); - return store_TIME_with_warning(rc ? NULL : <ime, &str, error); + Datetime dt(&error, nr, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&dt, &str, error); } - int Field_temporal_with_date::store_time_dec(const MYSQL_TIME *ltime, uint dec) { int error; ErrConvTime str(ltime); THD *thd= get_thd(); Datetime dt(thd, &error, ltime, sql_mode_for_dates(thd)); - ltime= dt.is_valid_datetime() ? dt.get_mysql_time() : NULL; - return store_TIME_with_warning(const_cast(ltime), &str, error); + return store_TIME_with_warning(&dt, &str, error); } @@ -5708,34 +5686,28 @@ Item *Field_temporal::get_equal_const_item_datetime(THD *thd, ** In number context: HHMMSS ** Stored as a 3 byte unsigned int ****************************************************************************/ -int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime, - const ErrConv *str, int was_cut) +int Field_time::store_TIME_with_warning(const Time *t, + const ErrConv *str, int warn) { ASSERT_COLUMN_MARKED_FOR_WRITE; - - if (!ltime) + // Handle totally bad values + if (!t->is_valid_time()) { - Datetime tmp; - store_TIME(const_cast(tmp.get_mysql_time())); + static const Datetime zero; + store_TIME(zero.get_mysql_time()); set_warnings(Sql_condition::WARN_LEVEL_WARN, str, MYSQL_TIME_WARN_TRUNCATED); return 1; } - if (ltime->year != 0 || ltime->month != 0) + // Adjust and store the value + if (t->fraction_remainder(decimals())) { - ltime->year= ltime->month= ltime->day= 0; - was_cut|= MYSQL_TIME_NOTE_TRUNCATED; + Time truncated(t->trunc(decimals())); + store_TIME(truncated.get_mysql_time()); } - my_time_trunc(ltime, decimals()); - store_TIME(ltime); - if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) && - MYSQL_TIME_WARN_HAVE_NOTES(was_cut)) - { - set_warnings(Sql_condition::WARN_LEVEL_NOTE, str, - was_cut | MYSQL_TIME_WARN_TRUNCATED); - return 3; - } - set_warnings(Sql_condition::WARN_LEVEL_WARN, str, was_cut); - return was_cut ? 2 : 0; + else + store_TIME(t->get_mysql_time()); + // Calculate return value and send warnings if needed + return store_TIME_return_code_with_warnings(warn, str, MYSQL_TIMESTAMP_TIME); } @@ -5752,12 +5724,10 @@ void Field_time::store_TIME(const MYSQL_TIME *ltime) int Field_time::store(const char *from,size_t len,CHARSET_INFO *cs) { - MYSQL_TIME ltime; - MYSQL_TIME_STATUS status; ErrConvString str(from, len, cs); - bool rc= str_to_time(cs, from, len, <ime, sql_mode_for_dates(get_thd()), - &status); - return store_TIME_with_warning(rc ? NULL : <ime, &str, status.warnings); + int error; + Time tm(&error, from, len, cs, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&tm, &str, error); } @@ -5766,28 +5736,25 @@ int Field_time::store_time_dec(const MYSQL_TIME *ltime, uint dec) ErrConvTime str(ltime); int warn; Time tm(&warn, ltime, curdays); - ltime= tm.is_valid_time() ? tm.get_mysql_time() : NULL; - return store_TIME_with_warning(const_cast(ltime), &str, warn); + return store_TIME_with_warning(&tm, &str, warn); } int Field_time::store(double nr) { - MYSQL_TIME ltime; ErrConvDouble str(nr); int was_cut; - bool rc= Sec6(nr).to_time(<ime, &was_cut); - return store_TIME_with_warning(rc ? NULL : <ime, &str, was_cut); + Time tm(&was_cut, nr); + return store_TIME_with_warning(&tm, &str, was_cut); } int Field_time::store(longlong nr, bool unsigned_val) { - MYSQL_TIME ltime; ErrConvInteger str(nr, unsigned_val); int was_cut; - bool rc= Sec6(nr, unsigned_val).to_time(<ime, &was_cut); - return store_TIME_with_warning(rc ? NULL : <ime, &str, was_cut); + Time tm(&was_cut, nr, unsigned_val); + return store_TIME_with_warning(&tm, &str, was_cut); } @@ -5943,10 +5910,9 @@ void Field_time_hires::store_TIME(const MYSQL_TIME *ltime) int Field_time::store_decimal(const my_decimal *d) { ErrConvDecimal str(d); - MYSQL_TIME ltime; int was_cut; - bool rc= Sec6(d).to_time(<ime, &was_cut); - return store_TIME_with_warning(rc ? NULL : <ime, &str, was_cut); + Time tm(&was_cut, d); + return store_TIME_with_warning(&tm, &str, was_cut); } @@ -6268,7 +6234,7 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -void Field_date::store_TIME(MYSQL_TIME *ltime) +void Field_date::store_TIME(const MYSQL_TIME *ltime) { uint tmp= ltime->year*10000L + ltime->month*100+ltime->day; int4store(ptr,tmp); @@ -6361,7 +6327,7 @@ void Field_date::sql_type(String &res) const ** In number context: YYYYMMDD ****************************************************************************/ -void Field_newdate::store_TIME(MYSQL_TIME *ltime) +void Field_newdate::store_TIME(const MYSQL_TIME *ltime) { uint tmp= ltime->year*16*32 + ltime->month*32+ltime->day; int3store(ptr,tmp); @@ -6512,7 +6478,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx, ** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int. ****************************************************************************/ -void Field_datetime::store_TIME(MYSQL_TIME *ltime) +void Field_datetime::store_TIME(const MYSQL_TIME *ltime) { ulonglong tmp= TIME_to_ulonglong_datetime(ltime); int8store(ptr,tmp); @@ -6650,13 +6616,14 @@ int Field_datetime::set_time() thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start()); now_time.second_part= thd->query_start_sec_part(); set_notnull(); + my_time_trunc(&now_time, decimals()); store_TIME(&now_time); thd->time_zone_used= 1; return 0; } -void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) +void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime) { ulonglong packed= sec_part_shift(pack_time(ltime), dec); store_bigendian(packed, ptr, Field_datetime_hires::pack_length()); @@ -6665,11 +6632,9 @@ void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime) int Field_temporal_with_date::store_decimal(const my_decimal *d) { int error; - MYSQL_TIME ltime; - THD *thd= get_thd(); ErrConvDecimal str(d); - bool rc= Sec6(d).to_datetime(<ime, sql_mode_for_dates(thd), &error); - return store_TIME_with_warning(rc ? NULL : <ime, &str, error); + Datetime tm(&error, d, sql_mode_for_dates(get_thd())); + return store_TIME_with_warning(&tm, &str, error); } bool Field_datetime_with_dec::send_binary(Protocol *protocol) @@ -6742,9 +6707,8 @@ int Field_datetimef::reset() return 0; } -void Field_datetimef::store_TIME(MYSQL_TIME *ltime) +void Field_datetimef::store_TIME(const MYSQL_TIME *ltime) { - my_time_trunc(ltime, decimals()); longlong tmp= TIME_to_longlong_datetime_packed(ltime); my_datetime_packed_to_binary(tmp, ptr, dec); } diff --git a/sql/field.h b/sql/field.h index f39c69cd273..fe550093550 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2595,6 +2595,20 @@ class Field_temporal: public Field { protected: Item *get_equal_const_item_datetime(THD *thd, const Context &ctx, Item *const_item); + int store_TIME_return_code_with_warnings(int warn, const ErrConv *str, + timestamp_type ts_type) + { + if (!MYSQL_TIME_WARN_HAVE_WARNINGS(warn) && + MYSQL_TIME_WARN_HAVE_NOTES(warn)) + { + set_warnings(Sql_condition::WARN_LEVEL_NOTE, str, + warn | MYSQL_TIME_WARN_TRUNCATED, ts_type); + return 3; + } + set_warnings(Sql_condition::WARN_LEVEL_WARN, str, warn, ts_type); + return warn ? 2 : 0; + } + public: Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -2660,9 +2674,10 @@ public: */ class Field_temporal_with_date: public Field_temporal { protected: - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, + int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str, int was_cut); - virtual void store_TIME(MYSQL_TIME *ltime) = 0; + void store_TIME_with_trunc(const Time *); + virtual void store_TIME(const MYSQL_TIME *ltime) = 0; virtual bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const = 0; bool validate_MMDD(bool not_zero_date, uint month, uint day, @@ -2694,7 +2709,8 @@ public: class Field_timestamp :public Field_temporal { protected: sql_mode_t sql_mode_for_timestamp(THD *thd) const; - int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *, int warn); + int store_TIME_with_warning(THD *, const Datetime *, + const ErrConv *, int warn); public: Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -2948,7 +2964,7 @@ public: class Field_date :public Field_date_common { - void store_TIME(MYSQL_TIME *ltime); + void store_TIME(const MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -2984,7 +3000,7 @@ public: class Field_newdate :public Field_date_common { - void store_TIME(MYSQL_TIME *ltime); + void store_TIME(const MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, @@ -3019,7 +3035,7 @@ class Field_time :public Field_temporal { long curdays; protected: virtual void store_TIME(const MYSQL_TIME *ltime); - int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, int warn); + int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn); void set_warnings(Sql_condition::enum_warning_level level, const ErrConv *str, int was_cut) { @@ -3177,7 +3193,7 @@ public: class Field_datetime :public Field_temporal_with_date { - void store_TIME(MYSQL_TIME *ltime); + void store_TIME(const MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, @@ -3270,7 +3286,7 @@ public: DATETIME(1..6) */ class Field_datetime_hires :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); + void store_TIME(const MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, @@ -3293,7 +3309,7 @@ public: DATETIME(0..6) - MySQL56 version */ class Field_datetimef :public Field_datetime_with_dec { - void store_TIME(MYSQL_TIME *ltime); + void store_TIME(const MYSQL_TIME *ltime); bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; int save_field_metadata(uchar *metadata_ptr) { diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 27c32cf11ad..e8dbf663df6 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -21,6 +21,7 @@ #include "sql_time.h" #include "item.h" #include "log.h" +#include "tztime.h" Type_handler_row type_handler_row; @@ -359,12 +360,13 @@ VYear_op::VYear_op(Item_func_hybrid_field_type *item) { } -void Time::make_from_item(Item *item, const Options opt) +void Time::make_from_item(int *warn, Item *item, const Options opt) { + *warn= 0; if (item->get_date(this, opt.get_date_flags())) time_type= MYSQL_TIMESTAMP_NONE; else - valid_MYSQL_TIME_to_valid_value(opt); + valid_MYSQL_TIME_to_valid_value(warn, opt); } diff --git a/sql/sql_type.h b/sql/sql_type.h index 68b4de7c052..b81692d72fd 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -466,6 +466,10 @@ protected: time_type= MYSQL_TIMESTAMP_NONE; } public: + long fraction_remainder(uint dec) const + { + return my_time_fraction_remainder(second_part, dec); + } }; @@ -474,7 +478,7 @@ public: using Item's native timestamp type, without automatic timestamp type conversion. */ -class Temporal_hybrid: private Temporal +class Temporal_hybrid: public Temporal { public: Temporal_hybrid(THD *thd, Item *item); @@ -524,7 +528,7 @@ public: Time derives from MYSQL_TIME privately to make sure it is accessed externally only in the valid state. */ -class Time: private Temporal +class Time: public Temporal { public: enum datetime_to_time_mode_t @@ -599,30 +603,27 @@ private: /* Convert a valid DATE or DATETIME to TIME. Before this call, "this" must be a valid DATE or DATETIME value, - e.g. returned from Item::get_date(). + e.g. returned from Item::get_date(), str_to_time(), number_to_time(). After this call, "this" is a valid TIME value. */ - void valid_datetime_to_valid_time(const Options opt) + void valid_datetime_to_valid_time(int *warn, const Options opt) { DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE || time_type == MYSQL_TIMESTAMP_DATETIME); /* - Make sure that day and hour are valid, so the result hour value + We're dealing with a DATE or DATETIME returned from + str_to_time(), number_to_time() or unpack_time(). + Do some asserts to make sure the result hour value after mixing days to hours does not go out of the valid TIME range. + The maximum hour value after mixing days will be 31*24+23=767, + which is within the supported TIME range. + Thus no adjust_time_range_or_invalidate() is needed here. */ DBUG_ASSERT(day < 32); DBUG_ASSERT(hour < 24); - if (year == 0 && month == 0 && - opt.datetime_to_time_mode() == + if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS) - { - /* - The maximum hour value after mixing days will be 31*24+23=767, - which is within the supported TIME range. - Thus no adjust_time_range_or_invalidate() is needed here. - */ - hour+= day * 24; - } + datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day); year= month= day= 0; time_type= MYSQL_TIMESTAMP_TIME; DBUG_ASSERT(is_valid_time_slow()); @@ -630,6 +631,7 @@ private: /** Convert valid DATE/DATETIME to valid TIME if needed. This method is called after Item::get_date(), + str_to_time(), number_to_time(). which can return only valid TIME/DATE/DATETIME values. Before this call, "this" is: - either a valid TIME/DATE/DATETIME value @@ -639,12 +641,12 @@ private: - either a valid TIME (within the supported TIME range), - or MYSQL_TIMESTAMP_NONE */ - void valid_MYSQL_TIME_to_valid_value(const Options opt) + void valid_MYSQL_TIME_to_valid_value(int *warn, const Options opt) { switch (time_type) { case MYSQL_TIMESTAMP_DATE: case MYSQL_TIMESTAMP_DATETIME: - valid_datetime_to_valid_time(opt); + valid_datetime_to_valid_time(warn, opt); break; case MYSQL_TIMESTAMP_NONE: break; @@ -656,6 +658,19 @@ private: break; } } + + /* + This method is called after number_to_time() and str_to_time(), + which can return DATE or DATETIME values. Convert to TIME if needed. + We trust that xxx_to_time() returns a valid TIME/DATE/DATETIME value, + so here we need to do only simple validation. + */ + void xxx_to_time_result_to_valid_value(int *warn, const Options opt) + { + // str_to_time(), number_to_time() never return MYSQL_TIMESTAMP_ERROR + DBUG_ASSERT(time_type != MYSQL_TIMESTAMP_ERROR); + valid_MYSQL_TIME_to_valid_value(warn, opt); + } void adjust_time_range_or_invalidate(int *warn) { if (check_time_range(this, TIME_SECOND_PART_DIGITS, warn)) @@ -667,12 +682,49 @@ private: long curdays); void make_from_time(int *warn, const MYSQL_TIME *from); void make_from_datetime(int *warn, const MYSQL_TIME *from, long curdays); - void make_from_item(class Item *item, const Options opt); + void make_from_item(int *warn, Item *item, const Options opt); public: + /* + All constructors that accept an "int *warn" parameter initialize *warn. + The old value gets lost. + */ Time() { time_type= MYSQL_TIMESTAMP_NONE; } - Time(Item *item) { make_from_item(item, Options()); } - Time(Item *item, const Options opt) { make_from_item(item, opt); } + Time(Item *item) + { + int warn; + make_from_item(&warn, item, Options()); + } + Time(Item *item, const Options opt) + { + int warn; + make_from_item(&warn, item, opt); + } Time(int *warn, const MYSQL_TIME *from, long curdays); + Time(int *warn, const char *str, uint len, CHARSET_INFO *cs, + const Options opt) + { + MYSQL_TIME_STATUS status; + if (str_to_time(cs, str, len, this, opt.get_date_flags(), &status)) + time_type= MYSQL_TIMESTAMP_NONE; + // The below call will optionally add notes to already collected warnings: + xxx_to_time_result_to_valid_value(&status.warnings, opt); + *warn= status.warnings; + } + Time(int *warn, const Sec6 &nr, const Options opt) + { + if (nr.to_time(this, warn)) + time_type= MYSQL_TIMESTAMP_NONE; + xxx_to_time_result_to_valid_value(warn, opt); + } + Time(int *warn, double nr) + :Temporal(Time(warn, Sec6(nr), Options())) + { } + Time(int *warn, longlong nr, bool unsigned_val) + :Temporal(Time(warn, Sec6(nr, unsigned_val), Options())) + { } + Time(int *warn, const my_decimal *d) + :Temporal(Time(warn, Sec6(d), Options())) + { } static sql_mode_t flags_for_get_date() { return TIME_TIME_ONLY | TIME_INVALID_DATES; } static sql_mode_t comparison_flags_for_get_date() @@ -748,6 +800,12 @@ public: { return is_valid_time() ? Temporal::to_packed() : 0; } + Time trunc(uint dec) const + { + Time tm(*this); + my_time_trunc(&tm, dec); + return tm; + } }; @@ -774,7 +832,7 @@ public: it is accessed externally only in the valid state. */ -class Temporal_with_date: protected Temporal +class Temporal_with_date: public Temporal { protected: void check_date_or_invalidate(int *warn, sql_mode_t flags); @@ -792,6 +850,21 @@ protected: { make_from_item(thd, item); } + Temporal_with_date(int *warn, const Sec6 &nr, sql_mode_t flags) + { + DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0); + if (nr.to_datetime(this, flags, warn)) + time_type= MYSQL_TIMESTAMP_NONE; + } + Temporal_with_date(int *warn, const char *str, uint len, CHARSET_INFO *cs, + sql_mode_t flags) + { + DBUG_ASSERT((flags & TIME_TIME_ONLY) == 0); + MYSQL_TIME_STATUS status; + if (str_to_datetime(cs, str, len, this, flags, &status)) + time_type= MYSQL_TIMESTAMP_NONE; + *warn= status.warnings; + } public: bool check_date_with_warn(ulonglong flags) { @@ -916,6 +989,11 @@ class Datetime: public Temporal_with_date DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATETIME); return !check_datetime_range(this); } + void date_to_datetime_if_needed() + { + if (time_type == MYSQL_TIMESTAMP_DATE) + date_to_datetime(this); + } void make_from_time(THD *thd, int *warn, const MYSQL_TIME *from, sql_mode_t flags); void make_from_datetime(THD *thd, int *warn, const MYSQL_TIME *from, @@ -924,22 +1002,19 @@ public: Datetime(THD *thd, Item *item, sql_mode_t flags) :Temporal_with_date(thd, item, flags) { - if (time_type == MYSQL_TIMESTAMP_DATE) - date_to_datetime(this); + date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } Datetime(THD *thd, Item *item) :Temporal_with_date(thd, item) { - if (time_type == MYSQL_TIMESTAMP_DATE) - date_to_datetime(this); + date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } Datetime(Item *item) :Temporal_with_date(current_thd, item) { - if (time_type == MYSQL_TIMESTAMP_DATE) - date_to_datetime(this); + date_to_datetime_if_needed(); DBUG_ASSERT(is_valid_value_slow()); } Datetime(THD *thd, int *warn, const MYSQL_TIME *from, sql_mode_t flags); @@ -947,6 +1022,39 @@ public: { set_zero_time(this, MYSQL_TIMESTAMP_DATETIME); } + Datetime(int *warn, const char *str, uint len, CHARSET_INFO *cs, + sql_mode_t flags) + :Temporal_with_date(warn, str, len, cs, flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + Datetime(int *warn, double nr, sql_mode_t flags) + :Temporal_with_date(warn, Sec6(nr), flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + Datetime(int *warn, const my_decimal *d, sql_mode_t flags) + :Temporal_with_date(warn, Sec6(d), flags) + { + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + /* + Create a Datime object from a longlong number. + Note, unlike in Time(), we don't need an "unsigned_val" here, + as it's not important if overflow happened because + of a negative number, or because of a huge positive number. + */ + Datetime(int *warn, longlong sec, ulong usec, sql_mode_t flags) + :Temporal_with_date(warn, Sec6(false, (ulonglong) sec, usec), flags) + { + Sec6 nr(false, (ulonglong) sec, usec); + date_to_datetime_if_needed(); + DBUG_ASSERT(is_valid_value_slow()); + } + bool is_valid_datetime() const { /* @@ -1019,6 +1127,12 @@ public: { return is_valid_datetime() ? Temporal::to_packed() : 0; } + Datetime trunc(uint dec) const + { + Datetime tm(*this); + my_time_trunc(&tm, dec); + return tm; + } };