summaryrefslogtreecommitdiff
path: root/sql/sql_time.cc
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2011-10-19 21:45:18 +0200
committerSergei Golubchik <sergii@pisem.net>2011-10-19 21:45:18 +0200
commit76f0b94bb0b2994d639353530c5b251d0f1a204b (patch)
tree9ed50628aac34f89a37637bab2fc4915b86b5eb4 /sql/sql_time.cc
parent4e46d8e5bff140f2549841167dc4b65a3c0a645d (diff)
parent5dc1a2231f55bacc9aaf0e24816f3d9c2ee1f21d (diff)
downloadmariadb-git-76f0b94bb0b2994d639353530c5b251d0f1a204b.tar.gz
merge with 5.3
sql/sql_insert.cc: CREATE ... IF NOT EXISTS may do nothing, but it is still not a failure. don't forget to my_ok it. ****** CREATE ... IF NOT EXISTS may do nothing, but it is still not a failure. don't forget to my_ok it. sql/sql_table.cc: small cleanup ****** small cleanup
Diffstat (limited to 'sql/sql_time.cc')
-rw-r--r--sql/sql_time.cc301
1 files changed, 159 insertions, 142 deletions
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index de3bd35b46c..ce50fdb345b 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -1,4 +1,5 @@
/* Copyright (C) 2000-2006 MySQL AB
+ Copyright (c) 2009-2011 Monty Program Ab
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
@@ -252,8 +253,9 @@ to_ascii(CHARSET_INFO *cs,
/* Character set-aware version of str_to_time() */
-bool str_to_time(CHARSET_INFO *cs, const char *str,uint length,
- MYSQL_TIME *l_time, int *warning)
+timestamp_type
+str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+ MYSQL_TIME *l_time, ulong fuzzydate, int *warning)
{
char cnv[32];
if ((cs->state & MY_CS_NONASCII) != 0)
@@ -261,7 +263,7 @@ bool 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, warning);
+ return str_to_time(str, length, l_time, fuzzydate, warning);
}
@@ -291,76 +293,132 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs,
timestamp_type
str_to_datetime_with_warn(CHARSET_INFO *cs,
const char *str, uint length, MYSQL_TIME *l_time,
- uint flags)
+ ulong flags)
{
int was_cut;
THD *thd= current_thd;
timestamp_type ts_type;
ts_type= str_to_datetime(cs, str, length, l_time,
- (flags | (thd->variables.sql_mode &
- (MODE_INVALID_DATES |
- MODE_NO_ZERO_DATE))),
+ (flags | (sql_mode_for_dates(thd))),
&was_cut);
if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- str, length, ts_type, NullS);
+ make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ str, length, flags & TIME_TIME_ONLY ?
+ MYSQL_TIMESTAMP_TIME : ts_type, NullS);
return ts_type;
}
-/*
- Convert a datetime from broken-down MYSQL_TIME representation to corresponding
- TIMESTAMP value.
+/**
+ converts a pair of numbers (integer part, microseconds) to MYSQL_TIME
- SYNOPSIS
- TIME_to_timestamp()
- thd - current thread
- t - datetime in broken-down representation,
- in_dst_time_gap - pointer to bool which is set to true if t represents
- value which doesn't exists (falls into the spring
- time-gap) or to false otherwise.
-
- RETURN
- Number seconds in UTC since start of Unix Epoch corresponding to t.
- 0 - t contains datetime value which is out of TIMESTAMP range.
-
+ @param neg sign of the time value
+ @param nr integer part of the number to convert
+ @param sec_part microsecond part of the number
+ @param ltime converted value will be written here
+ @param fuzzydate conversion flags (TIME_FUZZY_DATE, etc)
+ @param str original number, as an ErrConv. For the warning
+ @param field_name field name or NULL if not a field. For the warning
+
+ @returns 0 for success, 1 for a failure
*/
-my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_gap)
+static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
+ MYSQL_TIME *ltime, ulong fuzzydate,
+ const ErrConv *str,
+ const char *field_name)
{
- my_time_t timestamp;
+ int was_cut;
+ longlong res;
+ enum_field_types f_type;
- *in_dst_time_gap= 0;
- thd->time_zone_used= 1;
+ if (fuzzydate & TIME_TIME_ONLY)
+ {
+ f_type= MYSQL_TYPE_TIME;
+ res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
+ }
+ else
+ {
+ f_type= MYSQL_TYPE_DATETIME;
+ res= neg ? -1 : number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut);
+ }
- timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
- if (timestamp)
+ if (res < 0 || (was_cut && !(fuzzydate & TIME_FUZZY_DATE)))
{
- return timestamp;
+ make_truncated_value_warning(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN, str,
+ res < 0 ? MYSQL_TIMESTAMP_ERROR
+ : mysql_type_to_time_type(f_type),
+ field_name);
}
+ return res < 0;
+}
+
- /* If we are here we have range error. */
- return(0);
+bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
+ ulong fuzzydate, const char *field_name)
+{
+ const ErrConvDouble str(value);
+ bool neg= value < 0;
+
+ if (neg)
+ value= -value;
+
+ if (value > LONGLONG_MAX)
+ value= static_cast<double>(LONGLONG_MAX);
+
+ longlong nr= static_cast<ulonglong>(floor(value));
+ uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR);
+ return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
+ field_name);
+}
+
+
+bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
+ ulong fuzzydate, const char *field_name)
+{
+ const ErrConvDecimal str(value);
+ ulonglong nr;
+ ulong sec_part;
+ bool neg= my_decimal2seconds(value, &nr, &sec_part);
+ return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
+ field_name);
+}
+
+
+bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime,
+ ulong fuzzydate, const char *field_name)
+{
+ const ErrConvInteger str(value);
+ bool neg= value < 0;
+ return number_to_time_with_warn(neg, neg ? -value : value, 0, ltime,
+ fuzzydate, &str, field_name);
}
/*
- Convert a time string to a MYSQL_TIME struct and produce a warning
- if string was cut during conversion.
+ Convert a datetime from broken-down MYSQL_TIME representation to
+ corresponding TIMESTAMP value.
- NOTE
- See str_to_time() for more info.
+ SYNOPSIS
+ TIME_to_timestamp()
+ thd - current thread
+ t - datetime in broken-down representation,
+ error_code - 0, if the conversion was successful;
+ ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
+ which is out of TIMESTAMP range;
+ ER_WARN_INVALID_TIMESTAMP, if t represents value which
+ doesn't exists (falls into the spring time-gap).
+
+ RETURN
+ Number seconds in UTC since start of Unix Epoch corresponding to t.
+ 0 - in case of ER_WARN_DATA_OUT_OF_RANGE
*/
-bool
-str_to_time_with_warn(CHARSET_INFO *cs,
- const char *str, uint length, MYSQL_TIME *l_time)
+
+my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code)
{
- int warning;
- bool ret_val= str_to_time(str, length, l_time, &warning);
- if (ret_val || warning)
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- str, length, MYSQL_TIMESTAMP_TIME, NullS);
- return ret_val;
+ thd->time_zone_used= 1;
+ return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code);
}
@@ -725,11 +783,6 @@ KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
};
-/*
- Return format string according format name.
- If name is unknown, result is NULL
-*/
-
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
timestamp_type type)
{
@@ -746,60 +799,15 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
}
}
-/****************************************************************************
- Functions to create default time/date/datetime strings
-
- NOTE:
- For the moment the DATE_TIME_FORMAT argument is ignored becasue
- MySQL doesn't support comparing of date/time/datetime strings that
- are not in arbutary order as dates are compared as strings in some
- context)
- This functions don't check that given MYSQL_TIME structure members are
- in valid range. If they are not, return value won't reflect any
- valid date either. Additionally, make_time doesn't take into
- account time->day member: it's assumed that days have been converted
- to hours already.
-****************************************************************************/
-
-void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
- const MYSQL_TIME *l_time, String *str)
-{
- uint length= (uint) my_time_to_str(l_time, (char*) str->ptr());
- str->length(length);
- str->set_charset(&my_charset_numeric);
-}
-
-
-void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
- const MYSQL_TIME *l_time, String *str)
-{
- uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
- str->length(length);
- str->set_charset(&my_charset_numeric);
-}
-
-
-void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
- const MYSQL_TIME *l_time, String *str)
-{
- uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
- str->length(length);
- str->set_charset(&my_charset_numeric);
-}
-
-
-void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
- const char *str_val,
- uint str_length, timestamp_type time_type,
+void make_truncated_value_warning(THD *thd,
+ MYSQL_ERROR::enum_warning_level level,
+ const ErrConv *sval,
+ timestamp_type time_type,
const char *field_name)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
const char *type_str;
CHARSET_INFO *cs= &my_charset_latin1;
- char buff[128];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.copy(str_val, str_length, system_charset_info);
- str[str_length]= 0; // Ensure we have end 0 for snprintf
switch (time_type) {
case MYSQL_TIMESTAMP_DATE:
@@ -816,32 +824,37 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve
if (field_name)
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- type_str, str.c_ptr(), field_name,
+ type_str, sval->ptr(), field_name,
(ulong) thd->warning_info->current_row_for_warning());
else
{
if (time_type > MYSQL_TIMESTAMP_ERROR)
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE),
- type_str, str.c_ptr());
+ type_str, sval->ptr());
else
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER(ER_WRONG_VALUE), type_str, str.c_ptr());
+ ER(ER_WRONG_VALUE), type_str, sval->ptr());
}
push_warning(thd, level,
ER_TRUNCATED_WRONG_VALUE, warn_buff);
}
+
/* Daynumber from year 0 to 9999-12-31 */
#define MAX_DAY_NUMBER 3652424L
-
-bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
+#define COMBINE(X) \
+ (((((X)->day * 24LL + (X)->hour) * 60LL + \
+ (X)->minute) * 60LL + (X)->second)*1000000LL + \
+ (X)->second_part)
+#define GET_PART(X, N) X % N ## LL; X/= N ## LL
+
+bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
+ INTERVAL interval)
{
long period, sign;
- ltime->neg= 0;
-
- sign= (interval.neg ? -1 : 1);
+ sign= (interval.neg == ltime->neg ? 1 : -1);
switch (int_type) {
case INTERVAL_SECOND:
@@ -858,35 +871,43 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
case INTERVAL_DAY_SECOND:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_HOUR:
+ case INTERVAL_DAY:
{
- longlong sec, days, daynr, microseconds, extra_sec;
- ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
- microseconds= ltime->second_part + sign*interval.second_part;
- extra_sec= microseconds/1000000L;
- microseconds= microseconds%1000000L;
-
- sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
- ltime->second +
- sign* (longlong) (interval.day*3600*24L +
- interval.hour*LL(3600)+interval.minute*LL(60)+
- interval.second))+ extra_sec;
- if (microseconds < 0)
+ longlong usec, daynr;
+ my_bool neg= 0;
+ enum enum_mysql_timestamp_type time_type= ltime->time_type;
+
+ if (time_type != MYSQL_TIMESTAMP_TIME)
+ ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
+
+ usec= COMBINE(ltime) + sign*COMBINE(&interval);
+
+ if (usec < 0)
{
- microseconds+= LL(1000000);
- sec--;
+ neg= 1;
+ usec= -usec;
}
- days= sec/(3600*LL(24));
- sec-= days*3600*LL(24);
- if (sec < 0)
+
+ ltime->second_part= GET_PART(usec, 1000000);
+ ltime->second= GET_PART(usec, 60);
+ ltime->minute= GET_PART(usec, 60);
+ ltime->neg^= neg;
+
+ if (time_type == MYSQL_TIMESTAMP_TIME)
{
- days--;
- sec+= 3600*LL(24);
+ if (usec > TIME_MAX_HOUR)
+ goto invalid_date;
+ ltime->hour= static_cast<uint>(usec);
+ ltime->day= 0;
+ return 0;
}
- ltime->second_part= (uint) microseconds;
- ltime->second= (uint) (sec % 60);
- ltime->minute= (uint) (sec/60 % 60);
- ltime->hour= (uint) (sec/3600);
- daynr= calc_daynr(ltime->year,ltime->month,1) + days;
+
+ if (int_type != INTERVAL_DAY)
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
+
+ ltime->hour= GET_PART(usec, 24);
+ daynr= usec;
+
/* Day number from year 0 to 9999-12-31 */
if ((ulonglong) daynr > MAX_DAY_NUMBER)
goto invalid_date;
@@ -894,7 +915,6 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
&ltime->day);
break;
}
- case INTERVAL_DAY:
case INTERVAL_WEEK:
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
sign * (long) interval.day);
@@ -932,13 +952,15 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
goto null_date;
}
- return 0; // Ok
+ if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
+ 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");
+ ltime->time_type == MYSQL_TIMESTAMP_TIME ?
+ "time" : "datetime");
null_date:
return 1;
}
@@ -1038,19 +1060,14 @@ 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)
{
- ulonglong a_t= TIME_to_ulonglong_datetime(a);
- ulonglong b_t= TIME_to_ulonglong_datetime(b);
+ ulonglong a_t= pack_time(a);
+ ulonglong b_t= pack_time(b);
if (a_t < b_t)
return -1;
if (a_t > b_t)
return 1;
- if (a->second_part < b->second_part)
- return -1;
- if (a->second_part > b->second_part)
- return 1;
-
return 0;
}