diff options
-rw-r--r-- | sql/item_timefunc.cc | 88 | ||||
-rw-r--r-- | sql/item_timefunc.h | 63 | ||||
-rw-r--r-- | sql/sql_time.cc | 20 | ||||
-rw-r--r-- | sql/sql_time.h | 1 | ||||
-rw-r--r-- | sql/sql_type.h | 118 |
5 files changed, 176 insertions, 114 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 40d1ba2ff3f..f12c8e12668 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2119,69 +2119,45 @@ bool Item_extract::fix_length_and_dec() } -longlong Item_extract::val_int() +uint Extract_source::week(THD *thd) const { - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; + DBUG_ASSERT(is_valid_extract_source()); uint year; - ulong week_format; - long neg; - date_mode_t is_time_flag = date_value ? date_mode_t(0) : TIME_TIME_ONLY; - - // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion - if ((null_value= args[0]->get_date(current_thd, <ime, is_time_flag))) - return 0; - - neg= ltime.neg ? -1 : 1; + ulong week_format= current_thd->variables.default_week_format; + return calc_week(this, week_mode(week_format), &year); +} - DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0); - if (ltime.time_type == MYSQL_TIMESTAMP_TIME) - time_to_daytime_interval(<ime); +longlong Item_extract::val_int() +{ + DBUG_ASSERT(fixed == 1); + Extract_source dt(current_thd, args[0], m_date_mode); + if ((null_value= !dt.is_valid_extract_source())) + return 0; switch (int_type) { - case INTERVAL_YEAR: return ltime.year; - case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month; - case INTERVAL_QUARTER: return (ltime.month+2)/3; - case INTERVAL_MONTH: return ltime.month; - case INTERVAL_WEEK: - { - week_format= current_thd->variables.default_week_format; - return calc_week(<ime, week_mode(week_format), &year); - } - case INTERVAL_DAY: return (long) ltime.day * neg; - case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg; - case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+ - ltime.hour*100L+ - ltime.minute)*neg; - case INTERVAL_DAY_SECOND: return ((longlong) ltime.day*1000000L+ - (longlong) (ltime.hour*10000L+ - ltime.minute*100+ - ltime.second))*neg; - case INTERVAL_HOUR: return (long) ltime.hour*neg; - case INTERVAL_HOUR_MINUTE: return (long) (ltime.hour*100+ltime.minute)*neg; - case INTERVAL_HOUR_SECOND: return (long) (ltime.hour*10000+ltime.minute*100+ - ltime.second)*neg; - case INTERVAL_MINUTE: return (long) ltime.minute*neg; - case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg; - case INTERVAL_SECOND: return (long) ltime.second*neg; - case INTERVAL_MICROSECOND: return (long) ltime.second_part*neg; - case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L + - (longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L + - ltime.minute*100 + - ltime.second)*1000000L + - ltime.second_part)*neg; - case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+ - ltime.second))*1000000L+ - ltime.second_part)*neg; - case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+ - ltime.second_part)*neg; + case INTERVAL_YEAR: return dt.year(); + case INTERVAL_YEAR_MONTH: return dt.year_month(); + case INTERVAL_QUARTER: return dt.quarter(); + case INTERVAL_MONTH: return dt.month(); + case INTERVAL_WEEK: return dt.week(current_thd); + case INTERVAL_DAY: return dt.day(); + case INTERVAL_DAY_HOUR: return dt.day_hour(); + case INTERVAL_DAY_MINUTE: return dt.day_minute(); + case INTERVAL_DAY_SECOND: return dt.day_second(); + case INTERVAL_HOUR: return dt.hour(); + case INTERVAL_HOUR_MINUTE: return dt.hour_minute(); + case INTERVAL_HOUR_SECOND: return dt.hour_second(); + case INTERVAL_MINUTE: return dt.minute(); + case INTERVAL_MINUTE_SECOND: return dt.minute_second(); + case INTERVAL_SECOND: return dt.second(); + case INTERVAL_MICROSECOND: return dt.microsecond(); + case INTERVAL_DAY_MICROSECOND: return dt.day_microsecond(); + case INTERVAL_HOUR_MICROSECOND: return dt.hour_microsecond(); + case INTERVAL_MINUTE_MICROSECOND: return dt.minute_microsecond(); + case INTERVAL_SECOND_MICROSECOND: return dt.second_microsecond(); case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } - return 0; // Impossible + return 0; // Impossible } bool Item_extract::eq(const Item *item, bool binary_cmp) const diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 59031c69c6a..de5ba8df2fe 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -964,9 +964,17 @@ public: }; -class Item_extract :public Item_int_func +class Item_extract :public Item_int_func, + public Type_handler_hybrid_field_type { - bool date_value; + date_mode_t m_date_mode; + const Type_handler_int_result *handler_by_length(uint32 length, + uint32 threashold) + { + if (length >= threashold) + return &type_handler_longlong; + return &type_handler_long; + } void set_date_length(uint32 length) { /* @@ -975,53 +983,34 @@ class Item_extract :public Item_int_func because all around the code we assume that max_length is sign inclusive. Another options is to set unsigned_flag to "true". */ - max_length= length; //QQ: see above - date_value= true; + set_handler(handler_by_length(max_length= length, 10)); // QQ: see above + m_date_mode= date_mode_t(0); } void set_day_length(uint32 length) { - max_length= length + 1/*sign*/; // e.g. '-24:00:00' -> -1 - date_value= true; + /* + Units starting with DAY can be negative: + EXTRACT(DAY FROM '-24:00:00') -> -1 + */ + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= date_mode_t(0); } void set_time_length(uint32 length) { - max_length= length + 1/*sign*/; - date_value= false; + set_handler(handler_by_length(max_length= length + 1/*sign*/, 11)); + m_date_mode= TIME_TIME_ONLY; } public: const interval_type int_type; // keep it public Item_extract(THD *thd, interval_type type_arg, Item *a): - Item_int_func(thd, a), int_type(type_arg) {} + Item_int_func(thd, a), + Type_handler_hybrid_field_type(&type_handler_longlong), + m_date_mode(date_mode_t(0)), + int_type(type_arg) + { } const Type_handler *type_handler() const { - switch (int_type) { - case INTERVAL_YEAR: - case INTERVAL_YEAR_MONTH: - case INTERVAL_QUARTER: - case INTERVAL_MONTH: - case INTERVAL_WEEK: - case INTERVAL_DAY: - case INTERVAL_DAY_HOUR: - case INTERVAL_DAY_MINUTE: - case INTERVAL_DAY_SECOND: - case INTERVAL_HOUR: - case INTERVAL_HOUR_MINUTE: - case INTERVAL_HOUR_SECOND: - case INTERVAL_MINUTE: - case INTERVAL_MINUTE_SECOND: - case INTERVAL_SECOND: - case INTERVAL_MICROSECOND: - case INTERVAL_SECOND_MICROSECOND: - return &type_handler_long; - case INTERVAL_DAY_MICROSECOND: - case INTERVAL_HOUR_MICROSECOND: - case INTERVAL_MINUTE_MICROSECOND: - return &type_handler_longlong; - case INTERVAL_LAST: - break; - } - DBUG_ASSERT(0); - return &type_handler_longlong; + return Type_handler_hybrid_field_type::type_handler(); } longlong val_int(); enum Functype functype() const { return EXTRACT_FUNC; } diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 2bbe7257eb0..630d150be77 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -1218,26 +1218,6 @@ bool time_to_datetime(MYSQL_TIME *ltime) } -/* - 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 ***/ /* diff --git a/sql/sql_time.h b/sql/sql_time.h index 7db7bd4944a..433374e2e9a 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -36,7 +36,6 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); void set_current_date(THD *thd, MYSQL_TIME *to); bool time_to_datetime(MYSQL_TIME *ltime); -void time_to_daytime_interval(MYSQL_TIME *l_time); bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code); bool str_to_datetime_with_warn(THD *thd, diff --git a/sql/sql_type.h b/sql/sql_type.h index 767e715a94e..8356b08ab67 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -478,6 +478,10 @@ protected: second <= TIME_MAX_SECOND && second_part <= TIME_MAX_SECOND_PART; } + bool has_zero_YYYYMM() const + { + return year == 0 && month == 0; + } bool has_zero_YYYYMMDD() const { return year == 0 && month == 0 && day == 0; @@ -556,6 +560,120 @@ public: }; +/* + This class resembles the SQL standard <extract source>, + used in extract expressions, e.g: EXTRACT(DAY FROM dt) + <extract expression> ::= + EXTRACT <left paren> <extract field> FROM <extract source> <right paren> + <extract source> ::= <datetime value expression> | <interval value expression> +*/ +class Extract_source: public Temporal_hybrid +{ + /* + 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. + */ + void time_to_daytime_interval() + { + DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_TIME); + DBUG_ASSERT(has_zero_YYYYMMDD()); + MYSQL_TIME::day= MYSQL_TIME::hour / 24; + MYSQL_TIME::hour%= 24; + } + bool is_valid_extract_source_slow() const + { + return is_valid_temporal() && MYSQL_TIME::hour < 24 && + (has_zero_YYYYMM() || time_type != MYSQL_TIMESTAMP_TIME); + } + bool is_valid_value_slow() const + { + return time_type == MYSQL_TIMESTAMP_NONE || is_valid_extract_source_slow(); + } +public: + Extract_source(THD *thd, Item *item, date_mode_t mode) + :Temporal_hybrid(thd, item, mode) + { + if (MYSQL_TIME::time_type == MYSQL_TIMESTAMP_TIME) + time_to_daytime_interval(); + DBUG_ASSERT(is_valid_value_slow()); + } + inline const MYSQL_TIME *get_mysql_time() const + { + DBUG_ASSERT(is_valid_extract_source_slow()); + return this; + } + bool is_valid_extract_source() const { return is_valid_temporal(); } + int sign() const { return get_mysql_time()->neg ? -1 : 1; } + uint year() const { return get_mysql_time()->year; } + uint month() const { return get_mysql_time()->month; } + int day() const { return (int) get_mysql_time()->day * sign(); } + int hour() const { return (int) get_mysql_time()->hour * sign(); } + int minute() const { return (int) get_mysql_time()->minute * sign(); } + int second() const { return (int) get_mysql_time()->second * sign(); } + int microsecond() const { return (int) get_mysql_time()->second_part * sign(); } + + uint year_month() const { return year() * 100 + month(); } + uint quarter() const { return (month() + 2)/3; } + uint week(THD *thd) const; + + longlong second_microsecond() const + { + return (second() * 1000000LL + microsecond()); + } + + // DAY TO XXX + longlong day_hour() const + { + return (longlong) day() * 100LL + hour(); + } + longlong day_minute() const + { + return day_hour() * 100LL + minute(); + } + longlong day_second() const + { + return day_minute() * 100LL + second(); + } + longlong day_microsecond() const + { + return day_second() * 1000000LL + microsecond(); + } + + // HOUR TO XXX + int hour_minute() const + { + return hour() * 100 + minute(); + } + int hour_second() const + { + return hour_minute() * 100 + second(); + } + longlong hour_microsecond() const + { + return hour_second() * 1000000LL + microsecond(); + } + + // MINUTE TO XXX + int minute_second() const + { + return minute() * 100 + second(); + } + longlong minute_microsecond() const + { + return minute_second() * 1000000LL + microsecond(); + } +}; + + +/* + This class is used for the "time_interval" argument of these SQL functions: + TIMESTAMP(tm,time_interval) + ADDTIME(tm,time_interval) + Features: + - DATE and DATETIME formats are treated as errors + - Preserves hours for TIME format as is, without limiting to TIME_MAX_HOUR +*/ class Interval_DDhhmmssff: public Temporal { static const LEX_CSTRING m_type_name; |