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.cc516
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(&ltime,0))
+ if (get_arg0_date(&ltime, 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(&ltime,0))
+ if (get_arg0_date(&ltime,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(&ltime,1);
+ (void) get_arg0_date(&ltime, 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(&ltime,1);
+ (void) get_arg0_date(&ltime, 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(&ltime,1);
+ (void) get_arg0_date(&ltime, 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(&ltime,0))
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE))
return 0;
return (longlong) calc_week(&ltime,
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(&ltime,0))
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE))
return 0;
week= calc_week(&ltime,
(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(&ltime, 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(&ltime,1);
+ (void) get_arg0_date(&ltime, 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(&ltime);
+ buff_length= (uint) my_datetime_to_str(&ltime, 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(&ltime);
+ return (longlong) TIME_to_ulonglong_datetime(&ltime);
+}
+
+
+double Item_func_sysdate_local::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ store_now_in_TIME(&ltime);
+ return (longlong) TIME_to_ulonglong_datetime(&ltime);
+}
+
+
+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(&ltime);
+ *res= ltime;
+ return 0;
+}
+
+
+int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions)
+{
+ store_now_in_TIME(&ltime);
+ to->set_notnull();
+ to->store_time(&ltime, 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, &not_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, &ltime->year, &ltime->month,
&ltime->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,&ltime->year,&ltime->month,&ltime->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(&ltime,0))
+ if (Item_date_add_interval::get_date(&ltime, 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(&ltime,0))
+ if (Item_date_add_interval::get_date(&ltime, 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(&ltime,1))
+ if (get_arg0_date(&ltime, 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(&ltime, 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(&ltime,1) &&
+ if (!get_arg0_date(&ltime, TIME_FUZZY_DATE) &&
!make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME,
&ltime, 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(&ltime,1) && !str->alloc(11))
+ if (!get_arg0_date(&ltime, TIME_FUZZY_DATE) && !str->alloc(11))
{
make_date((DATE_TIME_FORMAT *) 0, &ltime, 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(&ltime1, TIME_NO_ZERO_DATE) ||
+ args[1]->get_date(&ltime2, TIME_NO_ZERO_DATE))
+ goto null_date;
+
+ if (calc_time_diff(&ltime2,&ltime1, 1,
+ &seconds, &microseconds))
+ 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)
{