summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2013-07-10 12:12:27 +0400
committerAlexander Barkov <bar@mariadb.org>2013-07-10 12:12:27 +0400
commitd98584f56a5ec46d6258bba6250c6c797a678afd (patch)
tree7608b63b2a1e8bf9b6f9e74f9a0dd6a5848fbdbb /sql-common
parent17f3ae267f3b8c189be1671f86902f24ae79cdeb (diff)
downloadmariadb-git-d98584f56a5ec46d6258bba6250c6c797a678afd.tar.gz
Adding support for the SQL-standard temporal literals.
added: mysql-test/r/temporal_literal.result mysql-test/t/temporal_literal.test modified: client/mysqlbinlog.cc include/my_time.h mysql-test/r/cast.result mysql-test/r/partition_innodb.result mysql-test/t/cast.test mysql-test/t/partition_innodb.test sql-common/my_time.c sql/field.cc sql/item.cc sql/item.h sql/item_cmpfunc.cc sql/item_create.cc sql/item_create.h sql/item_strfunc.cc sql/item_timefunc.cc sql/item_timefunc.h sql/sql_select.cc sql/sql_time.cc sql/sql_time.h sql/sql_yacc.yy storage/spider/spd_db_mysql.cc
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/my_time.c222
1 files changed, 128 insertions, 94 deletions
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index d4093bb4df9..03701f61ae9 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -130,6 +130,7 @@ static int get_digits(uint *val, uint *number_of_fields, const char **str,
return get_number(val, number_of_fields, str, min(end, *str + length));
}
+
static int get_punct(const char **str, const char *end)
{
if (*str >= end)
@@ -203,6 +204,24 @@ static uint skip_digits(const char **str, const char *end)
return s - start;
}
+
+static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status,
+ uint *number_of_fields,
+ const char **str, const char *end)
+{
+ const char *start= *str;
+ uint tmp= 0; /* For the case '10:10:10.' */
+ if (get_digits(&tmp, number_of_fields, str, end, 6))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ if ((status->precision= (*str - start)) < 6)
+ *val= tmp * log_10_int[6 - (*str - start)];
+ else
+ *val= tmp;
+ if (skip_digits(str, end))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+}
+
+
/*
Convert a timestamp string to a MYSQL_TIME value.
@@ -217,9 +236,8 @@ static uint skip_digits(const char **str, const char *end)
TIME_NO_ZERO_IN_DATE Don't allow partial dates
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
TIME_INVALID_DATES Allow 2000-02-31
- was_cut 0 Value OK
- 1 If value was cut during conversion
- 2 check_date(date,flags) considers date invalid
+ status Conversion status
+
DESCRIPTION
At least the following formats are recogniced (based on number of digits)
@@ -230,20 +248,29 @@ static uint skip_digits(const char **str, const char *end)
The second part may have an optional .###### fraction part.
- RETURN VALUES
+ status->warnings is set to:
+ 0 Value OK
+ MYSQL_TIME_WARN_TRUNCATED If value was cut during conversion
+ MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
+
+ l_time->time_type is set as follows:
MYSQL_TIMESTAMP_NONE String wasn't a timestamp, like
[DD [HH:[MM:[SS]]]].fraction.
+ l_time is not changed.
MYSQL_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
MYSQL_TIMESTAMP_DATETIME Full timestamp
MYSQL_TIMESTAMP_ERROR Timestamp with wrong values.
All elements in l_time is set to 0
+ RETURN VALUES
+ 0 - Ok
+ 1 - Error
*/
#define MAX_DATE_PARTS 8
-enum enum_mysql_timestamp_type
+my_bool
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
- ulonglong flags, int *was_cut)
+ ulonglong flags, MYSQL_TIME_STATUS *status)
{
const char *end=str+length, *pos;
uint number_of_fields= 0, digits, year_length, not_zero_date;
@@ -252,19 +279,20 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
if (flags & TIME_TIME_ONLY)
{
- enum enum_mysql_timestamp_type ret;
- ret= str_to_time(str, length, l_time, flags, was_cut);
+ my_bool ret= str_to_time(str, length, l_time, flags, status);
DBUG_RETURN(ret);
}
- *was_cut= 0;
+
+ my_time_status_init(status);
/* Skip space at start */
for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
;
if (str == end || ! my_isdigit(&my_charset_latin1, *str))
{
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
+ status->warnings= MYSQL_TIME_WARN_TRUNCATED;
+ l_time->time_type= MYSQL_TIMESTAMP_NONE;
+ DBUG_RETURN(1);
}
/*
@@ -293,50 +321,49 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
(only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]])
*/
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
- *was_cut= get_digits(&l_time->year, &number_of_fields, &str, end, year_length)
- || get_digits(&l_time->month, &number_of_fields, &str, end, 2)
- || get_digits(&l_time->day, &number_of_fields, &str, end, 2)
- || get_maybe_T(&str, end)
- || get_digits(&l_time->hour, &number_of_fields, &str, end, 2)
- || get_digits(&l_time->minute, &number_of_fields, &str, end, 2)
- || get_digits(&l_time->second, &number_of_fields, &str, end, 2);
+ if (get_digits(&l_time->year, &number_of_fields, &str, end, year_length)
+ || get_digits(&l_time->month, &number_of_fields, &str, end, 2)
+ || get_digits(&l_time->day, &number_of_fields, &str, end, 2)
+ || get_maybe_T(&str, end)
+ || get_digits(&l_time->hour, &number_of_fields, &str, end, 2)
+ || get_digits(&l_time->minute, &number_of_fields, &str, end, 2)
+ || get_digits(&l_time->second, &number_of_fields, &str, end, 2))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
}
else
{
const char *start= str;
- *was_cut = get_number(&l_time->year, &number_of_fields, &str, end);
+ if (get_number(&l_time->year, &number_of_fields, &str, end))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
year_length= str - start;
- if (!*was_cut)
- *was_cut= get_punct(&str, end)
- || get_number(&l_time->month, &number_of_fields, &str, end)
- || get_punct(&str, end)
- || get_number(&l_time->day, &number_of_fields, &str, end)
- || get_date_time_separator(&number_of_fields, flags, &str, end)
- || get_number(&l_time->hour, &number_of_fields, &str, end)
- || get_punct(&str, end)
- || get_number(&l_time->minute, &number_of_fields, &str, end)
- || get_punct(&str, end)
- || get_number(&l_time->second, &number_of_fields, &str, end);
+ if (!status->warnings &&
+ (get_punct(&str, end)
+ || get_number(&l_time->month, &number_of_fields, &str, end)
+ || get_punct(&str, end)
+ || get_number(&l_time->day, &number_of_fields, &str, end)
+ || get_date_time_separator(&number_of_fields, flags, &str, end)
+ || get_number(&l_time->hour, &number_of_fields, &str, end)
+ || get_punct(&str, end)
+ || get_number(&l_time->minute, &number_of_fields, &str, end)
+ || get_punct(&str, end)
+ || get_number(&l_time->second, &number_of_fields, &str, end)))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
}
- if (number_of_fields < 3)
- *was_cut= 1;
-
/* we're ok if date part is correct. even if the rest is truncated */
- if (*was_cut && number_of_fields < 3)
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
+ if (number_of_fields < 3)
+ {
+ l_time->time_type= MYSQL_TIMESTAMP_NONE;
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ DBUG_RETURN(TRUE);
+ }
- if (!*was_cut && str < end && *str == '.')
+ if (!status->warnings && str < end && *str == '.')
{
- uint second_part;
- const char *start= ++str;
- *was_cut= get_digits(&second_part, &number_of_fields, &str, end, 6);
- if (str - start < 6)
- second_part*= log_10_int[6 - (str - start)];
- l_time->second_part= second_part;
- if (skip_digits(&str, end))
- *was_cut= 1;
+ str++;
+ get_microseconds(&l_time->second_part, status,
+ &number_of_fields, &str, end);
}
not_zero_date = l_time->year || l_time->month || l_time->day ||
@@ -349,11 +376,11 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
if (l_time->year > 9999 || l_time->month > 12 || l_time->day > 31 ||
l_time->hour > 23 || l_time->minute > 59 || l_time->second > 59)
{
- *was_cut= 1;
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
goto err;
}
- if (check_date(l_time, not_zero_date, flags, was_cut))
+ if (check_date(l_time, not_zero_date, flags, &status->warnings))
goto err;
l_time->time_type= (number_of_fields <= 3 ?
@@ -363,16 +390,17 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
{
if (!my_isspace(&my_charset_latin1,*str))
{
- *was_cut= 1;
+ status->warnings= MYSQL_TIME_WARN_TRUNCATED;
break;
}
}
- DBUG_RETURN(l_time->time_type);
+ DBUG_RETURN(FALSE);
err:
bzero((char*) l_time, sizeof(*l_time));
- DBUG_RETURN(MYSQL_TIMESTAMP_ERROR);
+ l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ DBUG_RETURN(TRUE);
}
@@ -387,23 +415,26 @@ err:
There may be an optional [.second_part] after seconds
length Length of str
l_time Store result here
- warning Set MYSQL_TIME_WARN_TRUNCATED flag if the input string
- was cut during conversion, and/or
- MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is
- out of range.
+ status Conversion status
+
NOTES
+
Because of the extra days argument, this function can only
work with times where the time arguments are in the above order.
+ status->warnings is set as follows:
+ MYSQL_TIME_WARN_TRUNCATED if the input string was cut during conversion,
+ and/or
+ MYSQL_TIME_WARN_OUT_OF_RANGE flag is set if the value is out of range.
+
RETURN
- MYSQL_TIMESTAMP_TIME
- MYSQL_TIMESTAMP_ERROR
+ FALSE on success
+ TRUE on error
*/
-enum enum_mysql_timestamp_type
-str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
- ulonglong fuzzydate, int *warning)
+my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
+ ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
{
ulong date[5];
ulonglong value;
@@ -411,7 +442,7 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
my_bool found_days,found_hours, neg= 0;
uint UNINIT_VAR(state);
- *warning= 0;
+ my_time_status_init(status);
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
length--;
if (str != end && *str == '-')
@@ -421,22 +452,20 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
length--;
}
if (str == end)
- return MYSQL_TIMESTAMP_ERROR;
+ {
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ goto err;
+ }
/* Check first if this is a full TIMESTAMP */
if (length >= 12)
{ /* Probably full timestamp */
- int was_cut;
- enum enum_mysql_timestamp_type
- res= str_to_datetime(str, length, l_time,
+ (void) str_to_datetime(str, length, l_time,
(fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY,
- &was_cut);
- if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
- {
- if (was_cut)
- *warning|= MYSQL_TIME_WARN_TRUNCATED;
- return res;
- }
+ status);
+ if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
+ return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
+ my_time_status_init(status);
}
l_time->neg= neg;
@@ -504,24 +533,15 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
fractional:
/* Get fractional second part */
- if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
+ if (!status->warnings && str < end && *str == '.')
{
- int field_length= 5;
- str++; value=(uint) (uchar) (*str - '0');
- while (++str != end && my_isdigit(&my_charset_latin1, *str))
- {
- if (field_length-- > 0)
- value= value*10 + (uint) (uchar) (*str - '0');
- }
- if (field_length > 0)
- value*= (long) log_10_int[field_length];
- else if (field_length < 0)
- *warning|= MYSQL_TIME_WARN_TRUNCATED;
- date[4]= (ulong) value;
+ uint number_of_fields= 0;
+ str++;
+ get_microseconds(&date[4], status, &number_of_fields, &str, end);
}
else
- date[4]=0;
-
+ date[4]= 0;
+
/* Check for exponent part: E<gigit> | E<sign><digit> */
/* (may occur as result of %g formatting of time value) */
if ((end - str) > 1 &&
@@ -530,7 +550,10 @@ fractional:
((str[1] == '-' || str[1] == '+') &&
(end - str) > 2 &&
my_isdigit(&my_charset_latin1, str[2]))))
- return MYSQL_TIMESTAMP_ERROR;
+ {
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ goto err;
+ }
if (internal_format_positions[7] != 255)
{
@@ -553,8 +576,11 @@ fractional:
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
date[2] > UINT_MAX || date[3] > UINT_MAX ||
date[4] > UINT_MAX)
- return MYSQL_TIMESTAMP_ERROR;
-
+ {
+ status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ goto err;
+ }
+
l_time->year= 0; /* For protocol::store_time */
l_time->month= 0;
l_time->day= date[0];
@@ -565,9 +591,9 @@ fractional:
l_time->time_type= MYSQL_TIMESTAMP_TIME;
/* Check if the value is valid and fits into MYSQL_TIME range */
- if (check_time_range(l_time, 6, warning))
- return MYSQL_TIMESTAMP_ERROR;
-
+ if (check_time_range(l_time, 6, &status->warnings))
+ return TRUE;
+
/* Check if there is garbage at end of the MYSQL_TIME specification */
if (str != end)
{
@@ -575,12 +601,17 @@ fractional:
{
if (!my_isspace(&my_charset_latin1,*str))
{
- *warning|= MYSQL_TIME_WARN_TRUNCATED;
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
break;
}
} while (++str != end);
}
- return MYSQL_TIMESTAMP_TIME;
+ return FALSE;
+
+err:
+ bzero((char*) l_time, sizeof(*l_time));
+ l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ return TRUE;
}
@@ -610,7 +641,10 @@ int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning)
999000, 999900, 999990, 999999};
if (my_time->minute >= 60 || my_time->second >= 60)
+ {
+ *warning|= MYSQL_TIME_WARN_TRUNCATED;
return 1;
+ }
hour= my_time->hour + (24*my_time->day);
@@ -1357,7 +1391,7 @@ double TIME_to_double(const MYSQL_TIME *my_time)
return my_time->neg ? -d : d;
}
-longlong pack_time(MYSQL_TIME *my_time)
+longlong pack_time(const MYSQL_TIME *my_time)
{
return ((((((my_time->year * 13ULL +
my_time->month) * 32ULL +