summaryrefslogtreecommitdiff
path: root/sql/sql_time.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_time.cc')
-rw-r--r--sql/sql_time.cc238
1 files changed, 199 insertions, 39 deletions
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index c5c65391758..b55b1d76b99 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -17,11 +17,11 @@
/* Functions to handle date and time */
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED by other includes
#include "sql_time.h"
-#include "tztime.h" // struct Time_zone
-#include "sql_class.h" // THD, MODE_INVALID_DATES, MODE_NO_ZERO_DATE
+#include "tztime.h" // struct Time_zone
+#include "sql_class.h" // THD
#include <m_ctype.h>
@@ -106,9 +106,9 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
uint days;
ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
ulong first_daynr=calc_daynr(l_time->year,1,1);
- bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
- bool week_year= test(week_behaviour & WEEK_YEAR);
- bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
+ bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST);
+ bool week_year= MY_TEST(week_behaviour & WEEK_YEAR);
+ bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY);
uint weekday=calc_weekday(first_daynr, !monday_first);
*year=l_time->year;
@@ -218,11 +218,11 @@ bool
check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
timestamp_type ts_type)
{
- int dummy_warnings;
- if (check_date(ltime, fuzzy_date, &dummy_warnings))
+ int unused;
+ if (check_date(ltime, fuzzy_date, &unused))
{
ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
}
@@ -239,7 +239,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
if (check_time_range(ltime, dec, &warnings))
return true;
if (warnings)
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, MYSQL_TIMESTAMP_TIME, NullS);
return false;
}
@@ -279,9 +279,9 @@ to_ascii(CHARSET_INFO *cs,
/* Character set-aware version of str_to_time() */
-timestamp_type
+bool
str_to_time(CHARSET_INFO *cs, const char *str,uint length,
- MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning)
+ MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
{
char cnv[32];
if ((cs->state & MY_CS_NONASCII) != 0)
@@ -289,14 +289,14 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length,
length= to_ascii(cs, str, length, cnv, sizeof(cnv));
str= cnv;
}
- return str_to_time(str, length, l_time, fuzzydate, warning);
+ return str_to_time(str, length, l_time, fuzzydate, status);
}
/* Character set-aware version of str_to_datetime() */
-timestamp_type str_to_datetime(CHARSET_INFO *cs,
- const char *str, uint length,
- MYSQL_TIME *l_time, ulonglong flags, int *was_cut)
+bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong flags,
+ MYSQL_TIME_STATUS *status)
{
char cnv[32];
if ((cs->state & MY_CS_NONASCII) != 0)
@@ -304,7 +304,7 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs,
length= to_ascii(cs, str, length, cnv, sizeof(cnv));
str= cnv;
}
- return str_to_datetime(str, length, l_time, flags, was_cut);
+ return str_to_datetime(str, length, l_time, flags, status);
}
@@ -316,26 +316,24 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs,
See description of str_to_datetime() for more information.
*/
-timestamp_type
+bool
str_to_datetime_with_warn(CHARSET_INFO *cs,
const char *str, uint length, MYSQL_TIME *l_time,
ulonglong flags)
{
- int was_cut;
+ MYSQL_TIME_STATUS status;
THD *thd= current_thd;
- timestamp_type ts_type;
-
- ts_type= str_to_datetime(cs, str, length, l_time,
- (flags | (sql_mode_for_dates(thd))),
- &was_cut);
- if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
- make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
+ if (ret_val || status.warnings)
+ make_truncated_value_warning(thd,
+ ret_val ? Sql_condition::WARN_LEVEL_WARN :
+ Sql_condition::time_warn_level(status.warnings),
str, length, flags & TIME_TIME_ONLY ?
- MYSQL_TIMESTAMP_TIME : ts_type, NullS);
+ MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
DBUG_EXECUTE_IF("str_to_datetime_warn",
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_YES, str););
- return ts_type;
+ return ret_val;
}
@@ -386,7 +384,7 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
if (res < 0 || have_warnings)
{
make_truncated_value_warning(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN, str,
+ Sql_condition::WARN_LEVEL_WARN, str,
res < 0 ? MYSQL_TIMESTAMP_ERROR
: mysql_type_to_time_type(f_type),
field_name);
@@ -838,8 +836,25 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
}
}
+
+/**
+ Convert TIME/DATE/DATETIME value to String.
+ @param l_time DATE value
+ @param OUT str String to convert to
+ @param dec Number of fractional digits.
+*/
+bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec)
+{
+ if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ return true;
+ str->set_charset(&my_charset_numeric);
+ str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec));
+ return false;
+}
+
+
void make_truncated_value_warning(THD *thd,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const ErrConv *sval,
timestamp_type time_type,
const char *field_name)
@@ -864,7 +879,7 @@ void make_truncated_value_warning(THD *thd,
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
type_str, sval->ptr(), field_name,
- (ulong) thd->warning_info->current_row_for_warning());
+ (ulong) thd->get_stmt_da()->current_row_for_warning());
else
{
if (time_type > MYSQL_TIMESTAMP_ERROR)
@@ -915,6 +930,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
my_bool neg= 0;
enum enum_mysql_timestamp_type time_type= ltime->time_type;
+ if ((ulong) interval.day > MAX_DAY_NUMBER)
+ goto invalid_date;
+
if (time_type != MYSQL_TIMESTAMP_TIME)
ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
@@ -993,7 +1011,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
return 0; // Ok
invalid_date:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATETIME_FUNCTION_OVERFLOW,
ER(ER_DATETIME_FUNCTION_OVERFLOW),
ltime->time_type == MYSQL_TIMESTAMP_TIME ?
@@ -1030,8 +1048,8 @@ null_date:
*/
bool
-calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
- long *microseconds_out)
+calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
+ int l_sign, longlong *seconds_out, long *microseconds_out)
{
long days;
bool neg;
@@ -1057,13 +1075,13 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s
(uint) l_time2->day);
}
- microseconds= ((longlong)days*LL(86400) +
+ microseconds= ((longlong)days * SECONDS_IN_24H +
(longlong)(l_time1->hour*3600L +
l_time1->minute*60L +
l_time1->second) -
l_sign*(longlong)(l_time2->hour*3600L +
l_time2->minute*60L +
- l_time2->second)) * LL(1000000) +
+ l_time2->second)) * 1000000LL +
(longlong)l_time1->second_part -
l_sign*(longlong)l_time2->second_part;
@@ -1095,7 +1113,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s
*/
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
+int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
{
ulonglong a_t= pack_time(a);
ulonglong b_t= pack_time(b);
@@ -1150,7 +1168,7 @@ make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date,
{
/* e.g. negative time */
ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
}
@@ -1178,3 +1196,145 @@ void time_to_daytime_interval(MYSQL_TIME *ltime)
ltime->hour%= 24;
ltime->time_type= MYSQL_TIMESTAMP_NONE;
}
+
+
+/*** Conversion from TIME to DATETIME ***/
+
+/*
+ Simple case: TIME is within normal 24 hours internal.
+ Mix DATE part of ldate and TIME part of ltime together.
+*/
+static void
+mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
+ ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
+ ldate->hour= ltime->hour;
+ ldate->minute= ltime->minute;
+ ldate->second= ltime->second;
+ ldate->second_part= ltime->second_part;
+ ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/*
+ Complex case: TIME is negative or outside of the 24 hour interval.
+*/
+static void
+mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
+ ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
+ longlong seconds;
+ long days, useconds;
+ int sign= ltime->neg ? 1 : -1;
+ ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
+
+ DBUG_ASSERT(!ldate->neg);
+ DBUG_ASSERT(ldate->year > 0);
+
+ days= (long) (seconds / SECONDS_IN_24H);
+ calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
+ get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
+ ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/**
+ Mix a date value and a time value.
+
+ @param IN/OUT ldate Date value.
+ @param ltime Time value.
+*/
+static void
+mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from)
+{
+ if (!from->neg && from->hour < 24)
+ mix_date_and_time_simple(to, from);
+ else
+ mix_date_and_time_complex(to, from);
+}
+
+
+/**
+ Get current date in DATE format
+*/
+void set_current_date(THD *thd, MYSQL_TIME *to)
+{
+ thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start());
+ thd->time_zone_used= 1;
+ datetime_to_date(to);
+}
+
+
+/**
+ 5.5 compatible conversion from TIME to DATETIME
+*/
+static bool
+time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+
+ if (from->neg)
+ return true;
+
+ /* Set the date part */
+ uint day= from->hour / 24;
+ to->day= day % 31;
+ to->month= day / 31;
+ to->year= 0;
+ /* Set the time part */
+ to->hour= from->hour % 24;
+ to->minute= from->minute;
+ to->second= from->second;
+ to->second_part= from->second_part;
+ /* set sign and type */
+ to->neg= 0;
+ to->time_type= MYSQL_TIMESTAMP_DATETIME;
+ return false;
+}
+
+
+/**
+ Convert time to datetime.
+
+ The time value is added to the current datetime value.
+ @param IN ltime Time value to convert from.
+ @param OUT ltime2 Datetime value to convert to.
+*/
+bool
+time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
+ return time_to_datetime_old(thd, from, to);
+ set_current_date(thd, to);
+ mix_date_and_time(to, from);
+ return false;
+}
+
+
+bool
+time_to_datetime_with_warn(THD *thd,
+ const MYSQL_TIME *from, MYSQL_TIME *to,
+ ulonglong fuzzydate)
+{
+ int warn= 0;
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+ /*
+ After time_to_datetime() we need to do check_date(), as
+ the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE.
+ Note, the SQL standard time->datetime conversion mode always returns
+ a valid date based on CURRENT_DATE. So we need to do check_date()
+ only in the old mode.
+ */
+ if (time_to_datetime(thd, from, to) ||
+ ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
+ check_date(to, fuzzydate, &warn)))
+ {
+ ErrConvTime str(from);
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ &str, MYSQL_TIMESTAMP_DATETIME, 0);
+ return true;
+ }
+ return false;
+}