diff options
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r-- | sql/item_timefunc.cc | 469 |
1 files changed, 265 insertions, 204 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2dc704f6873..e8440803295 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -30,6 +30,7 @@ #pragma implementation // gcc: Class implementation #endif +#include <my_global.h> #include "sql_priv.h" /* It is necessary to include set_var.h instead of item.h because there @@ -147,14 +148,14 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, switch (*++ptr) { /* Year */ case 'Y': - tmp= (char*) val + min(4, val_len); + tmp= (char*) val + MY_MIN(4, val_len); l_time->year= (int) my_strtoll10(val, &tmp, &error); if ((int) (tmp-val) <= 2) l_time->year= year_2000_handling(l_time->year); val= tmp; break; case 'y': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->year= (int) my_strtoll10(val, &tmp, &error); val= tmp; l_time->year= year_2000_handling(l_time->year); @@ -163,7 +164,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Month */ case 'm': case 'c': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->month= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; @@ -180,15 +181,15 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Day */ case 'd': case 'e': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->day= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; case 'D': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->day= (int) my_strtoll10(val, &tmp, &error); /* Skip 'st, 'nd, 'th .. */ - val= tmp + min((int) (val_end-tmp), 2); + val= tmp + MY_MIN((int) (val_end-tmp), 2); break; /* Hour */ @@ -199,14 +200,14 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* fall through */ case 'k': case 'H': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->hour= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; /* Minute */ case 'i': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->minute= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; @@ -214,7 +215,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Second */ case 's': case 'S': - tmp= (char*) val + min(2, val_len); + tmp= (char*) val + MY_MIN(2, val_len); l_time->second= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; @@ -266,7 +267,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, val= tmp; break; case 'j': - tmp= (char*) val + min(val_len, 3); + tmp= (char*) val + MY_MIN(val_len, 3); yearday= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; @@ -278,7 +279,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, case 'u': sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V'); strict_week_number= (*ptr=='V' || *ptr=='v'); - tmp= (char*) val + min(val_len, 2); + tmp= (char*) val + MY_MIN(val_len, 2); if ((week_number= (int) my_strtoll10(val, &tmp, &error)) < 0 || (strict_week_number && !week_number) || week_number > 53) @@ -290,7 +291,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, case 'X': case 'x': strict_week_number_year_type= (*ptr=='X'); - tmp= (char*) val + min(4, val_len); + tmp= (char*) val + MY_MIN(4, val_len); strict_week_number_year= (int) my_strtoll10(val, &tmp, &error); val= tmp; break; @@ -426,7 +427,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, { if (!my_isspace(&my_charset_latin1,*val)) { - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, + Sql_condition::WARN_LEVEL_WARN, val_begin, length, cached_timestamp_type, NullS); break; @@ -437,10 +439,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, err: { + THD *thd= current_thd; char buff[128]; - strmake(buff, val_begin, min(length, sizeof(buff)-1)); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), + strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1)); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_TYPE, + ER_THD(thd, ER_WRONG_VALUE_FOR_TYPE), date_time_type, buff, "str_to_date"); } DBUG_RETURN(1); @@ -451,7 +455,7 @@ err: Create a formated date/time value in a string. */ -static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, +static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time, timestamp_type type, MY_LOCALE *locale, String *str) { char intbuff[15]; @@ -465,7 +469,7 @@ static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, if (l_time->neg) str->append('-'); - end= (ptr= format->format.str) + format->format.length; + end= (ptr= format.str) + format.length; for (; ptr != end ; ptr++) { if (*ptr != '%' || ptr+1 == end) @@ -573,7 +577,7 @@ static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, str->append_with_prefill(intbuff, length, 2, '0'); break; case 'j': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !l_time->month || !l_time->year) return 1; length= (uint) (int10_to_str(calc_daynr(l_time->year,l_time->month, l_time->day) - @@ -699,7 +703,7 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, { const char *end=str+length; uint i; - long msec_length= 0; + long field_length= 0; while (str != end && !my_isdigit(cs,*str)) str++; @@ -708,9 +712,10 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, { longlong value; const char *start= str; - for (value=0; str != end && my_isdigit(cs,*str) ; str++) - value= value*LL(10) + (longlong) (*str - '0'); - msec_length= 6 - (str - start); + for (value= 0; str != end && my_isdigit(cs, *str); str++) + value= value*10 + *str - '0'; + if ((field_length= str - start) >= 20) + return true; values[i]= value; while (str != end && !my_isdigit(cs,*str)) str++; @@ -725,8 +730,13 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, } } - if (transform_msec && msec_length > 0) - values[count - 1] *= (long) log_10_int[msec_length]; + if (transform_msec && field_length > 0) + { + if (field_length < 6) + values[count - 1] *= log_10_int[6 - field_length]; + else if (field_length > 6) + values[count - 1] /= log_10_int[field_length - 6]; + } return (str != end); } @@ -1075,7 +1085,7 @@ longlong Item_func_weekday::val_int() return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month, ltime.day), - odbc_type) + test(odbc_type); + odbc_type) + MY_TEST(odbc_type); } void Item_func_dayname::fix_length_and_dec() @@ -1093,7 +1103,7 @@ void Item_func_dayname::fix_length_and_dec() String* Item_func_dayname::val_str(String* str) { DBUG_ASSERT(fixed == 1); - uint weekday=(uint) val_int(); // Always Item_func_daynr() + uint weekday=(uint) val_int(); // Always Item_func_weekday() const char *day_name; uint err; @@ -1297,13 +1307,12 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) interval->neg= my_decimal2seconds(val, &second, &second_part); if (second == LONGLONG_MAX) { - char buff[DECIMAL_MAX_STR_LENGTH]; - int length= sizeof(buff); - decimal2string(val, buff, &length, 0, 0, 0); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + THD *thd= current_thd; + ErrConvDecimal err(val); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", - buff); + ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL", + err.ptr()); return true; } @@ -1477,7 +1486,9 @@ void Item_temporal_func::fix_length_and_dec() (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); collation.set(field_type() == MYSQL_TYPE_STRING ? default_charset() : &my_charset_numeric, - DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); + field_type() == MYSQL_TYPE_STRING ? + DERIVATION_COERCIBLE : DERIVATION_NUMERIC, + MY_REPERTOIRE_ASCII); fix_char_length(char_length); } @@ -1488,12 +1499,67 @@ String *Item_temporal_func::val_str(String *str) } +bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime) +{ + if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */ + return false; + + if (ltime->time_type != MYSQL_TIMESTAMP_TIME) + goto date_or_datetime_value; + + /* Convert TIME to DATE or DATETIME */ + switch (field_type()) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + { + MYSQL_TIME tmp; + if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0)) + return (null_value= true); + *ltime= tmp; + if (field_type() == MYSQL_TYPE_DATE) + datetime_to_date(ltime); + return false; + } + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ + return false; + default: + DBUG_ASSERT(0); + return (null_value= true); + } + +date_or_datetime_value: + /* Convert DATE or DATETIME to TIME, DATE, or DATETIME */ + switch (field_type()) + { + case MYSQL_TYPE_TIME: + datetime_to_time(ltime); + return false; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + date_to_datetime(ltime); + return false; + case MYSQL_TYPE_DATE: + datetime_to_date(ltime); + return false; + case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */ + return false; + default: + DBUG_ASSERT(0); + return (null_value= true); + } + return false; +} + + String *Item_temporal_hybrid_func::val_str_ascii(String *str) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_date(<ime, 0) || + if (get_date(<ime, 0) || fix_temporal_type(<ime) || (null_value= my_TIME_to_str(<ime, str, decimals))) return (String *) 0; @@ -1571,8 +1637,8 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items) { if (decimals > TIME_SECOND_PART_DIGITS) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), decimals, func_name(), - TIME_SECOND_PART_DIGITS); + my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<ulonglong>(decimals), + func_name(), TIME_SECOND_PART_DIGITS); return 1; } return Item_timefunc::fix_fields(thd, items); @@ -1593,7 +1659,7 @@ static void set_sec_part(ulong sec_part, MYSQL_TIME *ltime, Item *item) { ltime->second_part= sec_part; if (item->decimals < TIME_SECOND_PART_DIGITS) - ltime->second_part= sec_part_truncate(ltime->second_part, item->decimals); + my_time_trunc(ltime, item->decimals); } } @@ -1633,8 +1699,8 @@ bool Item_func_now::fix_fields(THD *thd, Item **items) { if (decimals > TIME_SECOND_PART_DIGITS) { - my_error(ER_TOO_BIG_PRECISION, MYF(0), decimals, func_name(), - TIME_SECOND_PART_DIGITS); + my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<ulonglong>(decimals), + func_name(), TIME_SECOND_PART_DIGITS); return 1; } return Item_temporal_func::fix_fields(thd, items); @@ -1737,13 +1803,13 @@ overflow: if (!err) { ErrConvInteger err2(sec, unsigned_flag); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &err2, MYSQL_TIMESTAMP_TIME, NullS); } else { ErrConvString err2(err); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &err2, MYSQL_TIMESTAMP_TIME, NullS); } return 0; @@ -1769,13 +1835,13 @@ void Item_func_date_format::fix_length_and_dec() if (arg1->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; - max_length= format_length(&arg1->str_value) * + max_length= format_length(arg1->val_str(NULL)) * collation.collation->mbmaxlen; } else { fixed_length=0; - max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10 * + max_length=MY_MIN(arg1->max_length, MAX_BLOB_WIDTH) * 10 * collation.collation->mbmaxlen; set_if_smaller(max_length,MAX_BLOB_WIDTH); } @@ -1883,6 +1949,7 @@ uint Item_func_date_format::format_length(const String *format) String *Item_func_date_format::val_str(String *str) { + StringBuffer<64> format_buffer; String *format; MYSQL_TIME l_time; uint size; @@ -1892,7 +1959,7 @@ String *Item_func_date_format::val_str(String *str) if (get_arg0_date(&l_time, is_time_flag)) return 0; - if (!(format = args[1]->val_str(str)) || !format->length()) + if (!(format= args[1]->val_str(&format_buffer)) || !format->length()) goto null_date; if (fixed_length) @@ -1903,18 +1970,13 @@ String *Item_func_date_format::val_str(String *str) if (size < MAX_DATE_STRING_REP_LENGTH) size= MAX_DATE_STRING_REP_LENGTH; - if (format == str) - str= &value; // Save result here + DBUG_ASSERT(format != str); if (str->alloc(size)) goto null_date; - DATE_TIME_FORMAT date_time_format; - date_time_format.format.str= (char*) format->ptr(); - date_time_format.format.length= format->length(); - /* Create the result string */ str->set_charset(collation.collation); - if (!make_date_time(&date_time_format, &l_time, + if (!make_date_time(format->lex_cstring(), &l_time, is_time_format ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATE, locale, str)) @@ -2016,7 +2078,7 @@ void Item_date_add_interval::fix_length_and_dec() enum_field_types arg0_field_type; /* - The field type for the result of an Item_date function is defined as + The field type for the result of an Item_datefunc is defined as follows: - If first arg is a MYSQL_TYPE_DATETIME result is MYSQL_TYPE_DATETIME @@ -2038,12 +2100,12 @@ void Item_date_add_interval::fix_length_and_dec() int_type <= INTERVAL_SECOND_MICROSECOND)) interval_dec= TIME_SECOND_PART_DIGITS; else if (int_type == INTERVAL_SECOND && args[1]->decimals > 0) - interval_dec= min(args[1]->decimals, TIME_SECOND_PART_DIGITS); + interval_dec= MY_MIN(args[1]->decimals, TIME_SECOND_PART_DIGITS); if (arg0_field_type == MYSQL_TYPE_DATETIME || arg0_field_type == MYSQL_TYPE_TIMESTAMP) { - decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); + decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); cached_field_type= MYSQL_TYPE_DATETIME; } else if (arg0_field_type == MYSQL_TYPE_DATE) @@ -2058,20 +2120,18 @@ void Item_date_add_interval::fix_length_and_dec() } else if (arg0_field_type == MYSQL_TYPE_TIME) { - decimals= max(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec); + decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec); if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH) cached_field_type= arg0_field_type; else cached_field_type= MYSQL_TYPE_DATETIME; } else - decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); + decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); Item_temporal_func::fix_length_and_dec(); } -/* Here arg[1] is a Item_interval object */ - bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { INTERVAL interval; @@ -2145,26 +2205,26 @@ void Item_extract::fix_length_and_dec() { maybe_null=1; // If wrong date 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; - case INTERVAL_DAY_SECOND: max_length=13; date_value=0; break; - case INTERVAL_HOUR: max_length=2; date_value=0; break; - case INTERVAL_HOUR_MINUTE: max_length=4; date_value=0; break; - case INTERVAL_HOUR_SECOND: max_length=6; date_value=0; break; - case INTERVAL_MINUTE: max_length=2; date_value=0; break; - case INTERVAL_MINUTE_SECOND: max_length=4; date_value=0; break; - case INTERVAL_SECOND: max_length=2; date_value=0; break; - case INTERVAL_MICROSECOND: max_length=2; date_value=0; break; - case INTERVAL_DAY_MICROSECOND: max_length=20; date_value=0; break; - case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break; - case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break; - case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break; + case INTERVAL_YEAR: set_date_length(4); break; // YYYY + case INTERVAL_YEAR_MONTH: set_date_length(6); break; // YYYYMM + case INTERVAL_QUARTER: set_date_length(2); break; // 1..4 + case INTERVAL_MONTH: set_date_length(2); break; // MM + case INTERVAL_WEEK: set_date_length(2); break; // 0..52 + case INTERVAL_DAY: set_date_length(2); break; // DD + case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh + case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm + case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss + case INTERVAL_HOUR: set_time_length(2); break; // hh + case INTERVAL_HOUR_MINUTE: set_time_length(4); break; // hhmm + case INTERVAL_HOUR_SECOND: set_time_length(6); break; // hhmmss + case INTERVAL_MINUTE: set_time_length(2); break; // mm + case INTERVAL_MINUTE_SECOND: set_time_length(4); break; // mmss + case INTERVAL_SECOND: set_time_length(2); break; // ss + case INTERVAL_MICROSECOND: set_time_length(6); break; // ffffff + case INTERVAL_DAY_MICROSECOND: set_time_length(14); break; // DDhhmmssffffff + case INTERVAL_HOUR_MICROSECOND: set_time_length(12); break; // hhmmssffffff + case INTERVAL_MINUTE_MICROSECOND: set_time_length(10); break; // mmssffffff + case INTERVAL_SECOND_MICROSECOND: set_time_length(8); break; // ssffffff case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */ } } @@ -2179,8 +2239,10 @@ longlong Item_extract::val_int() long neg; int is_time_flag = date_value ? 0 : TIME_TIME_ONLY; - if (get_arg0_date(<ime, is_time_flag)) + // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion + if ((null_value= args[0]->get_date(<ime, is_time_flag))) return 0; + neg= ltime.neg ? -1 : 1; DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0); @@ -2309,105 +2371,124 @@ void Item_char_typecast::print(String *str, enum_query_type query_type) str->append(')'); } + +void Item_char_typecast::check_truncation_with_warn(String *src, uint dstlen) +{ + if (dstlen < src->length()) + { + THD *thd= current_thd; + char char_type[40]; + ErrConvString err(src); + my_snprintf(char_type, sizeof(char_type), "%s(%lu)", + cast_cs == &my_charset_bin ? "BINARY" : "CHAR", + (ulong) cast_length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE, + ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), char_type, + err.ptr()); + } +} + + +String *Item_char_typecast::reuse(String *src, uint32 length) +{ + DBUG_ASSERT(length <= src->length()); + check_truncation_with_warn(src, length); + tmp_value.set(src->ptr(), length, cast_cs); + return &tmp_value; +} + + +/* + Make a copy, to handle conversion or fix bad bytes. +*/ +String *Item_char_typecast::copy(String *str, CHARSET_INFO *strcs) +{ + String_copier_for_item copier(current_thd); + if (copier.copy_with_warn(cast_cs, &tmp_value, strcs, + str->ptr(), str->length(), cast_length)) + { + null_value= 1; // EOM + return 0; + } + check_truncation_with_warn(str, copier.source_end_pos() - str->ptr()); + return &tmp_value; +} + + +uint Item_char_typecast::adjusted_length_with_warn(uint length) +{ + if (length <= current_thd->variables.max_allowed_packet) + return length; + + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED), + cast_cs == &my_charset_bin ? + "cast_as_binary" : func_name(), + thd->variables.max_allowed_packet); + return thd->variables.max_allowed_packet; +} + + String *Item_char_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res; - uint32 length; - if (cast_length != ~0U && - cast_length > current_thd->variables.max_allowed_packet) + if (has_explicit_length()) + cast_length= adjusted_length_with_warn(cast_length); + + if (!(res= args[0]->val_str(str))) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_ALLOWED_PACKET_OVERFLOWED, - ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), - cast_cs == &my_charset_bin ? - "cast_as_binary" : func_name(), - current_thd->variables.max_allowed_packet); - cast_length= current_thd->variables.max_allowed_packet; + null_value= 1; + return 0; } - if (!charset_conversion) + if (cast_cs == &my_charset_bin && + has_explicit_length() && + cast_length > res->length()) { - if (!(res= args[0]->val_str(str))) + // Special case: pad binary value with trailing 0x00 + DBUG_ASSERT(cast_length <= current_thd->variables.max_allowed_packet); + if (res->alloced_length() < cast_length) { - null_value= 1; - return 0; + str_value.alloc(cast_length); + str_value.copy(*res); + res= &str_value; } + bzero((char*) res->ptr() + res->length(), cast_length - res->length()); + res->length(cast_length); + res->set_charset(&my_charset_bin); } else { /* - Convert character set if differ from_cs is 0 in the case where the result set may vary between calls, for example with dynamic columns. */ - uint dummy_errors; - if (!(res= args[0]->val_str(str)) || - tmp_value.copy(res->ptr(), res->length(), - from_cs ? from_cs : res->charset(), - cast_cs, &dummy_errors)) - { - null_value= 1; - return 0; - } - res= &tmp_value; - } - - 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)) - */ - if (cast_length != ~0U) - { - 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", - (ulong) length); - - if (!res->alloced_length()) - { // Don't change const str - str_value= *res; // Not malloced string - res= &str_value; - } - ErrConvString err(res); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), char_type, - err.ptr()); - res->length((uint) length); - } - else if (cast_cs == &my_charset_bin && res->length() < cast_length) + CHARSET_INFO *cs= from_cs ? from_cs : res->charset(); + if (!charset_conversion) { - if (res->alloced_length() < cast_length) + // Try to reuse the original string (if well formed). + MY_STRCOPY_STATUS status; + cs->cset->well_formed_char_length(cs, res->ptr(), res->end(), + cast_length, &status); + if (!status.m_well_formed_error_pos) { - str_value.alloc(cast_length); - str_value.copy(*res); - res= &str_value; + res= reuse(res, status.m_source_end_pos - res->ptr()); } - bzero((char*) res->ptr() + res->length(), cast_length - res->length()); - res->length(cast_length); + goto end; } + // Character set conversion, or bad bytes were found. + if (!(res= copy(res, cs))) + return 0; } - null_value= 0; - if (res->length() > current_thd->variables.max_allowed_packet) - { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_ALLOWED_PACKET_OVERFLOWED, - ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), - cast_cs == &my_charset_bin ? - "cast_as_binary" : func_name(), - current_thd->variables.max_allowed_packet); - null_value= 1; - return 0; - } - return res; +end: + return ((null_value= (res->length() > + adjusted_length_with_warn(res->length())))) ? 0 : res; } @@ -2465,7 +2546,7 @@ bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (get_arg0_time(ltime)) return 1; if (decimals < TIME_SECOND_PART_DIGITS) - ltime->second_part= sec_part_truncate(ltime->second_part, decimals); + my_time_trunc(ltime, decimals); /* MYSQL_TIMESTAMP_TIME value can have non-zero day part, which we should not lose. @@ -2481,6 +2562,7 @@ bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { + fuzzy_date |= sql_mode_for_dates(current_thd); if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) return 1; @@ -2493,15 +2575,15 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { + fuzzy_date |= sql_mode_for_dates(current_thd); if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) return 1; if (decimals < TIME_SECOND_PART_DIGITS) - ltime->second_part= sec_part_truncate(ltime->second_part, decimals); - - if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATETIME)) - return (null_value= 1); + my_time_trunc(ltime, decimals); + DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME); + ltime->time_type= MYSQL_TIMESTAMP_DATETIME; return 0; } @@ -2547,7 +2629,7 @@ err: void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; - decimals= max(args[0]->decimals, args[1]->decimals); + decimals= MY_MAX(args[0]->decimals, args[1]->decimals); /* The field type for the result of an Item_func_add_time function is defined @@ -2567,14 +2649,14 @@ void Item_func_add_time::fix_length_and_dec() is_date) { cached_field_type= MYSQL_TYPE_DATETIME; - decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), - args[1]->temporal_precision(MYSQL_TYPE_TIME)); + decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), + args[1]->temporal_precision(MYSQL_TYPE_TIME)); } else if (arg0_field_type == MYSQL_TYPE_TIME) { cached_field_type= MYSQL_TYPE_TIME; - decimals= max(args[0]->temporal_precision(MYSQL_TYPE_TIME), - args[1]->temporal_precision(MYSQL_TYPE_TIME)); + decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), + args[1]->temporal_precision(MYSQL_TYPE_TIME)); } Item_temporal_func::fix_length_and_dec(); } @@ -2598,16 +2680,18 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) longlong seconds; int l_sign= sign; - if (is_date) // TIMESTAMP function + if (cached_field_type == MYSQL_TYPE_DATETIME) { + // TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP if (get_arg0_date(&l_time1, 0) || args[1]->get_time(&l_time2) || l_time1.time_type == MYSQL_TIMESTAMP_TIME || l_time2.time_type != MYSQL_TIMESTAMP_TIME) return (null_value= 1); } - else // ADDTIME function + else { + // ADDTIME function AND the first argument is TIME if (args[0]->get_time(&l_time1) || args[1]->get_time(&l_time2) || l_time2.time_type == MYSQL_TIMESTAMP_DATETIME) @@ -2632,9 +2716,9 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (!is_time && ltime->neg) return (null_value= 1); - days= (long)(seconds/86400L); + days= (long) (seconds / SECONDS_IN_24H); - calc_time_from_sec(ltime, (long)(seconds%86400L), microseconds); + calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds); ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; @@ -2683,8 +2767,6 @@ void Item_func_add_time::print(String *str, enum_query_type query_type) bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { DBUG_ASSERT(fixed == 1); - longlong seconds; - long microseconds; int l_sign= 1; MYSQL_TIME l_time1,l_time2,l_time3; ErrConvTime str(&l_time3); @@ -2701,36 +2783,11 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (l_time1.neg != l_time2.neg) l_sign= -l_sign; - bzero((char *)&l_time3, sizeof(l_time3)); - - l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign, - &seconds, µseconds); - - /* - For MYSQL_TIMESTAMP_TIME only: - If first argument was negative and diff between arguments - is non-zero we need to swap sign to get proper result. - */ - if (l_time1.neg && (seconds || microseconds)) - l_time3.neg= 1-l_time3.neg; // Swap sign of result - - /* - seconds is longlong, when casted to long it may become a small number - even if the original seconds value was too large and invalid. - as a workaround we limit seconds by a large invalid long number - ("invalid" means > TIME_MAX_SECOND) - */ - set_if_smaller(seconds, INT_MAX32); - - calc_time_from_sec(&l_time3, (long) seconds, microseconds); - - if ((fuzzy_date & TIME_NO_ZERO_DATE) && (seconds == 0) && - (microseconds == 0)) + if (calc_time_diff(&l_time1, &l_time2, l_sign, &l_time3, fuzzy_date)) return (null_value= 1); *ltime= l_time3; return (null_value= adjust_time_range_with_warn(ltime, decimals)); - } /** @@ -2772,7 +2829,7 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) char buf[28]; char *ptr= longlong10_to_str(hour.value(), buf, hour.is_unsigned() ? 10 : -10); int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, buf, len, MYSQL_TIMESTAMP_TIME, NullS); } @@ -2808,8 +2865,12 @@ longlong Item_func_timestamp_diff::val_int() int neg= 1; null_value= 0; - if (args[0]->get_date(<ime1, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE) || - args[1]->get_date(<ime2, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)) + if (args[0]->get_date_with_conversion(<ime1, + TIME_NO_ZERO_DATE | + TIME_NO_ZERO_IN_DATE) || + args[1]->get_date_with_conversion(<ime2, + TIME_NO_ZERO_DATE | + TIME_NO_ZERO_IN_DATE)) goto null_date; if (calc_time_diff(<ime2,<ime1, 1, @@ -2879,9 +2940,9 @@ longlong Item_func_timestamp_diff::val_int() case INTERVAL_MONTH: return months*neg; case INTERVAL_WEEK: - return seconds/86400L/7L*neg; + return seconds / SECONDS_IN_24H / 7L * neg; case INTERVAL_DAY: - return seconds/86400L*neg; + return seconds / SECONDS_IN_24H * neg; case INTERVAL_HOUR: return seconds/3600L*neg; case INTERVAL_MINUTE: @@ -3090,7 +3151,7 @@ void Item_func_str_to_date::fix_length_and_dec() } cached_field_type= MYSQL_TYPE_DATETIME; - decimals= NOT_FIXED_DEC; + decimals= TIME_SECOND_PART_DIGITS; if ((const_item= args[1]->const_item())) { char format_buff[64]; @@ -3143,7 +3204,7 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), ltime, cached_timestamp_type, 0, "datetime", - fuzzy_date)) + fuzzy_date | sql_mode_for_dates(current_thd))) return (null_value=1); if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day) { |