summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc33
-rw-r--r--sql/item.cc67
-rw-r--r--sql/item.h2
-rw-r--r--sql/item_func.h2
-rw-r--r--sql/item_timefunc.cc82
-rw-r--r--sql/nt_servc.cc2
-rw-r--r--sql/sql_time.cc148
-rw-r--r--sql/sql_time.h31
8 files changed, 301 insertions, 66 deletions
diff --git a/sql/field.cc b/sql/field.cc
index 9ec0e4d3089..922c9aba6c5 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4636,14 +4636,24 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
}
+static bool
+copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ if (from->time_type == MYSQL_TIMESTAMP_TIME)
+ return time_to_datetime(thd, from, to);
+ *to= *from;
+ return false;
+}
+
+
int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
int unused;
- MYSQL_TIME l_time= *ltime;
ErrConvTime str(ltime);
THD *thd= get_thd();
-
- bool valid= !check_date(&l_time, pack_time(&l_time) != 0,
+ MYSQL_TIME l_time;
+ bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) &&
+ !check_date(&l_time, pack_time(&l_time) != 0,
(thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE, &unused);
@@ -5201,15 +5211,28 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
- int error = 0, have_smth_to_conv= 1;
- MYSQL_TIME l_time= *ltime;
+ int error= 0, have_smth_to_conv= 1;
ErrConvTime str(ltime);
+ MYSQL_TIME l_time;
+
+ if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time))
+ {
+ /*
+ Set have_smth_to_conv and error in a way to have
+ store_TIME_with_warning do bzero().
+ */
+ have_smth_to_conv= false;
+ error= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ goto store;
+ }
+
/*
We don't perform range checking here since values stored in TIME
structure always fit into DATETIME range.
*/
have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0,
sql_mode_for_dates(current_thd), &error);
+store:
return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv);
}
diff --git a/sql/item.cc b/sql/item.cc
index 7901f1186d8..1c81fda9c63 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -234,6 +234,35 @@ bool Item::val_bool()
}
+/**
+ Get date/time/datetime.
+ Optionally extend TIME result to DATETIME.
+*/
+bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ /*
+ Some TIME type items return error when trying to do get_date()
+ without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
+ In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY.
+ In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
+ and leave it to get_date() to check date.
+ */
+ ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME &&
+ !current_thd->variables.old_mode) ? TIME_TIME_ONLY : 0;
+ if (get_date(ltime, fuzzydate | time_flag))
+ return true;
+ if (ltime->time_type == MYSQL_TIMESTAMP_TIME &&
+ !(fuzzydate & TIME_TIME_ONLY))
+ {
+ MYSQL_TIME tmp;
+ if (time_to_datetime_with_warn(current_thd, ltime, &tmp, fuzzydate))
+ return null_value= true;
+ *ltime= tmp;
+ }
+ return false;
+}
+
+
/*
For the items which don't have its own fast val_str_ascii()
implementation we provide a generic slower version,
@@ -8779,6 +8808,25 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
{
Item_result res_type=item_cmp_type(field->result_type(),
item->result_type());
+ /*
+ We have to check field->cmp_type() instead of res_type,
+ as result_type() - and thus res_type - can never be TIME_RESULT (yet).
+ */
+ if (field->cmp_type() == TIME_RESULT)
+ {
+ MYSQL_TIME field_time, item_time;
+ if (field->type() == MYSQL_TYPE_TIME)
+ {
+ field->get_time(&field_time);
+ item->get_time(&item_time);
+ }
+ else
+ {
+ field->get_date(&field_time, TIME_INVALID_DATES);
+ item->get_date(&item_time, TIME_INVALID_DATES);
+ }
+ return my_time_compare(&field_time, &item_time);
+ }
if (res_type == STRING_RESULT)
{
char item_buff[MAX_FIELD_WIDTH];
@@ -8829,25 +8877,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
return my_decimal_cmp(field_val, item_val);
}
/*
- We have to check field->cmp_type() instead of res_type,
- as result_type() - and thus res_type - can never be TIME_RESULT (yet).
- */
- if (field->cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME field_time, item_time;
- if (field->type() == MYSQL_TYPE_TIME)
- {
- field->get_time(&field_time);
- item->get_time(&item_time);
- }
- else
- {
- field->get_date(&field_time, TIME_INVALID_DATES);
- item->get_date(&item_time, TIME_INVALID_DATES);
- }
- return my_time_compare(&field_time, &item_time);
- }
- /*
The patch for Bug#13463415 started using this function for comparing
BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
Prefixing the auto variables with volatile fixes the problem....
diff --git a/sql/item.h b/sql/item.h
index 1faed26e1ee..e3ddf56511e 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1075,6 +1075,8 @@ public:
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
{ return get_date(ltime, TIME_TIME_ONLY | TIME_INVALID_DATES); }
+ // Get date with automatic TIME->DATETIME conversion
+ bool get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_seconds(ulonglong *sec, ulong *sec_part);
virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return get_date(ltime,fuzzydate); }
diff --git a/sql/item_func.h b/sql/item_func.h
index c1a92573eec..69abecc5f39 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -160,7 +160,7 @@ public:
void count_decimal_length();
inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
- return (null_value=args[0]->get_date(ltime, fuzzy_date));
+ return (null_value=args[0]->get_date_with_conversion(ltime, fuzzy_date));
}
void count_datetime_length(Item **item, uint nitems);
bool count_string_result_length(enum_field_types field_type,
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index b76474e6346..4d261e7a7d9 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1484,27 +1484,52 @@ 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:
- ltime->year= ltime->month= ltime->day= 0;
- ltime->time_type= MYSQL_TIMESTAMP_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:
- ltime->neg= 0;
- ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+ date_to_datetime(ltime);
return false;
case MYSQL_TYPE_DATE:
- ltime->neg= 0;
- ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
- ltime->time_type= MYSQL_TIMESTAMP_DATE;
+ datetime_to_date(ltime);
return false;
case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
return false;
default:
DBUG_ASSERT(0);
- return true;
+ return (null_value= true);
}
return false;
}
@@ -2190,8 +2215,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);
@@ -2512,26 +2539,7 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (decimals < TIME_SECOND_PART_DIGITS)
my_time_trunc(ltime, decimals);
- /*
- ltime is valid MYSQL_TYPE_TIME (according to fuzzy_date).
- But not every valid TIME value is a valid DATETIME value!
- */
- if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
- {
- if (ltime->neg)
- {
- ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
- &str, MYSQL_TIMESTAMP_DATETIME, 0);
- return (null_value= 1);
- }
-
- uint day= ltime->hour/24;
- ltime->hour %= 24;
- ltime->month= day / 31;
- ltime->day= day % 31;
- }
-
+ DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME);
ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
return 0;
}
@@ -2665,9 +2673,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;
@@ -2850,8 +2858,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,
@@ -2921,9 +2933,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:
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
index d6a8eac7ed5..c81bcef0316 100644
--- a/sql/nt_servc.cc
+++ b/sql/nt_servc.cc
@@ -33,7 +33,7 @@ NTService::NTService()
//time-out variables
nStartTimeOut = 15000;
- nStopTimeOut = 86400000;
+ nStopTimeOut = SECONDS_IN_24H * 1000;
nPauseTimeOut = 5000;
nResumeTimeOut = 5000;
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index c906f9e76d7..06098094505 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -1036,8 +1036,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;
@@ -1063,7 +1063,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s
(uint) l_time2->day);
}
- microseconds= ((longlong)days*86400LL +
+ microseconds= ((longlong)days * SECONDS_IN_24H +
(longlong)(l_time1->hour*3600L +
l_time1->minute*60L +
l_time1->second) -
@@ -1133,3 +1133,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
+*/
+static 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_mode)
+ 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_mode && 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;
+} \ No newline at end of file
diff --git a/sql/sql_time.h b/sql/sql_time.h
index e27102b15d3..7513ca7c00a 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -49,6 +49,33 @@ bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime,
ulonglong fuzzydate,
const char *name);
+bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
+bool time_to_datetime_with_warn(THD *thd,
+ const MYSQL_TIME *tm, MYSQL_TIME *dt,
+ ulonglong fuzzydate);
+inline void datetime_to_time(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->year= ltime->month= ltime->day= 0;
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+}
+inline void datetime_to_date(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
+ ltime->time_type= MYSQL_TIMESTAMP_DATE;
+}
+inline void date_to_datetime(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
void make_truncated_value_warning(THD *thd,
Sql_condition::enum_warning_level level,
const ErrConv *str_val,
@@ -76,8 +103,8 @@ bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
/* MYSQL_TIME operations */
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
INTERVAL interval);
-bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
- longlong *seconds_out, long *microseconds_out);
+bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
+ int l_sign, longlong *seconds_out, long *microseconds_out);
int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);