diff options
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r-- | sql/item_timefunc.cc | 516 |
1 files changed, 398 insertions, 118 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index d6b57464d59..63a7f1f130b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -160,7 +160,8 @@ static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0, static bool extract_date_time(DATE_TIME_FORMAT *format, const char *val, uint length, TIME *l_time, timestamp_type cached_timestamp_type, - const char **sub_pattern_end) + const char **sub_pattern_end, + const char *date_time_type) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -188,12 +189,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, for (; ptr != end && val != val_end; ptr++) { - if (*ptr == '%' && ptr+1 != end) { int val_len; char *tmp; + error= 0; /* Skip pre-space between each argument */ while (val != val_end && my_isspace(cs, *val)) val++; @@ -343,16 +344,22 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Time in AM/PM notation */ case 'r': - error= extract_date_time(&time_ampm_format, val, - (uint)(val_end - val), l_time, - cached_timestamp_type, &val); + /* + We can't just set error here, as we don't want to generate two + warnings in case of errors + */ + if (extract_date_time(&time_ampm_format, val, + (uint)(val_end - val), l_time, + cached_timestamp_type, &val, "time")) + DBUG_RETURN(1); break; /* Time in 24-hour notation */ case 'T': - error= extract_date_time(&time_24hrs_format, val, - (uint)(val_end - val), l_time, - cached_timestamp_type, &val); + if (extract_date_time(&time_24hrs_format, val, + (uint)(val_end - val), l_time, + cached_timestamp_type, &val, "time")) + DBUG_RETURN(1); break; /* Conversion specifiers that match classes of characters */ @@ -463,7 +470,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, if (!my_isspace(&my_charset_latin1,*val)) { make_truncated_value_warning(current_thd, val_begin, length, - cached_timestamp_type); + cached_timestamp_type, NullS); break; } } while (++val != val_end); @@ -471,6 +478,13 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, DBUG_RETURN(0); err: + { + char buff[128]; + strmake(buff, val_begin, min(length, sizeof(buff)-1)); + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), + date_time_type, buff, "str_to_time"); + } DBUG_RETURN(1); } @@ -483,7 +497,6 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, timestamp_type type, String *str) { char intbuff[15]; - uint days_i; uint hours_i; uint weekday; ulong length; @@ -493,7 +506,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, str->set_charset(&my_charset_bin); if (l_time->neg) - str->append("-", 1); + str->append('-'); end= (ptr= format->format.str) + format->format.length; for (; ptr != end ; ptr++) @@ -533,21 +546,21 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, length= int10_to_str(l_time->day, intbuff, 10) - intbuff; str->append_with_prefill(intbuff, length, 1, '0'); if (l_time->day >= 10 && l_time->day <= 19) - str->append("th", 2); + str->append(STRING_WITH_LEN("th")); else { switch (l_time->day %10) { case 1: - str->append("st",2); + str->append(STRING_WITH_LEN("st")); break; case 2: - str->append("nd",2); + str->append(STRING_WITH_LEN("nd")); break; case 3: - str->append("rd",2); + str->append(STRING_WITH_LEN("rd")); break; default: - str->append("th",2); + str->append(STRING_WITH_LEN("th")); break; } } @@ -586,8 +599,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, break; case 'h': case 'I': - days_i= l_time->hour/24; - hours_i= (l_time->hour%24 + 11)%12+1 + 24*days_i; + hours_i= (l_time->hour%24 + 11)%12+1; length= int10_to_str(hours_i, intbuff, 10) - intbuff; str->append_with_prefill(intbuff, length, 2, '0'); break; @@ -608,8 +620,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, str->append_with_prefill(intbuff, length, 1, '0'); break; case 'l': - days_i= l_time->hour/24; - hours_i= (l_time->hour%24 + 11)%12+1 + 24*days_i; + hours_i= (l_time->hour%24 + 11)%12+1; length= int10_to_str(hours_i, intbuff, 10) - intbuff; str->append_with_prefill(intbuff, length, 1, '0'); break; @@ -869,7 +880,7 @@ longlong Item_func_to_days::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - if (get_arg0_date(<ime,0)) + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) return 0; return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day); } @@ -878,7 +889,7 @@ longlong Item_func_dayofyear::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - if (get_arg0_date(<ime,0)) + if (get_arg0_date(<ime,TIME_NO_ZERO_DATE)) return 0; return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) - calc_daynr(ltime.year,1,1) + 1; @@ -888,7 +899,7 @@ longlong Item_func_dayofmonth::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - (void) get_arg0_date(<ime,1); + (void) get_arg0_date(<ime, TIME_FUZZY_DATE); return (longlong) ltime.day; } @@ -896,7 +907,7 @@ longlong Item_func_month::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - (void) get_arg0_date(<ime,1); + (void) get_arg0_date(<ime, TIME_FUZZY_DATE); return (longlong) ltime.month; } @@ -925,7 +936,7 @@ longlong Item_func_quarter::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - (void) get_arg0_date(<ime,1); + (void) get_arg0_date(<ime, TIME_FUZZY_DATE); return (longlong) ((ltime.month+2)/3); } @@ -997,7 +1008,7 @@ longlong Item_func_week::val_int() DBUG_ASSERT(fixed == 1); uint year; TIME ltime; - if (get_arg0_date(<ime,0)) + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) return 0; return (longlong) calc_week(<ime, week_mode((uint) args[1]->val_int()), @@ -1010,7 +1021,7 @@ longlong Item_func_yearweek::val_int() DBUG_ASSERT(fixed == 1); uint year,week; TIME ltime; - if (get_arg0_date(<ime,0)) + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) return 0; week= calc_week(<ime, (week_mode((uint) args[1]->val_int()) | WEEK_YEAR), @@ -1019,16 +1030,17 @@ longlong Item_func_yearweek::val_int() } -/* weekday() has a automatic to_days() on item */ - longlong Item_func_weekday::val_int() { DBUG_ASSERT(fixed == 1); - ulong tmp_value=(ulong) args[0]->val_int(); - if ((null_value=(args[0]->null_value || !tmp_value))) - return 0; /* purecov: inspected */ + TIME ltime; + + if (get_arg0_date(<ime, TIME_NO_ZERO_DATE)) + return 0; - return (longlong) calc_weekday(tmp_value,odbc_type)+test(odbc_type); + return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month, + ltime.day), + odbc_type) + test(odbc_type); } @@ -1051,7 +1063,7 @@ longlong Item_func_year::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; - (void) get_arg0_date(<ime,1); + (void) get_arg0_date(<ime, TIME_FUZZY_DATE); return (longlong) ltime.year; } @@ -1059,7 +1071,7 @@ longlong Item_func_year::val_int() longlong Item_func_unix_timestamp::val_int() { TIME ltime; - bool not_used; + my_bool not_used; DBUG_ASSERT(fixed == 1); if (arg_count == 0) @@ -1150,9 +1162,15 @@ static bool get_interval_value(Item *args,interval_type int_type, case INTERVAL_YEAR: interval->year= (ulong) value; break; + case INTERVAL_QUARTER: + interval->month= (ulong)(value*3); + break; case INTERVAL_MONTH: interval->month= (ulong) value; break; + case INTERVAL_WEEK: + interval->day= (ulong)(value*7); + break; case INTERVAL_DAY: interval->day= (ulong) value; break; @@ -1459,9 +1477,9 @@ void Item_func_now_utc::store_now_in_TIME(TIME *now_time) bool Item_func_now::get_date(TIME *res, - uint fuzzy_date __attribute__((unused))) + uint fuzzy_date __attribute__((unused))) { - *res=ltime; + *res= ltime; return 0; } @@ -1474,6 +1492,70 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions) } +/* + Converts current time in my_time_t to TIME represenatation for local + time zone. Defines time zone (local) used for whole SYSDATE function. +*/ +void Item_func_sysdate_local::store_now_in_TIME(TIME *now_time) +{ + THD *thd= current_thd; + thd->variables.time_zone->gmt_sec_to_TIME(now_time, time(NULL)); + thd->time_zone_used= 1; +} + + +String *Item_func_sysdate_local::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + store_now_in_TIME(<ime); + buff_length= (uint) my_datetime_to_str(<ime, buff); + str_value.set(buff, buff_length, &my_charset_bin); + return &str_value; +} + + +longlong Item_func_sysdate_local::val_int() +{ + DBUG_ASSERT(fixed == 1); + store_now_in_TIME(<ime); + return (longlong) TIME_to_ulonglong_datetime(<ime); +} + + +double Item_func_sysdate_local::val_real() +{ + DBUG_ASSERT(fixed == 1); + store_now_in_TIME(<ime); + return (longlong) TIME_to_ulonglong_datetime(<ime); +} + + +void Item_func_sysdate_local::fix_length_and_dec() +{ + decimals= 0; + collation.set(&my_charset_bin); + max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; +} + + +bool Item_func_sysdate_local::get_date(TIME *res, + uint fuzzy_date __attribute__((unused))) +{ + store_now_in_TIME(<ime); + *res= ltime; + return 0; +} + + +int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions) +{ + store_now_in_TIME(<ime); + to->set_notnull(); + to->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); + return 0; +} + + String *Item_func_sec_to_time::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -1523,38 +1605,60 @@ longlong Item_func_sec_to_time::val_int() void Item_func_date_format::fix_length_and_dec() { + /* + Must use this_item() in case it's a local SP variable + (for ->max_length and ->str_value) + */ + Item *arg1= args[1]->this_item(); + decimals=0; collation.set(&my_charset_bin); - if (args[1]->type() == STRING_ITEM) + if (arg1->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; /* - Force case sensitive collation on format string. - This needed because format modifiers with different case, - for example %m and %M, have different meaning. Thus eq() - will distinguish them. - */ - args[1]->collation.set( - get_charset_by_csname(args[1]->collation.collation->csname, - MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); - /* The result is a binary string (no reason to use collation->mbmaxlen This is becasue make_date_time() only returns binary strings */ - max_length= format_length(((Item_string*) args[1])->const_string()); + max_length= format_length(&arg1->str_value); } else { fixed_length=0; /* The result is a binary string (no reason to use collation->mbmaxlen */ - max_length=min(args[1]->max_length,MAX_BLOB_WIDTH) * 10; + max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10; set_if_smaller(max_length,MAX_BLOB_WIDTH); } maybe_null=1; // If wrong date } +bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const +{ + Item_func_date_format *item_func; + + if (item->type() != FUNC_ITEM) + return 0; + if (func_name() != ((Item_func*) item)->func_name()) + return 0; + if (this == item) + return 1; + item_func= (Item_func_date_format*) item; + if (!args[0]->eq(item_func->args[0], binary_cmp)) + return 0; + /* + We must compare format string case sensitive. + This needed because format modifiers with different case, + for example %m and %M, have different meaning. + */ + if (!args[1]->eq(item_func->args[1], 1)) + return 0; + return 1; +} + + + uint Item_func_date_format::format_length(const String *format) { uint size=0; @@ -1633,7 +1737,7 @@ String *Item_func_date_format::val_str(String *str) if (!is_time_format) { - if (get_arg0_date(&l_time,1)) + if (get_arg0_date(&l_time, TIME_FUZZY_DATE)) return 0; } else @@ -1746,15 +1850,15 @@ void Item_func_convert_tz::fix_length_and_dec() bool -Item_func_convert_tz::fix_fields(THD *thd_arg, TABLE_LIST *tables_arg, Item **ref) +Item_func_convert_tz::fix_fields(THD *thd_arg, Item **ref) { String str; - if (Item_date_func::fix_fields(thd_arg, tables_arg, ref)) - return 1; + if (Item_date_func::fix_fields(thd_arg, ref)) + return TRUE; tz_tables= thd_arg->lex->time_zone_tables_used; - return 0; + return FALSE; } @@ -1788,10 +1892,9 @@ longlong Item_func_convert_tz::val_int() bool Item_func_convert_tz::get_date(TIME *ltime, - uint fuzzy_date __attribute__((unused))) + uint fuzzy_date __attribute__((unused))) { my_time_t my_time_tmp; - bool not_used; String str; if (!from_tz_cached) @@ -1806,7 +1909,7 @@ bool Item_func_convert_tz::get_date(TIME *ltime, to_tz_cached= args[2]->const_item(); } - if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0)) + if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, TIME_NO_ZERO_DATE)) { null_value= 1; return 1; @@ -1817,6 +1920,7 @@ bool Item_func_convert_tz::get_date(TIME *ltime, ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 || ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31) { + my_bool not_used; my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, ¬_used); if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE) to_tz->gmt_sec_to_TIME(ltime, my_time_tmp); @@ -1877,7 +1981,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) INTERVAL interval; ltime->neg= 0; - if (args[0]->get_date(ltime,0) || + if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE) || get_interval_value(args[1],int_type,&value,&interval)) goto null_date; sign= (interval.neg ? -1 : 1); @@ -1931,33 +2035,35 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) daynr= calc_daynr(ltime->year,ltime->month,1) + days; /* Day number from year 0 to 9999-12-31 */ if ((ulonglong) daynr >= MAX_DAY_NUMBER) - goto null_date; + goto invalid_date; get_date_from_daynr((long) daynr, <ime->year, <ime->month, <ime->day); break; } case INTERVAL_DAY: + case INTERVAL_WEEK: period= (calc_daynr(ltime->year,ltime->month,ltime->day) + sign * (long) interval.day); /* Daynumber from year 0 to 9999-12-31 */ if ((ulong) period >= MAX_DAY_NUMBER) - goto null_date; + goto invalid_date; get_date_from_daynr((long) period,<ime->year,<ime->month,<ime->day); break; case INTERVAL_YEAR: ltime->year+= sign * (long) interval.year; if ((ulong) ltime->year >= 10000L) - goto null_date; + goto invalid_date; if (ltime->month == 2 && ltime->day == 29 && calc_days_in_year(ltime->year) != 366) ltime->day=28; // Was leap-year break; case INTERVAL_YEAR_MONTH: + case INTERVAL_QUARTER: case INTERVAL_MONTH: period= (ltime->year*12 + sign * (long) interval.year*12 + ltime->month-1 + sign * (long) interval.month); if ((ulong) period >= 120000L) - goto null_date; + goto invalid_date; ltime->year= (uint) (period / 12); ltime->month= (uint) (period % 12L)+1; /* Adjust day if the new month doesn't have enough days */ @@ -1973,6 +2079,11 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) } 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"); null_date: return (null_value=1); } @@ -1984,7 +2095,7 @@ String *Item_date_add_interval::val_str(String *str) TIME ltime; enum date_time_format_types format; - if (Item_date_add_interval::get_date(<ime,0)) + if (Item_date_add_interval::get_date(<ime, TIME_NO_ZERO_DATE)) return 0; if (ltime.time_type == MYSQL_TIMESTAMP_DATE) @@ -2007,7 +2118,7 @@ longlong Item_date_add_interval::val_int() DBUG_ASSERT(fixed == 1); TIME ltime; longlong date; - if (Item_date_add_interval::get_date(<ime,0)) + if (Item_date_add_interval::get_date(<ime, TIME_NO_ZERO_DATE)) return (longlong) 0; date = (ltime.year*100L + ltime.month)*100L + ltime.day; return ltime.time_type == MYSQL_TIMESTAMP_DATE ? date : @@ -2016,12 +2127,13 @@ longlong Item_date_add_interval::val_int() static const char *interval_names[]= { - "year", "month", "day", "hour", "minute", - "second", "microsecond", "year_month", - "day_hour", "day_minute", "day_second", - "hour_minute", "hour_second", "minute_second", - "day_microsecond", "hour_microsecond", - "minute_microsecond", "second_microsecond" + "year", "quarter", "month", "day", "hour", + "minute", "week", "second", "microsecond", + "year_month", "day_hour", "day_minute", + "day_second", "hour_minute", "hour_second", + "minute_second", "day_microsecond", + "hour_microsecond", "minute_microsecond", + "second_microsecond" }; void Item_date_add_interval::print(String *str) @@ -2037,9 +2149,9 @@ void Item_date_add_interval::print(String *str) void Item_extract::print(String *str) { - str->append("extract(", 8); + str->append(STRING_WITH_LEN("extract(")); str->append(interval_names[int_type]); - str->append(" from ", 6); + str->append(STRING_WITH_LEN(" from ")); args[0]->print(str); str->append(')'); } @@ -2052,7 +2164,9 @@ void Item_extract::fix_length_and_dec() switch (int_type) { case INTERVAL_YEAR: max_length=4; date_value=1; break; case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break; + case INTERVAL_QUARTER: max_length=2; date_value=1; break; case INTERVAL_MONTH: max_length=2; date_value=1; break; + case INTERVAL_WEEK: max_length=2; date_value=1; break; case INTERVAL_DAY: max_length=2; date_value=1; break; case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break; case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break; @@ -2076,10 +2190,12 @@ longlong Item_extract::val_int() { DBUG_ASSERT(fixed == 1); TIME ltime; + uint year; + ulong week_format; long neg; if (date_value) { - if (get_arg0_date(<ime,1)) + if (get_arg0_date(<ime, TIME_FUZZY_DATE)) return 0; neg=1; } @@ -2097,7 +2213,13 @@ longlong Item_extract::val_int() 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/3 + 1; 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 ltime.day; case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg; case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+ @@ -2138,7 +2260,7 @@ bool Item_extract::eq(const Item *item, bool binary_cmp) const if (this == item) return 1; if (item->type() != FUNC_ITEM || - func_name() != ((Item_func*)item)->func_name()) + functype() != ((Item_func*)item)->functype()) return 0; Item_extract* ie= (Item_extract*)item; @@ -2156,7 +2278,7 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const if (this == item) return 1; if (item->type() != FUNC_ITEM || - func_name() != ((Item_func*)item)->func_name()) + functype() != ((Item_func*)item)->functype()) return 0; Item_char_typecast *cast= (Item_char_typecast*)item; @@ -2171,9 +2293,9 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const void Item_typecast::print(String *str) { - str->append("cast(", 5); + str->append(STRING_WITH_LEN("cast(")); args[0]->print(str); - str->append(" as ", 4); + str->append(STRING_WITH_LEN(" as ")); str->append(cast_type()); str->append(')'); } @@ -2181,9 +2303,9 @@ void Item_typecast::print(String *str) void Item_char_typecast::print(String *str) { - str->append("cast(", 5); + str->append(STRING_WITH_LEN("cast(")); args[0]->print(str); - str->append(" as char", 8); + str->append(STRING_WITH_LEN(" as char")); if (cast_length >= 0) { str->append('('); @@ -2196,8 +2318,8 @@ void Item_char_typecast::print(String *str) } if (cast_cs) { - str->append(" charset ", 9); - str->append(cast_cs->name); + str->append(STRING_WITH_LEN(" charset ")); + str->append(cast_cs->csname); } str->append(')'); } @@ -2233,39 +2355,62 @@ String *Item_char_typecast::val_str(String *str) res->set_charset(cast_cs); /* - Cut the tail if cast with length - and the result is longer than cast length, e.g. - CAST('string' AS CHAR(1)) + Cut the tail if cast with length + and the result is longer than cast length, e.g. + CAST('string' AS CHAR(1)) */ - if (cast_length >= 0 && - (res->length() > (length= (uint32) res->charpos(cast_length)))) - { // Safe even if const arg - if (!res->alloced_length()) - { // Don't change const str - str_value= *res; // Not malloced string - res= &str_value; + if (cast_length >= 0) + { + if (res->length() > (length= (uint32) res->charpos(cast_length))) + { // Safe even if const arg + char char_type[40]; + my_snprintf(char_type, sizeof(char_type), "%s(%lu)", + cast_cs == &my_charset_bin ? "BINARY" : "CHAR", length); + + if (!res->alloced_length()) + { // Don't change const str + str_value= *res; // Not malloced string + res= &str_value; + } + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER(ER_TRUNCATED_WRONG_VALUE), char_type, + res->c_ptr_safe()); + res->length((uint) length); + } + else if (cast_cs == &my_charset_bin && res->length() < (uint) cast_length) + { + if (res->alloced_length() < (uint) cast_length) + { + str->alloc(cast_length); + str->copy(*res); + res= str; + } + bzero((char*) res->ptr() + res->length(), + (uint) cast_length - res->length()); + res->length(cast_length); } - res->length((uint) length); } null_value= 0; return res; } + void Item_char_typecast::fix_length_and_dec() { uint32 char_length; - /* - We always force character set conversion if cast_cs - is a multi-byte character set. It garantees that the - result of CAST is a well-formed string. - For single-byte character sets we allow just to copy - from the argument. A single-byte character sets string - is always well-formed. + /* + We always force character set conversion if cast_cs is a + multi-byte character set. It garantees that the result of CAST is + a well-formed string. For single-byte character sets we allow + just to copy from the argument. A single-byte character sets + string is always well-formed. */ - charset_conversion= (cast_cs->mbmaxlen > 1) || - !my_charset_same(args[0]->collation.collation, cast_cs) && - args[0]->collation.collation != &my_charset_bin && - cast_cs != &my_charset_bin; + charset_conversion= ((cast_cs->mbmaxlen > 1) || + !my_charset_same(args[0]->collation.collation, + cast_cs) && + args[0]->collation.collation != &my_charset_bin && + cast_cs != &my_charset_bin); collation.set(cast_cs, DERIVATION_IMPLICIT); char_length= (cast_length >= 0) ? cast_length : args[0]->max_length/args[0]->collation.collation->mbmaxlen; @@ -2277,7 +2422,7 @@ String *Item_datetime_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); TIME ltime; - if (!get_arg0_date(<ime,1) && + if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && !make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, <ime, str)) return str; @@ -2318,7 +2463,7 @@ String *Item_time_typecast::val_str(String *str) bool Item_date_typecast::get_date(TIME *ltime, uint fuzzy_date) { - bool res= get_arg0_date(ltime,1); + bool res= get_arg0_date(ltime, TIME_FUZZY_DATE); ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; return res; @@ -2330,7 +2475,7 @@ String *Item_date_typecast::val_str(String *str) DBUG_ASSERT(fixed == 1); TIME ltime; - if (!get_arg0_date(<ime,1) && !str->alloc(11)) + if (!get_arg0_date(<ime, TIME_FUZZY_DATE) && !str->alloc(11)) { make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; @@ -2425,7 +2570,7 @@ String *Item_func_add_time::val_str(String *str) null_value=0; if (is_date) // TIMESTAMP function { - if (get_arg0_date(&l_time1,1) || + if (get_arg0_date(&l_time1, TIME_FUZZY_DATE) || args[1]->get_time(&l_time2) || l_time1.time_type == MYSQL_TIMESTAMP_TIME || l_time2.time_type != MYSQL_TIMESTAMP_TIME) @@ -2486,18 +2631,18 @@ void Item_func_add_time::print(String *str) if (is_date) { DBUG_ASSERT(sign > 0); - str->append("timestamp(", 10); + str->append(STRING_WITH_LEN("timestamp(")); } else { if (sign > 0) - str->append("addtime(", 8); + str->append(STRING_WITH_LEN("addtime(")); else - str->append("subtime(", 8); + str->append(STRING_WITH_LEN("subtime(")); } args[0]->print(str); str->append(','); - args[0]->print(str); + args[1]->print(str); str->append(')'); } @@ -2605,6 +2750,142 @@ longlong Item_func_microsecond::val_int() } +longlong Item_func_timestamp_diff::val_int() +{ + TIME ltime1, ltime2; + longlong seconds; + long microseconds; + long months= 0; + int neg= 1; + + null_value= 0; + if (args[0]->get_date(<ime1, TIME_NO_ZERO_DATE) || + args[1]->get_date(<ime2, TIME_NO_ZERO_DATE)) + goto null_date; + + if (calc_time_diff(<ime2,<ime1, 1, + &seconds, µseconds)) + neg= -1; + + if (int_type == INTERVAL_YEAR || + int_type == INTERVAL_QUARTER || + int_type == INTERVAL_MONTH) + { + uint year_beg, year_end, month_beg, month_end, day_beg, day_end; + uint years= 0; + if (neg == -1) + { + year_beg= ltime2.year; + year_end= ltime1.year; + month_beg= ltime2.month; + month_end= ltime1.month; + day_beg= ltime2.day; + day_end= ltime1.day; + } + else + { + year_beg= ltime1.year; + year_end= ltime2.year; + month_beg= ltime1.month; + month_end= ltime2.month; + day_beg= ltime1.day; + day_end= ltime2.day; + } + + /* calc years */ + years= year_end - year_beg; + if (month_end < month_beg || (month_end == month_beg && day_end < day_beg)) + years-= 1; + + /* calc months */ + months= 12*years; + if (month_end < month_beg || (month_end == month_beg && day_end < day_beg)) + months+= 12 - (month_beg - month_end); + else + months+= (month_end - month_beg); + if (day_end < day_beg) + months-= 1; + } + + switch (int_type) { + case INTERVAL_YEAR: + return months/12*neg; + case INTERVAL_QUARTER: + return months/3*neg; + case INTERVAL_MONTH: + return months*neg; + case INTERVAL_WEEK: + return seconds/86400L/7L*neg; + case INTERVAL_DAY: + return seconds/86400L*neg; + case INTERVAL_HOUR: + return seconds/3600L*neg; + case INTERVAL_MINUTE: + return seconds/60L*neg; + case INTERVAL_SECOND: + return seconds*neg; + case INTERVAL_MICROSECOND: + /* + In MySQL difference between any two valid datetime values + in microseconds fits into longlong. + */ + return (seconds*1000000L+microseconds)*neg; + default: + break; + } + +null_date: + null_value=1; + return 0; +} + + +void Item_func_timestamp_diff::print(String *str) +{ + str->append(func_name()); + str->append('('); + + switch (int_type) { + case INTERVAL_YEAR: + str->append(STRING_WITH_LEN("YEAR")); + break; + case INTERVAL_QUARTER: + str->append(STRING_WITH_LEN("QUARTER")); + break; + case INTERVAL_MONTH: + str->append(STRING_WITH_LEN("MONTH")); + break; + case INTERVAL_WEEK: + str->append(STRING_WITH_LEN("WEEK")); + break; + case INTERVAL_DAY: + str->append(STRING_WITH_LEN("DAY")); + break; + case INTERVAL_HOUR: + str->append(STRING_WITH_LEN("HOUR")); + break; + case INTERVAL_MINUTE: + str->append(STRING_WITH_LEN("MINUTE")); + break; + case INTERVAL_SECOND: + str->append(STRING_WITH_LEN("SECOND")); + break; + case INTERVAL_MICROSECOND: + str->append(STRING_WITH_LEN("SECOND_FRAC")); + break; + default: + break; + } + + for (uint i=0 ; i < 2 ; i++) + { + str->append(','); + args[i]->print(str); + } + str->append(')'); +} + + String *Item_func_get_format::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2646,13 +2927,13 @@ void Item_func_get_format::print(String *str) switch (type) { case MYSQL_TIMESTAMP_DATE: - str->append("DATE, "); + str->append(STRING_WITH_LEN("DATE, ")); break; case MYSQL_TIMESTAMP_DATETIME: - str->append("DATETIME, "); + str->append(STRING_WITH_LEN("DATETIME, ")); break; case MYSQL_TIMESTAMP_TIME: - str->append("TIME, "); + str->append(STRING_WITH_LEN("TIME, ")); break; default: DBUG_ASSERT(0); @@ -2711,9 +2992,9 @@ get_date_time_result_type(const char *format, uint length) have all types of date-time components and can end our search. */ return DATE_TIME_MICROSECOND; - } } } + } /* We don't have all three types of date-time components */ if (frac_second_used) @@ -2743,8 +3024,7 @@ Field *Item_func_str_to_date::tmp_table_field(TABLE *t_arg) void Item_func_str_to_date::fix_length_and_dec() { char format_buff[64]; - String format_str(format_buff, sizeof(format_buff), &my_charset_bin); - String *format; + String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format; maybe_null= 1; decimals=0; cached_field_type= MYSQL_TYPE_STRING; @@ -2792,7 +3072,7 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), - ltime, cached_timestamp_type, 0)) + ltime, cached_timestamp_type, 0, "datetime")) goto null_date; if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day) { |