summaryrefslogtreecommitdiff
path: root/sql/item_timefunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r--sql/item_timefunc.cc469
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(&ltime, 0) ||
+ if (get_date(&ltime, 0) || fix_temporal_type(&ltime) ||
(null_value= my_TIME_to_str(&ltime, 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(&ltime, is_time_flag))
+ // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion
+ if ((null_value= args[0]->get_date(&ltime, 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, &microseconds);
-
- /*
- 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(&ltime1, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE) ||
- args[1]->get_date(&ltime2, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ if (args[0]->get_date_with_conversion(&ltime1,
+ TIME_NO_ZERO_DATE |
+ TIME_NO_ZERO_IN_DATE) ||
+ args[1]->get_date_with_conversion(&ltime2,
+ TIME_NO_ZERO_DATE |
+ TIME_NO_ZERO_IN_DATE))
goto null_date;
if (calc_time_diff(&ltime2,&ltime1, 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)
{