summaryrefslogtreecommitdiff
path: root/sql-common/my_time.c
diff options
context:
space:
mode:
Diffstat (limited to 'sql-common/my_time.c')
-rw-r--r--sql-common/my_time.c151
1 files changed, 129 insertions, 22 deletions
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 2841d33f7fd..008b339e955 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -261,6 +261,23 @@ static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status,
}
+static int check_time_range_internal(MYSQL_TIME *ltime,
+ ulong max_hour, uint dec,
+ int *warning);
+
+int check_time_range(MYSQL_TIME *ltime, uint dec, int *warning)
+{
+ return check_time_range_internal(ltime, TIME_MAX_HOUR, dec, warning);
+}
+
+
+static my_bool
+str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
+ MYSQL_TIME *l_time, ulong max_hour,
+ MYSQL_TIME_STATUS *status,
+ const char **endptr);
+
+
/*
Convert a timestamp string to a MYSQL_TIME value.
@@ -443,6 +460,20 @@ err:
}
+static size_t get_prefix_and_sign(my_bool *neg, const char *str, size_t length)
+{
+ const char *str0= str, *end= str + length;
+ for (; str < end && my_isspace(&my_charset_latin1, *str) ; str++)
+ { }
+ if (str < end && *str == '-')
+ {
+ *neg= TRUE;
+ str++;
+ }
+ return str - str0;
+}
+
+
/*
Convert a time string to a MYSQL_TIME struct.
@@ -475,23 +506,19 @@ err:
my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
{
- ulong date[5];
- ulonglong value;
- const char *end=str+length, *end_of_days;
- my_bool found_days,found_hours, neg= 0;
- uint UNINIT_VAR(state);
+ my_bool neg= 0;
+ size_t tmp_length;
+ const char *endptr;
DBUG_ASSERT(C_FLAGS_OK(fuzzydate));
my_time_status_init(status);
- for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
- length--;
- if (str != end && *str == '-')
+
+ if ((tmp_length= get_prefix_and_sign(&neg, str, length)))
{
- neg=1;
- str++;
- length--;
+ str+= tmp_length;
+ length-= tmp_length;
}
- if (str == end)
+ if (!length)
{
status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
goto err;
@@ -508,8 +535,80 @@ my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
my_time_status_init(status);
}
+ if (!str_to_DDhhmmssff_internal(neg, str, length, l_time, TIME_MAX_HOUR,
+ status, &endptr))
+ return FALSE;
+err:
+ bzero((char*) l_time, sizeof(*l_time));
+ l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ return TRUE;
+}
+
+
+my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *ltime,
+ ulong max_hour, MYSQL_TIME_STATUS *status)
+{
+ my_bool neg= 0;
+ size_t tmp_length;
+ const char *endptr;
+ my_time_status_init(status);
+
+ /* Remove trailing spaces */
+ for ( ; length > 0 && my_isspace(&my_charset_latin1, str[length - 1]) ; )
+ length--;
+
+ if ((tmp_length= get_prefix_and_sign(&neg, str, length)))
+ {
+ str+= tmp_length;
+ length-= tmp_length;
+ }
+ if (!length)
+ {
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_ERROR);
+ return TRUE;
+ }
+
+ /* Reject anything that might be parsed as a full TIMESTAMP */
+ if (length >= 12) /* The same condition with str_to_time() */
+ {
+ (void) str_to_datetime(str, length, ltime, C_TIME_DATETIME_ONLY, status);
+ if (ltime->time_type > MYSQL_TIMESTAMP_ERROR)
+ {
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ ltime->time_type= MYSQL_TIMESTAMP_NONE;
+ return TRUE;
+ }
+ my_time_status_init(status);
+ }
+
+ /*
+ Scan DDhhmmssff then reject anything that can remind date/datetime.
+ For example, in case of '2001-01-01', str_to_DDhhmmssff_internal()
+ will scan only '2001'.
+ */
+ if (str_to_DDhhmmssff_internal(neg, str, length, ltime, max_hour,
+ status, &endptr) ||
+ (endptr < str + length && endptr[0] == '-'))
+ return TRUE;
+ return FALSE;
+}
+
+
+static my_bool
+str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
+ MYSQL_TIME *l_time, ulong max_hour,
+ MYSQL_TIME_STATUS *status, const char **endptr)
+{
+ ulong date[5];
+ ulonglong value;
+ const char *end=str + length, *end_of_days;
+ my_bool found_days, found_hours;
+ uint UNINIT_VAR(state);
+
+ *endptr= str;
l_time->neg= neg;
- /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
+ /* Not a timestamp. Try to get this as a DAYS TO SECOND string */
for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
value=value*10L + (long) (*str - '0');
@@ -621,6 +720,11 @@ fractional:
goto err;
}
+ if ((ulonglong) date[0] * 24 + date[1] > (ulonglong) UINT_MAX32)
+ {
+ 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= 0;
@@ -630,8 +734,10 @@ fractional:
l_time->second_part= date[4];
l_time->time_type= MYSQL_TIMESTAMP_TIME;
+ *endptr= str;
+
/* Check if the value is valid and fits into MYSQL_TIME range */
- if (check_time_range(l_time, 6, &status->warnings))
+ if (check_time_range_internal(l_time, max_hour, 6, &status->warnings))
return TRUE;
/* Check if there is garbage at end of the MYSQL_TIME specification */
@@ -649,8 +755,7 @@ fractional:
return FALSE;
err:
- bzero((char*) l_time, sizeof(*l_time));
- l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ *endptr= str;
return TRUE;
}
@@ -659,8 +764,9 @@ err:
Check 'time' value to lie in the MYSQL_TIME range
SYNOPSIS:
- check_time_range()
+ check_time_range_internal()
time pointer to MYSQL_TIME value
+ ulong max_hour - maximum allowed hour value
uint dec
warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
@@ -674,9 +780,10 @@ err:
1 time value is invalid
*/
-int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning)
+int check_time_range_internal(struct st_mysql_time *my_time,
+ ulong max_hour, uint dec, int *warning)
{
- longlong hour;
+ ulonglong hour;
static ulong max_sec_part[TIME_SECOND_PART_DIGITS+1]= {000000, 900000, 990000,
999000, 999900, 999990, 999999};
@@ -691,14 +798,14 @@ int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning)
if (dec == AUTO_SEC_PART_DIGITS)
dec= TIME_SECOND_PART_DIGITS;
- if (hour <= TIME_MAX_HOUR &&
- (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
+ if (hour <= max_hour &&
+ (hour != max_hour || my_time->minute != TIME_MAX_MINUTE ||
my_time->second != TIME_MAX_SECOND ||
my_time->second_part <= max_sec_part[dec]))
return 0;
my_time->day= 0;
- my_time->hour= TIME_MAX_HOUR;
+ my_time->hour= max_hour;
my_time->minute= TIME_MAX_MINUTE;
my_time->second= TIME_MAX_SECOND;
my_time->second_part= max_sec_part[dec];