Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
@file
@brief
This file defines all time functions
@todo
Move month and days to language files
*/
#include "sql_priv.h"
It is necessary to include set_var.h instead of item.h because there
are dependencies on include order for set_var.h and item.h. This
will be resolved later.
*/
#include "sql_class.h"
#include "set_var.h"
#include "sql_locale.h"
#include "strfunc.h"
#include "sql_time.h"
#include "sql_class.h"
#include <m_ctype.h>
#include <time.h>
using std::min;
using std::max;
#define MAX_DAY_NUMBER 3652424L
Check and adjust a time value with a warning.
@param ltime Time variable.
@param decimals Precision.
@retval True on error, false of success.
*/
static void
adjust_time_range_with_warn(MYSQL_TIME *ltime, uint8 decimals)
{
if (check_time_range_quick(ltime))
{
int warning= 0;
make_truncated_value_warning(ErrConvString(ltime, decimals),
MYSQL_TIMESTAMP_TIME);
adjust_time_range(ltime, &warning);
}
}
Convert seconds to MYSQL_TIME value with overflow checking.
SYNOPSIS:
sec_to_time()
seconds number of seconds
ltime output MYSQL_TIME value
DESCRIPTION
If the 'seconds' argument is inside MYSQL_TIME data range, convert it to a
corresponding value.
Otherwise, truncate the resulting value to the nearest endpoint.
Note: Truncation in this context means setting the result to the MAX/MIN
value of TIME type if value is outside the allowed range.
If the number of decimals exceeds what is supported, the value
is rounded to the supported number of decimals.
RETURN
1 if the value was truncated during conversion
0 otherwise
*/
static bool sec_to_time(lldiv_t seconds, MYSQL_TIME *ltime)
{
int warning= 0;
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
if (seconds.quot < 0 || seconds.rem < 0)
{
ltime->neg= 1;
seconds.quot= -seconds.quot;
seconds.rem= -seconds.rem;
}
if (seconds.quot > TIME_MAX_VALUE_SECONDS)
{
set_max_hhmmss(ltime);
return true;
}
ltime->hour= (uint) (seconds.quot / 3600);
uint sec= (uint) (seconds.quot % 3600);
ltime->minute= sec / 60;
ltime->second= sec % 60;
time_add_nanoseconds_with_round(ltime, seconds.rem, &warning);
adjust_time_range(ltime, &warning);
return warning ? true : false;
}
Date formats corresponding to compound %r and %T conversion specifiers
Note: We should init at least first element of "positions" array
(first member) or hpux11 compiler will die horribly.
*/
static DATE_TIME_FORMAT time_ampm_format= {{0}, '\0', 0,
{(char *)"%I:%i:%S %p", 11}};
static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
{(char *)"%H:%i:%S", 8}};
Extract datetime value to MYSQL_TIME struct from string value
according to format string.
@param format date/time format specification
@param val String to decode
@param length Length of string
@param l_time Store result here
@param cached_timestamp_type It uses to get an appropriate warning
in the case when the value is truncated.
@param sub_pattern_end if non-zero then we are parsing string which
should correspond compound specifier (like %T or
%r) and this parameter is pointer to place where
pointer to end of string matching this specifier
should be stored.
@note
Possibility to parse strings matching to patterns equivalent to compound
specifiers is mainly intended for use from inside of this function in
order to understand %T and %r conversion specifiers, so number of
conversion specifiers that can be used in such sub-patterns is limited.
Also most of checks are skipped in this case.
@note
If one adds new format specifiers to this function he should also
consider adding them to Item_func_str_to_date::fix_from_format().
@retval
0 ok
@retval
1 error
*/
static bool extract_date_time(DATE_TIME_FORMAT *format,
const char *val, uint length, MYSQL_TIME *l_time,
timestamp_type cached_timestamp_type,
const char **sub_pattern_end,
const char *date_time_type)
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
int error= 0;
int strict_week_number_year= -1;
int frac_part;
bool usa_time= 0;
bool UNINIT_VAR(sunday_first_n_first_week_non_iso);
bool UNINIT_VAR(strict_week_number);
bool UNINIT_VAR(strict_week_number_year_type);
const char *val_begin= val;
const char *val_end= val + length;
const char *ptr= format->format.str;
const char *end= ptr + format->format.length;
const CHARSET_INFO *cs= &my_charset_bin;
DBUG_ENTER("extract_date_time");
if (!sub_pattern_end)
memset(l_time, 0, sizeof(*l_time));
for (; ptr != end && val != val_end; ptr++)
{
if ((val+= cs->cset->scan(cs, val, val_end, MY_SEQ_SPACES)) >= val_end)
break;
if (*ptr == '%' && ptr+1 != end)
{
int val_len;
char *tmp;
error= 0;
val_len= (uint) (val_end - val);
switch (*++ptr) {
case 'Y':
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 + 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);
break;
case 'm':
case 'c':
tmp= (char*) val + MY_MIN(2, val_len);
l_time->month= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'M':
if ((l_time->month= check_word(my_locale_en_US.month_names,
val, val_end, &val)) <= 0)
goto err;
break;
case 'b':
if ((l_time->month= check_word(my_locale_en_US.ab_month_names,
val, val_end, &val)) <= 0)
goto err;
break;
case 'd':
case 'e':
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 + MY_MIN(2, val_len);
l_time->day= (int) my_strtoll10(val, &tmp, &error);
val= tmp + MY_MIN((int) (val_end-tmp), 2);
break;
case 'h':
case 'I':
case 'l':
usa_time= 1;
case 'k':
case 'H':
tmp= (char*) val + MY_MIN(2, val_len);
l_time->hour= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'i':
tmp= (char*) val + MY_MIN(2, val_len);
l_time->minute= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 's':
case 'S':
tmp= (char*) val + MY_MIN(2, val_len);
l_time->second= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'f':
tmp= (char*) val_end;
if (tmp - val > 6)
tmp= (char*) val + 6;
l_time->second_part= (int) my_strtoll10(val, &tmp, &error);
frac_part= 6 - (int) (tmp - val);
if (frac_part > 0)
l_time->second_part*= (ulong) log_10_int[frac_part];
val= tmp;
break;
case 'p':
if (val_len < 2 || ! usa_time)
goto err;
if (!my_strnncoll(&my_charset_latin1,
(const uchar *) val, 2,
(const uchar *) "PM", 2))
daypart= 12;
else if (my_strnncoll(&my_charset_latin1,
(const uchar *) val, 2,
(const uchar *) "AM", 2))
goto err;
val+= 2;
break;
case 'W':
if ((weekday= check_word(my_locale_en_US.day_names, val, val_end, &val)) <= 0)
goto err;
break;
case 'a':
if ((weekday= check_word(my_locale_en_US.ab_day_names, val, val_end, &val)) <= 0)
goto err;
break;
case 'w':
tmp= (char*) val + 1;
if ((weekday= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
weekday >= 7)
goto err;
if (!weekday)
weekday= 7;
val= tmp;
break;
case 'j':
tmp= (char*) val + MY_MIN(val_len, 3);
yearday= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'V':
case 'U':
case 'v':
case 'u':
sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V');
strict_week_number= (*ptr=='V' || *ptr=='v');
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)
goto err;
val= tmp;
break;
case 'X':
case 'x':
strict_week_number_year_type= (*ptr=='X');
tmp= (char*) val + MY_MIN(4, val_len);
strict_week_number_year= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'r':
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;
case 'T':
if (extract_date_time(&time_24hrs_format, val,
(uint)(val_end - val), l_time,
cached_timestamp_type, &val, "time"))
DBUG_RETURN(1);
break;
case '.':
while (my_ispunct(cs, *val) && val != val_end)
val++;
break;
case '@':
while (my_isalpha(cs, *val) && val != val_end)
val++;
break;
case '#':
while (my_isdigit(cs, *val) && val != val_end)
val++;
break;
default:
goto err;
}
if (error)
goto err;
}
else if (!my_isspace(cs, *ptr))
{
if (*val != *ptr)
goto err;
val++;
}
}
if (usa_time)
{
if (l_time->hour > 12 || l_time->hour < 1)
goto err;
l_time->hour= l_time->hour%12+daypart;
}
If we are recursively called for parsing string matching compound
specifiers we are already done.
*/
if (sub_pattern_end)
{
*sub_pattern_end= val;
DBUG_RETURN(0);
}
if (yearday > 0)
{
uint days;
days= calc_daynr(l_time->year,1,1) + yearday - 1;
if (days <= 0 || days > MAX_DAY_NUMBER)
goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (week_number >= 0 && weekday)
{
int days;
uint weekday_b;
%V,%v require %X,%x resprectively,
%U,%u should be used with %Y and not %X or %x
*/
if ((strict_week_number &&
(strict_week_number_year < 0 ||
strict_week_number_year_type != sunday_first_n_first_week_non_iso)) ||
(!strict_week_number && strict_week_number_year >= 0))
goto err;
days= calc_daynr((strict_week_number ? strict_week_number_year :
l_time->year),
1, 1);
weekday_b= calc_weekday(days, sunday_first_n_first_week_non_iso);
Below we are going to sum:
1) number of days since year 0 till 1st day of 1st week of this year
2) number of days between 1st week and our week
3) and position of our day in the week
*/
if (sunday_first_n_first_week_non_iso)
{
days+= ((weekday_b == 0) ? 0 : 7) - weekday_b +
(week_number - 1) * 7 +
weekday % 7;
}
else
{
days+= ((weekday_b <= 3) ? 0 : 7) - weekday_b +
(week_number - 1) * 7 +
(weekday - 1);
}
if (days <= 0 || days > MAX_DAY_NUMBER)
goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 ||
l_time->minute > 59 || l_time->second > 59)
goto err;
if (val != val_end)
{
do
{
if (!my_isspace(&my_charset_latin1,*val))
{
make_truncated_value_warning(ErrConvString(val_begin, length),
cached_timestamp_type);
break;
}
} while (++val != val_end);
}
DBUG_RETURN(0);
err:
{
char buff[128];
strmake(buff, val_begin, min<size_t>(length, sizeof(buff)-1));
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
date_time_type, buff, "str_to_date");
}
DBUG_RETURN(1);
}
Create a formated date/time value in a string.
*/
bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
timestamp_type type, String *str)
{
char intbuff[15];
uint hours_i;
uint weekday;
ulong length;
const char *ptr, *end;
THD *thd= current_thd;
MY_LOCALE *locale= thd->variables.lc_time_names;
str->length(0);
if (l_time->neg)
str->append('-');
end= (ptr= format->format.str) + format->format.length;
for (; ptr != end ; ptr++)
{
if (*ptr != '%' || ptr+1 == end)
str->append(*ptr);
else
{
switch (*++ptr) {
case 'M':
if (!l_time->month)
return 1;
str->append(locale->month_names->type_names[l_time->month-1],
(uint) strlen(locale->month_names->type_names[l_time->month-1]),
system_charset_info);
break;
case 'b':
if (!l_time->month)
return 1;
str->append(locale->ab_month_names->type_names[l_time->month-1],
(uint) strlen(locale->ab_month_names->type_names[l_time->month-1]),
system_charset_info);
break;
case 'W':
if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year))
return 1;
weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day),0);
str->append(locale->day_names->type_names[weekday],
(uint) strlen(locale->day_names->type_names[weekday]),
system_charset_info);
break;
case 'a':
if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year))
return 1;
weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day),0);
str->append(locale->ab_day_names->type_names[weekday],
(uint) strlen(locale->ab_day_names->type_names[weekday]),
system_charset_info);
break;
case 'D':
if (type == MYSQL_TIMESTAMP_TIME)
return 1;
length= (uint) (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(STRING_WITH_LEN("th"));
else
{
switch (l_time->day %10) {
case 1:
str->append(STRING_WITH_LEN("st"));
break;
case 2:
str->append(STRING_WITH_LEN("nd"));
break;
case 3:
str->append(STRING_WITH_LEN("rd"));
break;
default:
str->append(STRING_WITH_LEN("th"));
break;
}
}
break;
case 'Y':
length= (uint) (int10_to_str(l_time->year, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 4, '0');
break;
case 'y':
length= (uint) (int10_to_str(l_time->year%100, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'm':
length= (uint) (int10_to_str(l_time->month, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'c':
length= (uint) (int10_to_str(l_time->month, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 1, '0');
break;
case 'd':
length= (uint) (int10_to_str(l_time->day, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'e':
length= (uint) (int10_to_str(l_time->day, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 1, '0');
break;
case 'f':
length= (uint) (int10_to_str(l_time->second_part, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 6, '0');
break;
case 'H':
length= (uint) (int10_to_str(l_time->hour, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'h':
case 'I':
hours_i= (l_time->hour%24 + 11)%12+1;
length= (uint) (int10_to_str(hours_i, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'i':
length= (uint) (int10_to_str(l_time->minute, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'j':
if (type == MYSQL_TIMESTAMP_TIME)
return 1;
length= (uint) (int10_to_str(calc_daynr(l_time->year,l_time->month,
l_time->day) -
calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 3, '0');
break;
case 'k':
length= (uint) (int10_to_str(l_time->hour, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 1, '0');
break;
case 'l':
hours_i= (l_time->hour%24 + 11)%12+1;
length= (uint) (int10_to_str(hours_i, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 1, '0');
break;
case 'p':
hours_i= l_time->hour%24;
str->append(hours_i < 12 ? "AM" : "PM",2);
break;
case 'r':
length= sprintf(intbuff, ((l_time->hour % 24) < 12) ?
"%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
(l_time->hour+11)%12+1,
l_time->minute,
l_time->second);
str->append(intbuff, length);
break;
case 'S':
case 's':
length= (uint) (int10_to_str(l_time->second, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'T':
length= sprintf(intbuff, "%02d:%02d:%02d",
l_time->hour, l_time->minute, l_time->second);
str->append(intbuff, length);
break;
case 'U':
case 'u':
{
uint year;
if (type == MYSQL_TIMESTAMP_TIME)
return 1;
length= (uint) (int10_to_str(calc_week(l_time,
(*ptr) == 'U' ?
WEEK_FIRST_WEEKDAY : WEEK_MONDAY_FIRST,
&year),
intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
}
break;
case 'v':
case 'V':
{
uint year;
if (type == MYSQL_TIMESTAMP_TIME)
return 1;
length= (uint) (int10_to_str(calc_week(l_time,
((*ptr) == 'V' ?
(WEEK_YEAR | WEEK_FIRST_WEEKDAY) :
(WEEK_YEAR | WEEK_MONDAY_FIRST)),
&year),
intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 2, '0');
}
break;
case 'x':
case 'X':
{
uint year;
if (type == MYSQL_TIMESTAMP_TIME)
return 1;
(void) calc_week(l_time,
((*ptr) == 'X' ?
WEEK_YEAR | WEEK_FIRST_WEEKDAY :
WEEK_YEAR | WEEK_MONDAY_FIRST),
&year);
length= (uint) (int10_to_str(year, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 4, '0');
}
break;
case 'w':
if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year))
return 1;
weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day),1);
length= (uint) (int10_to_str(weekday, intbuff, 10) - intbuff);
str->append_with_prefill(intbuff, length, 1, '0');
break;
default:
str->append(*ptr);
break;
}
}
}
return 0;
}
void Item_date_literal::print(String *str, enum_query_type query_type)
{
str->append("DATE'");
str->append(cached_time.cptr());
str->append('\'');
}
void Item_datetime_literal::print(String *str, enum_query_type query_type)
{
str->append("TIMESTAMP'");
str->append(cached_time.cptr());
str->append('\'');
}
void Item_time_literal::print(String *str, enum_query_type query_type)
{
str->append("TIME'");
str->append(cached_time.cptr());
str->append('\'');
}
longlong Item_func_period_add::val_int()
{
DBUG_ASSERT(fixed == 1);
ulong period=(ulong) args[0]->val_int();
int months=(int) args[1]->val_int();
if ((null_value=args[0]->null_value || args[1]->null_value) ||
period == 0L)
return 0;
return (longlong)
convert_month_to_period((uint) ((int) convert_period_to_month(period)+
months));
}
longlong Item_func_period_diff::val_int()
{
DBUG_ASSERT(fixed == 1);
ulong period1=(ulong) args[0]->val_int();
ulong period2=(ulong) args[1]->val_int();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0;
return (longlong) ((long) convert_period_to_month(period1)-
(long) convert_period_to_month(period2));
}
longlong Item_func_to_days::val_int()
{
return (longlong) 0;
}
longlong Item_func_to_seconds::val_int()
{
return 0;
}
Get information about this Item tree monotonicity
SYNOPSIS
Item_func_to_days::get_monotonicity_info()
DESCRIPTION
Get information about monotonicity of the function represented by this item
tree.
RETURN
See enum_monotonicity_info.
*/
enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const
{
if (args[0]->type() == Item::FIELD_ITEM)
{
if (args[0]->field_type() == MYSQL_TYPE_DATE)
return MONOTONIC_STRICT_INCREASING_NOT_NULL;
if (args[0]->field_type() == MYSQL_TYPE_DATETIME)
return MONOTONIC_INCREASING_NOT_NULL;
}
return NON_MONOTONIC;
}
enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const
{
if (args[0]->type() == Item::FIELD_ITEM)
{
if (args[0]->field_type() == MYSQL_TYPE_DATE ||
args[0]->field_type() == MYSQL_TYPE_DATETIME)
return MONOTONIC_STRICT_INCREASING_NOT_NULL;
}
return NON_MONOTONIC;
}
longlong Item_func_dayofyear::val_int()
{
return 0;
}
longlong Item_func_dayofmonth::val_int()
{
return 0;
}
longlong Item_func_month::val_int()
{
return 0;
}
String* Item_func_monthname::val_str(String* str)
{
return 0;
}
Returns the quarter of the year.
*/
longlong Item_func_quarter::val_int()
{
return 0;
}
longlong Item_func_hour::val_int()
{
return 0;
}
longlong Item_func_minute::val_int()
{
return 0;
}
Returns the second in time_exp in the range of 0 - 59.
*/
longlong Item_func_second::val_int()
{
return 0;
}
uint week_mode(uint mode)
{
uint week_format= (mode & 7);
if (!(week_format & WEEK_MONDAY_FIRST))
week_format^= WEEK_FIRST_WEEKDAY;
return week_format;
}
@verbatim
The bits in week_format(for calc_week() function) has the following meaning:
WEEK_MONDAY_FIRST (0) If not set Sunday is first day of week
If set Monday is first day of week
WEEK_YEAR (1) If not set Week is in range 0-53
Week 0 is returned for the the last week of the previous year (for
a date at start of january) In this case one can get 53 for the
first week of next year. This flag ensures that the week is
relevant for the given year. Note that this flag is only
releveant if WEEK_JANUARY is not set.
If set Week is in range 1-53.
In this case one may get week 53 for a date in January (when
the week is that last week of previous year) and week 1 for a
date in December.
WEEK_FIRST_WEEKDAY (2) If not set Weeks are numbered according
to ISO 8601:1988
If set The week that contains the first
'first-day-of-week' is week 1.
ISO 8601:1988 means that if the week containing January 1 has
four or more days in the new year, then it is week 1;
Otherwise it is the last week of the previous year, and the
next week is week 1.
@endverbatim
*/
longlong Item_func_week::val_int()
{
return 0;
}
longlong Item_func_yearweek::val_int()
{
return 0;
}
longlong Item_func_weekday::val_int()
{
return 0;
}
String* Item_func_dayname::val_str(String* str)
{
return (String*) 0;
}
longlong Item_func_year::val_int()
{
return 0 ;
}
Get information about this Item tree monotonicity
SYNOPSIS
Item_func_year::get_monotonicity_info()
DESCRIPTION
Get information about monotonicity of the function represented by this item
tree.
RETURN
See enum_monotonicity_info.
*/
enum_monotonicity_info Item_func_year::get_monotonicity_info() const
{
if (args[0]->type() == Item::FIELD_ITEM &&
(args[0]->field_type() == MYSQL_TYPE_DATE ||
args[0]->field_type() == MYSQL_TYPE_DATETIME))
return MONOTONIC_INCREASING;
return NON_MONOTONIC;
}
enum_monotonicity_info Item_func_unix_timestamp::get_monotonicity_info() const
{
if (args[0]->type() == Item::FIELD_ITEM &&
(args[0]->field_type() == MYSQL_TYPE_TIMESTAMP))
return MONOTONIC_INCREASING;
return NON_MONOTONIC;
}
void MYSQL_TIME_cache::set_time(MYSQL_TIME *ltime, uint8 dec_arg)
{
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME);
time= *ltime;
time_packed= TIME_to_longlong_time_packed(&time);
dec= dec_arg;
reset_string();
}
void MYSQL_TIME_cache::set_date(MYSQL_TIME *ltime)
{
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE);
time= *ltime;
time_packed= TIME_to_longlong_date_packed(&time);
dec= 0;
reset_string();
}
void MYSQL_TIME_cache::set_datetime(MYSQL_TIME *ltime, uint8 dec_arg)
{
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
time= *ltime;
time_packed= TIME_to_longlong_datetime_packed(&time);
dec= dec_arg;
reset_string();
}
void MYSQL_TIME_cache::cache_string()
{
DBUG_ASSERT(time.time_type != MYSQL_TIMESTAMP_NONE);
if (string_length == 0)
string_length= my_TIME_to_str(&time, string_buff, decimals());
}
const char *MYSQL_TIME_cache::cptr()
{
cache_string();
return string_buff;
}
bool MYSQL_TIME_cache::get_date(MYSQL_TIME *ltime, uint fuzzydate) const
{
int warnings;
get_TIME(ltime);
return check_date(ltime, non_zero_date(ltime), fuzzydate, &warnings);
}
String *MYSQL_TIME_cache::val_str(String *str)
{
cache_string();
str->set(string_buff, string_length, &my_charset_latin1);
return str;
}
'interval_names' reflects the order of the enumeration interval_type.
See item_timefunc.h
*/
static const char *interval_names[]=
{
"year", "quarter", "month", "week", "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"
};
void Item_date_add_interval::print(String *str, enum_query_type query_type)
{
str->append('(');
args[0]->print(str, query_type);
str->append(date_sub_interval?" - interval ":" + interval ");
args[1]->print(str, query_type);
str->append(' ');
str->append(interval_names[int_type]);
str->append(')');
}
void Item_extract::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("extract("));
str->append(interval_names[int_type]);
str->append(STRING_WITH_LEN(" from "));
args[0]->print(str, query_type);
str->append(')');
}
void Item_datetime_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(cast_type());
if (decimals)
str->append_parenthesized(decimals);
str->append(')');
}
void Item_time_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(cast_type());
if (decimals)
str->append_parenthesized(decimals);
str->append(')');
}
void Item_date_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(cast_type());
str->append(')');
}
void Item_func_add_time::print(String *str, enum_query_type query_type)
{
if (is_date)
{
DBUG_ASSERT(sign > 0);
str->append(STRING_WITH_LEN("timestamp("));
}
else
{
if (sign > 0)
str->append(STRING_WITH_LEN("addtime("));
else
str->append(STRING_WITH_LEN("subtime("));
}
args[0]->print(str, query_type);
str->append(',');
args[1]->print(str, query_type);
str->append(')');
}
void Item_func_timestamp_diff::print(String *str, enum_query_type query_type)
{
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, query_type);
}
str->append(')');
}
String *Item_func_get_format::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
const char *format_name;
KNOWN_DATE_TIME_FORMAT *format;
String *val= args[0]->val_str_ascii(str);
ulong val_len;
if ((null_value= args[0]->null_value))
return 0;
val_len= val->length();
for (format= &known_date_time_formats[0];
(format_name= format->format_name);
format++)
{
uint format_name_len;
format_name_len= (uint) strlen(format_name);
if (val_len == format_name_len &&
!my_strnncoll(&my_charset_latin1,
(const uchar *) val->ptr(), val_len,
(const uchar *) format_name, val_len))
{
const char *format_str= get_date_time_format_str(format, type);
str->set(format_str, (uint) strlen(format_str), &my_charset_numeric);
return str;
}
}
null_value= 1;
return 0;
}
void Item_func_get_format::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
switch (type) {
case MYSQL_TIMESTAMP_DATE:
str->append(STRING_WITH_LEN("DATE, "));
break;
case MYSQL_TIMESTAMP_DATETIME:
str->append(STRING_WITH_LEN("DATETIME, "));
break;
case MYSQL_TIMESTAMP_TIME:
str->append(STRING_WITH_LEN("TIME, "));
break;
default:
DBUG_ASSERT(0);
}
args[0]->print(str, query_type);
str->append(')');
}
Set type of datetime value (DATE/TIME/...) which will be produced
according to format string.
@param format format string
@param length length of format string
@note
We don't process day format's characters('D', 'd', 'e') because day
may be a member of all date/time types.
@note
Format specifiers supported by this function should be in sync with
specifiers supported by extract_date_time() function.
*/
void Item_func_str_to_date::fix_from_format(const char *format, uint length)
{
const char *time_part_frms= "HISThiklrs";
const char *date_part_frms= "MVUXYWabcjmvuxyw";
bool date_part_used= 0, time_part_used= 0, frac_second_used= 0;
const char *val= format;
const char *end= format + length;
for (; val != end && val != end; val++)
{
if (*val == '%' && val + 1 != end)
{
val++;
if (*val == 'f')
frac_second_used= time_part_used= 1;
else if (!time_part_used && strchr(time_part_frms, *val))
time_part_used= 1;
else if (!date_part_used && strchr(date_part_frms, *val))
date_part_used= 1;
if (date_part_used && frac_second_used)
{
frac_second_used implies time_part_used, and thus we already
have all types of date-time components and can end our search.
*/
cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME;
cached_field_type= MYSQL_TYPE_DATETIME;
fix_length_and_dec_and_charset_datetime(MAX_DATETIME_WIDTH,
DATETIME_MAX_DECIMALS);
return;
}
}
}
if (frac_second_used)
{
cached_timestamp_type= MYSQL_TIMESTAMP_TIME;
cached_field_type= MYSQL_TYPE_TIME;
fix_length_and_dec_and_charset_datetime(MAX_TIME_FULL_WIDTH,
DATETIME_MAX_DECIMALS);
}
else if (time_part_used)
{
if (date_part_used)
{
cached_timestamp_type= MYSQL_TIMESTAMP_DATETIME;
cached_field_type= MYSQL_TYPE_DATETIME;
fix_length_and_dec_and_charset_datetime(MAX_DATETIME_WIDTH, 0);
}
else
{
cached_timestamp_type= MYSQL_TIMESTAMP_TIME;
cached_field_type= MYSQL_TYPE_TIME;
fix_length_and_dec_and_charset_datetime(MAX_TIME_WIDTH, 0);
}
}
else
{
cached_timestamp_type= MYSQL_TIMESTAMP_DATE;
cached_field_type= MYSQL_TYPE_DATE;
fix_length_and_dec_and_charset_datetime(MAX_DATE_WIDTH, 0);
}
}