summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/client.c327
-rw-r--r--sql-common/my_time.c778
2 files changed, 629 insertions, 476 deletions
diff --git a/sql-common/client.c b/sql-common/client.c
index 6d3b5a69b44..86b05a206a8 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -127,10 +127,6 @@ char *mysql_unix_port= 0;
const char *unknown_sqlstate= "HY000";
const char *not_error_sqlstate= "00000";
const char *cant_connect_sqlstate= "08001";
-#ifdef HAVE_SMEM
-char *shared_memory_base_name= 0;
-const char *def_shared_memory_base_name= default_shared_memory_base_name;
-#endif
static void mysql_close_free_options(MYSQL *mysql);
static void mysql_close_free(MYSQL *mysql);
@@ -326,248 +322,6 @@ HANDLE create_named_pipe(MYSQL *mysql, uint connect_timeout, char **arg_host,
#endif
-/*
- Create new shared memory connection, return handler of connection
-
- SYNOPSIS
- create_shared_memory()
- mysql Pointer of mysql structure
- net Pointer of net structure
- connect_timeout Timeout of connection
-*/
-
-#ifdef HAVE_SMEM
-HANDLE create_shared_memory(MYSQL *mysql,NET *net, uint connect_timeout)
-{
- ulong smem_buffer_length = shared_memory_buffer_length + 4;
- /*
- event_connect_request is event object for start connection actions
- event_connect_answer is event object for confirm, that server put data
- handle_connect_file_map is file-mapping object, use for create shared
- memory
- handle_connect_map is pointer on shared memory
- handle_map is pointer on shared memory for client
- event_server_wrote,
- event_server_read,
- event_client_wrote,
- event_client_read are events for transfer data between server and client
- handle_file_map is file-mapping object, use for create shared memory
- */
- HANDLE event_connect_request = NULL;
- HANDLE event_connect_answer = NULL;
- HANDLE handle_connect_file_map = NULL;
- char *handle_connect_map = NULL;
-
- char *handle_map = NULL;
- HANDLE event_server_wrote = NULL;
- HANDLE event_server_read = NULL;
- HANDLE event_client_wrote = NULL;
- HANDLE event_client_read = NULL;
- HANDLE event_conn_closed = NULL;
- HANDLE handle_file_map = NULL;
- ulong connect_number;
- char connect_number_char[22], *p;
- char *tmp= NULL;
- char *suffix_pos;
- DWORD error_allow = 0;
- DWORD error_code = 0;
- DWORD event_access_rights= SYNCHRONIZE | EVENT_MODIFY_STATE;
- char *shared_memory_base_name = mysql->options.shared_memory_base_name;
- static const char *name_prefixes[] = {"","Global\\"};
- const char *prefix;
- uint i;
-
- /*
- If this is NULL, somebody freed the MYSQL* options. mysql_close()
- is a good candidate. We don't just silently (re)set it to
- def_shared_memory_base_name as that would create really confusing/buggy
- behavior if the user passed in a different name on the command-line or
- in a my.cnf.
- */
- DBUG_ASSERT(shared_memory_base_name != NULL);
-
- /*
- get enough space base-name + '_' + longest suffix we might ever send
- */
- if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, MYF(MY_FAE))))
- goto err;
-
- /*
- The name of event and file-mapping events create agree next rule:
- shared_memory_base_name+unique_part
- Where:
- shared_memory_base_name is unique value for each server
- unique_part is uniquel value for each object (events and file-mapping)
- */
- for (i = 0; i< array_elements(name_prefixes); i++)
- {
- prefix= name_prefixes[i];
- suffix_pos = strxmov(tmp, prefix , shared_memory_base_name, "_", NullS);
- strmov(suffix_pos, "CONNECT_REQUEST");
- event_connect_request= OpenEvent(event_access_rights, FALSE, tmp);
- if (event_connect_request)
- {
- break;
- }
- }
- if (!event_connect_request)
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR;
- goto err;
- }
- strmov(suffix_pos, "CONNECT_ANSWER");
- if (!(event_connect_answer= OpenEvent(event_access_rights,FALSE,tmp)))
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR;
- goto err;
- }
- strmov(suffix_pos, "CONNECT_DATA");
- if (!(handle_connect_file_map= OpenFileMapping(FILE_MAP_WRITE,FALSE,tmp)))
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR;
- goto err;
- }
- if (!(handle_connect_map= MapViewOfFile(handle_connect_file_map,
- FILE_MAP_WRITE,0,0,sizeof(DWORD))))
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_MAP_ERROR;
- goto err;
- }
-
- /* Send to server request of connection */
- if (!SetEvent(event_connect_request))
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_SET_ERROR;
- goto err;
- }
-
- /* Wait of answer from server */
- if (WaitForSingleObject(event_connect_answer,connect_timeout*1000) !=
- WAIT_OBJECT_0)
- {
- error_allow = CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR;
- goto err;
- }
-
- /* Get number of connection */
- connect_number = uint4korr(handle_connect_map);/*WAX2*/
- p= int10_to_str(connect_number, connect_number_char, 10);
-
- /*
- The name of event and file-mapping events create agree next rule:
- shared_memory_base_name+unique_part+number_of_connection
-
- Where:
- shared_memory_base_name is uniquel value for each server
- unique_part is uniquel value for each object (events and file-mapping)
- number_of_connection is number of connection between server and client
- */
- suffix_pos = strxmov(tmp, prefix , shared_memory_base_name, "_", connect_number_char,
- "_", NullS);
- strmov(suffix_pos, "DATA");
- if ((handle_file_map = OpenFileMapping(FILE_MAP_WRITE,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_FILE_MAP_ERROR;
- goto err2;
- }
- if ((handle_map = MapViewOfFile(handle_file_map,FILE_MAP_WRITE,0,0,
- smem_buffer_length)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_MAP_ERROR;
- goto err2;
- }
-
- strmov(suffix_pos, "SERVER_WROTE");
- if ((event_server_wrote = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
- goto err2;
- }
-
- strmov(suffix_pos, "SERVER_READ");
- if ((event_server_read = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
- goto err2;
- }
-
- strmov(suffix_pos, "CLIENT_WROTE");
- if ((event_client_wrote = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
- goto err2;
- }
-
- strmov(suffix_pos, "CLIENT_READ");
- if ((event_client_read = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
- goto err2;
- }
-
- strmov(suffix_pos, "CONNECTION_CLOSED");
- if ((event_conn_closed = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
- {
- error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
- goto err2;
- }
- /*
- Set event that server should send data
- */
- SetEvent(event_server_read);
-
-err2:
- if (error_allow == 0)
- {
- net->vio= vio_new_win32shared_memory(handle_file_map,handle_map,
- event_server_wrote,
- event_server_read,event_client_wrote,
- event_client_read,event_conn_closed);
- }
- else
- {
- error_code = GetLastError();
- if (event_server_read)
- CloseHandle(event_server_read);
- if (event_server_wrote)
- CloseHandle(event_server_wrote);
- if (event_client_read)
- CloseHandle(event_client_read);
- if (event_client_wrote)
- CloseHandle(event_client_wrote);
- if (event_conn_closed)
- CloseHandle(event_conn_closed);
- if (handle_map)
- UnmapViewOfFile(handle_map);
- if (handle_file_map)
- CloseHandle(handle_file_map);
- }
-err:
- my_free(tmp);
- if (error_allow)
- error_code = GetLastError();
- if (event_connect_request)
- CloseHandle(event_connect_request);
- if (event_connect_answer)
- CloseHandle(event_connect_answer);
- if (handle_connect_map)
- UnmapViewOfFile(handle_connect_map);
- if (handle_connect_file_map)
- CloseHandle(handle_connect_file_map);
- if (error_allow)
- {
- if (error_allow == CR_SHARED_MEMORY_EVENT_ERROR)
- set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
- ER(error_allow), suffix_pos, error_code);
- else
- set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
- ER(error_allow), error_code);
- return(INVALID_HANDLE_VALUE);
- }
- return(handle_map);
-}
-#endif
-
/**
Read a packet from server. Give error message if socket was down
or packet is an error message
@@ -987,7 +741,7 @@ static const char *default_options[]=
"ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath",
"character-sets-dir", "default-character-set", "interactive-timeout",
"connect-timeout", "local-infile", "disable-local-infile",
- "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
+ "ssl-cipher", "max-allowed-packet", "protocol",
"multi-results", "multi-statements", "multi-queries", "secure-auth",
"report-data-truncation", "plugin-dir", "default-auth",
"bind-address", "ssl-crl", "ssl-crlpath",
@@ -1000,7 +754,7 @@ enum option_id {
OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath,
OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout,
OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile,
- OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
+ OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol,
OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth,
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth,
OPT_bind_address, OPT_ssl_crl, OPT_ssl_crlpath,
@@ -1241,13 +995,6 @@ void mysql_read_default_options(struct st_mysql_options *options,
options->protocol= UINT_MAX32;
}
break;
- case OPT_shared_memory_base_name:
-#ifdef HAVE_SMEM
- if (options->shared_memory_base_name != def_shared_memory_base_name)
- my_free(options->shared_memory_base_name);
- options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME));
-#endif
- break;
case OPT_multi_results:
options->client_flag|= CLIENT_MULTI_RESULTS;
break;
@@ -1640,10 +1387,6 @@ mysql_init(MYSQL *mysql)
? WAIT_FOR_QUERY : ALWAYS_ACCEPT;
#endif
-#ifdef HAVE_SMEM
- mysql->options.shared_memory_base_name= (char*) def_shared_memory_base_name;
-#endif
-
mysql->options.methods_to_use= MYSQL_OPT_GUESS_CONNECTION;
mysql->options.report_data_truncation= TRUE; /* default */
@@ -1833,9 +1576,15 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const c
#ifdef HAVE_X509_check_host
ret_validation=
- (X509_check_host(server_cert, server_hostname,
- strlen(server_hostname), 0, 0) != 1) &&
- (X509_check_ip_asc(server_cert, server_hostname, 0) != 1);
+ X509_check_host(server_cert, server_hostname,
+ strlen(server_hostname), 0, 0) != 1;
+#ifndef HAVE_WOLFSSL
+ if (ret_validation)
+ {
+ ret_validation=
+ X509_check_ip_asc(server_cert, server_hostname, 0) != 1;
+ }
+#endif
#else
subject= X509_get_subject_name(server_cert);
cn_loc= X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
@@ -2610,12 +2359,6 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
info->protocol= MYSQL_VIO_PIPE;
info->handle= vio->hPipe;
return;
- case VIO_TYPE_SHARED_MEMORY:
- info->protocol= MYSQL_VIO_MEMORY;
-#ifdef HAVE_SMEM
- info->handle= vio->handle_file_map; /* or what ? */
-#endif
- return;
#endif
default: DBUG_ASSERT(0);
}
@@ -2950,42 +2693,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
/*
Part 0: Grab a socket and connect it to the server
*/
-#if defined(HAVE_SMEM)
- if ((!mysql->options.protocol ||
- mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) &&
- (!host || !strcmp(host,LOCAL_HOST)) &&
- mysql->options.shared_memory_base_name)
- {
- DBUG_PRINT("info", ("Using shared memory"));
- if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) ==
- INVALID_HANDLE_VALUE)
- {
- DBUG_PRINT("error",
- ("host: '%s' socket: '%s' shared memory: %s have_tcpip: %d",
- host ? host : "<null>",
- unix_socket ? unix_socket : "<null>",
- mysql->options.shared_memory_base_name,
- (int) have_tcpip));
- if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY)
- goto error;
- /*
- Try also with PIPE or TCP/IP. Clear the error from
- create_shared_memory().
- */
-
- net_clear_error(net);
- }
- else
- {
- mysql->options.protocol=MYSQL_PROTOCOL_MEMORY;
- unix_socket = 0;
- host=mysql->options.shared_memory_base_name;
- my_snprintf(host_info=buff, sizeof(buff)-1,
- ER(CR_SHARED_MEMORY_CONNECTION), host);
- }
- }
-#endif /* HAVE_SMEM */
#if defined(HAVE_SYS_UN_H)
if (!net->vio &&
(!mysql->options.protocol ||
@@ -3598,10 +3306,6 @@ static void mysql_close_free_options(MYSQL *mysql)
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
mysql_ssl_free(mysql);
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
-#ifdef HAVE_SMEM
- if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
- my_free(mysql->options.shared_memory_base_name);
-#endif /* HAVE_SMEM */
if (mysql->options.extension)
{
struct mysql_async_context *ctxt= mysql->options.extension->async_context;
@@ -4100,13 +3804,6 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
case MYSQL_OPT_PROTOCOL:
mysql->options.protocol= *(uint*) arg;
break;
- case MYSQL_SHARED_MEMORY_BASE_NAME:
-#ifdef HAVE_SMEM
- if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
- my_free(mysql->options.shared_memory_base_name);
- mysql->options.shared_memory_base_name=my_strdup(arg,MYF(MY_WME));
-#endif
- break;
case MYSQL_OPT_USE_REMOTE_CONNECTION:
case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
case MYSQL_OPT_GUESS_CONNECTION:
@@ -4244,8 +3941,8 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
}
}
break;
+ case MYSQL_SHARED_MEMORY_BASE_NAME:
default:
- break;
DBUG_RETURN(1);
}
DBUG_RETURN(0);
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 3a1f176485f..5c14b8071a3 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -58,6 +58,19 @@ uint calc_days_in_year(uint year)
366 : 365);
}
+
+#ifdef DBUG_ASSERT_EXISTS
+
+
+static const ulonglong C_KNOWN_FLAGS= C_TIME_NO_ZERO_IN_DATE |
+ C_TIME_NO_ZERO_DATE |
+ C_TIME_INVALID_DATES;
+
+#define C_FLAGS_OK(flags) (((flags) & ~C_KNOWN_FLAGS) == 0)
+
+#endif
+
+
/**
@brief Check datetime value for validity according to flags.
@@ -82,13 +95,14 @@ uint calc_days_in_year(uint year)
my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
ulonglong flags, int *was_cut)
{
+ DBUG_ASSERT(C_FLAGS_OK(flags));
if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
return FALSE;
if (not_zero_date)
{
- if (((flags & TIME_NO_ZERO_IN_DATE) &&
+ if (((flags & C_TIME_NO_ZERO_IN_DATE) &&
(ltime->month == 0 || ltime->day == 0)) || ltime->neg ||
- (!(flags & TIME_INVALID_DATES) &&
+ (!(flags & C_TIME_INVALID_DATES) &&
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
ltime->day != 29)))
@@ -97,12 +111,13 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
return TRUE;
}
}
- else if (flags & TIME_NO_ZERO_DATE)
+ else if (flags & C_TIME_NO_ZERO_DATE)
{
/*
We don't set *was_cut here to signal that the problem was a zero date
and not an invalid date
*/
+ *was_cut|= MYSQL_TIME_WARN_ZERO_DATE;
return TRUE;
}
return FALSE;
@@ -145,7 +160,8 @@ static int get_punct(const char **str, const char *end)
return 1;
}
-static int get_date_time_separator(uint *number_of_fields, ulonglong flags,
+static int get_date_time_separator(uint *number_of_fields,
+ my_bool punct_is_date_time_separator,
const char **str, const char *end)
{
const char *s= *str;
@@ -164,11 +180,11 @@ static int get_date_time_separator(uint *number_of_fields, ulonglong flags,
but
cast("11:11:11.12.12.12" as time) should give 11:11:11.12
that is, a punctuation character can be accepted as a date/time separator
- only if TIME_DATETIME_ONLY (see str_to_time) is not set.
+ only if "punct_is_date_time_separator" is set.
*/
if (my_ispunct(&my_charset_latin1, *s))
{
- if (flags & TIME_DATETIME_ONLY)
+ if (!punct_is_date_time_separator)
{
/* see above, returning 1 is not enough, we need hard abort here */
*number_of_fields= 0;
@@ -241,25 +257,220 @@ static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status,
*val= (ulong) (tmp * log_10_int[6 - (*str - start)]);
else
*val= tmp;
+ if (str[0] < end && my_isdigit(&my_charset_latin1, str[0][0]))
+ {
+ /*
+ We don't need the exact nanoseconds value.
+ Knowing the first digit is enough for rounding.
+ */
+ status->nanoseconds= 100 * (uint)(str[0][0] - '0');
+ }
if (skip_digits(str, end))
status->warnings|= MYSQL_TIME_NOTE_TRUNCATED;
}
+static int check_time_range_internal(MYSQL_TIME *ltime,
+ ulong max_hour, ulong err_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, UINT_MAX32,
+ dec, warning);
+}
+
+
+static my_bool
+set_neg(my_bool neg, MYSQL_TIME_STATUS *st, MYSQL_TIME *ltime)
+{
+ if ((ltime->neg= neg) && ltime->time_type != MYSQL_TIMESTAMP_TIME)
+ {
+ st->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+ /* Remove trailing spaces and garbage */
+static my_bool get_suffix(const char *str, size_t length, size_t *new_length)
+{
+ /*
+ QQ: perhaps 'T' should be considered as a date/time delimiter only
+ if it's followed by a digit. Learn ISO 8601 details.
+ */
+ my_bool garbage= FALSE;
+ for ( ; length > 0 ; length--)
+ {
+ char ch= str[length - 1];
+ if (my_isdigit(&my_charset_latin1, ch) ||
+ my_ispunct(&my_charset_latin1, ch))
+ break;
+ if (my_isspace(&my_charset_latin1, ch))
+ continue;
+ if (ch == 'T')
+ {
+ /* 'T' has a meaning only after a digit. Otherwise it's a garbage */
+ if (length >= 2 && my_isdigit(&my_charset_latin1, str[length - 2]))
+ break;
+ }
+ garbage= TRUE;
+ }
+ *new_length= length;
+ return garbage;
+}
+
+
+static size_t get_prefix(const char *str, size_t length, const char **endptr)
+{
+ const char *str0= str, *end= str + length;
+ for (; str < end && my_isspace(&my_charset_latin1, *str) ; str++)
+ { }
+ *endptr= str;
+ return str - str0;
+}
+
+
+static size_t get_sign(my_bool *neg, const char *str, size_t length,
+ const char **endptr)
+{
+ const char *str0= str;
+ if (length)
+ {
+ if ((*neg= (*str == '-')) || (*str == '+'))
+ str++;
+ }
+ else
+ *neg= FALSE;
+ *endptr= str;
+ return str - str0;
+}
+
+
+static my_bool find_body(my_bool *neg, const char *str, size_t length,
+ MYSQL_TIME *to, int *warn,
+ const char **new_str, size_t *new_length)
+{
+ size_t sign_length;
+ *warn= 0;
+ length-= get_prefix(str, length, &str);
+ sign_length= get_sign(neg, str, length, &str);
+ length-= sign_length;
+ /* There can be a space after a sign again: '- 10:20:30' or '- 1 10:20:30' */
+ length-= get_prefix(str, length, &str);
+ if (get_suffix(str, length, &length))
+ *warn|= MYSQL_TIME_WARN_TRUNCATED;
+ *new_str= str;
+ *new_length= length;
+ if (!length || !my_isdigit(&my_charset_latin1, *str))
+ {
+ *warn|= MYSQL_TIME_WARN_EDOM;
+ set_zero_time(to, MYSQL_TIMESTAMP_ERROR);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+typedef struct
+{
+ uint count_punct;
+ uint count_colon;
+ uint count_iso_date_time_separator;
+} MYSQL_TIME_USED_CHAR_STATISTICS;
+
+
+static void
+mysql_time_used_char_statistics_init(MYSQL_TIME_USED_CHAR_STATISTICS *to,
+ const char *str, const char *end)
+{
+ const char *s;
+ bzero((void *) to, sizeof(MYSQL_TIME_USED_CHAR_STATISTICS));
+ for (s= str; s < end; s++)
+ {
+ if (my_ispunct(&my_charset_latin1, *s))
+ to->count_punct++;
+ if (*s == ':')
+ to->count_colon++;
+ if (*s == 'T')
+ to->count_iso_date_time_separator++;
+ }
+}
+
+
+static my_bool
+is_datetime_body_candidate(const char *str, size_t length,
+ my_bool allow_dates_delimited,
+ my_bool allow_dates_numeric)
+{
+ static uint min_date_length= 5; /* '1-1-1' -> '0001-01-01' */
+ uint pos, count_punct= 0;
+ uint date_time_separator_length= MY_TEST(!allow_dates_delimited);
+ if (length >= 12)
+ return TRUE;
+ /*
+ The shortest possible DATE is '1-1-1', which is 5 characters.
+ To make a full datetime it should be at least followed by a space or a 'T'.
+ To make a date it should be just not less that 5 characters.
+ */
+ if (length < min_date_length + date_time_separator_length &&
+ !allow_dates_numeric)
+ return FALSE;
+ for (pos= 0; pos < length; pos++)
+ {
+ if (str[pos] == 'T') /* Date/time separator */
+ return TRUE;
+ if (str[pos] == ' ')
+ {
+ /*
+ We found a space. If can be a DATE/TIME separator:
+ TIME('1-1-1 1:1:1.0) -> '0001-01-01 01:01:01.0'
+
+ But it can be also a DAY/TIME separator:
+ TIME('1 11') -> 35:00:00 = 1 day 11 hours
+ TIME('1 111') -> 135:00:00 = 1 day 111 hours
+ TIME('11 11') -> 275:00:00 = 11 days 11 hours
+ TIME('111 11') -> 838:59:59 = 111 days 11 hours with overflow
+ TIME('1111 11') -> 838:59:59 = 1111 days 11 hours with overflow
+ */
+ return count_punct > 0; /* Can be a DATE if already had separators*/
+ }
+ if (my_ispunct(&my_charset_latin1, str[pos]))
+ {
+ if (allow_dates_delimited && str[pos] != ':')
+ return TRUE;
+ count_punct++;
+ }
+ }
+ return allow_dates_numeric && count_punct == 0;
+}
+
+
+static my_bool
+str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
+ MYSQL_TIME *l_time,
+ ulong max_hour, ulong err_hour,
+ MYSQL_TIME_STATUS *status,
+ const char **endptr);
+
+
/*
Convert a timestamp string to a MYSQL_TIME value.
SYNOPSIS
- str_to_datetime()
+ str_to_datetime_or_date_body()
str String to parse
length Length of string
l_time Date is stored here
flags Bitmap of following items
- TIME_FUZZY_DATE
TIME_DATETIME_ONLY Set if we only allow full datetimes.
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
+ punct_is_date_time_separator
+ Allow punctuation as a date/time separator,
+ or return a hard error.
status Conversion status
@@ -292,32 +503,22 @@ static void get_microseconds(ulong *val, MYSQL_TIME_STATUS *status,
#define MAX_DATE_PARTS 8
-my_bool
-str_to_datetime(const char *str, size_t length, MYSQL_TIME *l_time,
- ulonglong flags, MYSQL_TIME_STATUS *status)
+static my_bool
+str_to_datetime_or_date_body(const char *str, size_t length, MYSQL_TIME *l_time,
+ ulonglong flags,
+ my_bool punct_is_date_time_separator,
+ MYSQL_TIME_STATUS *status,
+ uint *number_of_fields,
+ const char **endptr)
{
const char *end=str+length, *pos;
- uint number_of_fields= 0, digits, year_length, not_zero_date;
- DBUG_ENTER("str_to_datetime");
+ uint digits, year_length, not_zero_date;
+ int warn= 0;
+ DBUG_ENTER("str_to_datetime_or_date_body");
+ DBUG_ASSERT(C_FLAGS_OK(flags));
bzero(l_time, sizeof(*l_time));
-
- if (flags & TIME_TIME_ONLY)
- {
- my_bool ret= str_to_time(str, length, l_time, flags, status);
- DBUG_RETURN(ret);
- }
-
- 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))
- {
- status->warnings= MYSQL_TIME_WARN_TRUNCATED;
- l_time->time_type= MYSQL_TIMESTAMP_NONE;
- DBUG_RETURN(1);
- }
+ *number_of_fields= 0;
+ *endptr= str;
/*
Calculate number of digits in first part.
@@ -345,49 +546,53 @@ str_to_datetime(const char *str, size_t length, MYSQL_TIME *l_time,
(only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]])
*/
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 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)
+ 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;
+ || 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))
+ warn|= MYSQL_TIME_WARN_TRUNCATED;
}
else
{
const char *start= str;
- if (get_number(&l_time->year, &number_of_fields, &str, end))
- status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ if (get_number(&l_time->year, number_of_fields, &str, end))
+ warn|= MYSQL_TIME_WARN_TRUNCATED;
year_length= (uint)(str - start);
- if (!status->warnings &&
+ if (!warn &&
(get_punct(&str, end)
- || get_number(&l_time->month, &number_of_fields, &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_number(&l_time->day, number_of_fields, &str, end)
+ || get_date_time_separator(number_of_fields,
+ punct_is_date_time_separator, &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_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;
+ || get_number(&l_time->second, number_of_fields, &str, end)))
+ warn|= MYSQL_TIME_WARN_TRUNCATED;
}
+ status->warnings|= warn;
+ *endptr= str;
/* we're ok if date part is correct. even if the rest is truncated */
- if (number_of_fields < 3)
+ if (*number_of_fields < 3)
{
l_time->time_type= MYSQL_TIMESTAMP_NONE;
status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
DBUG_RETURN(TRUE);
}
- if (!status->warnings && str < end && *str == '.')
+ if (!warn && str < end && *str == '.')
{
str++;
get_microseconds(&l_time->second_part, status,
- &number_of_fields, &str, end);
+ number_of_fields, &str, end);
+ *endptr= str;
}
not_zero_date = l_time->year || l_time->month || l_time->day ||
@@ -407,23 +612,16 @@ str_to_datetime(const char *str, size_t length, MYSQL_TIME *l_time,
if (check_date(l_time, not_zero_date, flags, &status->warnings))
goto err;
- l_time->time_type= (number_of_fields <= 3 ?
+ l_time->time_type= (*number_of_fields <= 3 ?
MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
- for (; str != end ; str++)
- {
- if (!my_isspace(&my_charset_latin1,*str))
- {
- status->warnings= MYSQL_TIME_WARN_TRUNCATED;
- break;
- }
- }
+ if (str != end)
+ status->warnings= MYSQL_TIME_WARN_TRUNCATED;
DBUG_RETURN(FALSE);
err:
- bzero((char*) l_time, sizeof(*l_time));
- l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
DBUG_RETURN(TRUE);
}
@@ -432,7 +630,7 @@ err:
Convert a time string to a MYSQL_TIME struct.
SYNOPSIS
- str_to_time()
+ str_to_datetime_or_date_or_time_body()
str A string in full TIMESTAMP format or
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
[M]MSS or [S]S
@@ -457,45 +655,280 @@ err:
TRUE on error
*/
-my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
- ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
+static my_bool
+str_to_datetime_or_date_or_time_body(const char *str, size_t length,
+ MYSQL_TIME *l_time,
+ ulonglong fuzzydate,
+ MYSQL_TIME_STATUS *status,
+ ulong time_max_hour,
+ ulong time_err_hour,
+ my_bool allow_dates_delimited,
+ my_bool allow_dates_numeric)
{
- 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_time_status_init(status);
- for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
- length--;
- if (str != end && *str == '-')
- {
- neg=1;
- str++;
- length--;
- }
- if (str == end)
- {
- status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
- goto err;
- }
+ const char *endptr;
+ DBUG_ASSERT(C_FLAGS_OK(fuzzydate));
/* Check first if this is a full TIMESTAMP */
- if (length >= 12)
+ if (is_datetime_body_candidate(str, length,
+ allow_dates_delimited,
+ allow_dates_numeric))
{ /* Probably full timestamp */
- (void) str_to_datetime(str, length, l_time,
- (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY,
- status);
- if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
- return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
+ int warn_copy= status->warnings; /* could already be set by find_body() */
+ uint number_of_fields;
+ (void) str_to_datetime_or_date_body(str, length, l_time, fuzzydate,
+ FALSE, status,
+ &number_of_fields, &endptr);
+ DBUG_ASSERT(endptr >= str);
+ DBUG_ASSERT(endptr <= str + length);
+ switch (l_time->time_type) {
+ case MYSQL_TIMESTAMP_DATETIME:
+ return FALSE;
+ case MYSQL_TIMESTAMP_DATE:
+ {
+ /*
+ Successfully parsed as DATE, but it can also be a TIME:
+ '24:02:03' - continue and parse as TIME
+ '24:02:03 garbage /////' - continue and parse as TIME
+ '24:02:03T' - return DATE
+ '24-02-03' - return DATE
+ '24/02/03' - return DATE
+ '11111' - return DATE
+ */
+ MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
+ mysql_time_used_char_statistics_init(&used_chars, str, endptr);
+ if (used_chars.count_iso_date_time_separator || !used_chars.count_colon)
+ return FALSE;
+ }
+ break;
+ case MYSQL_TIMESTAMP_ERROR:
+ {
+ MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
+ /*
+ Check if it parsed as DATETIME but then failed as out of range:
+ '2011-02-32 8:46:06.23434' - return error
+ */
+ if (number_of_fields > 3)
+ return TRUE;
+ /*
+ Check if it parsed as DATE but then failed as out of range:
+ '100000:02:03' - continue and parse as TIME
+ '100000:02:03T' - return error
+ '100000/02/03' - return error
+ '100000-02-03' - return error
+ */
+ mysql_time_used_char_statistics_init(&used_chars, str, endptr);
+ if (used_chars.count_iso_date_time_separator || !used_chars.count_colon)
+ return TRUE;
+ }
+ break;
+ case MYSQL_TIMESTAMP_NONE:
+ {
+ if (allow_dates_numeric && endptr >= str + length)
+ {
+ /*
+ For backward compatibility this parses as DATE and fails:
+ EXTRACT(DAY FROM '1111') -- return error
+ EXTRACT(DAY FROM '1') -- return error
+ */
+ MYSQL_TIME_USED_CHAR_STATISTICS used_chars;
+ mysql_time_used_char_statistics_init(&used_chars, str, endptr);
+ if (!used_chars.count_iso_date_time_separator &&
+ !used_chars.count_colon &&
+ !used_chars.count_punct)
+ return TRUE;
+ }
+ /*
+ - '256 10:30:30' - continue and parse as TIME
+ - '4294967296:59:59.123456456' - continue and parse as TIME
+ */
+ }
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ DBUG_ASSERT(0);
+ break;
+ }
+ my_time_status_init(status);
+ status->warnings= warn_copy;
+ }
+
+ if (!str_to_DDhhmmssff_internal(FALSE, str, length, l_time,
+ time_max_hour, time_err_hour,
+ status, &endptr))
+ return FALSE;
+
+ set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
+ return TRUE;
+}
+
+
+/*
+ Convert a string with INTERVAL DAY TO SECOND to MYSQL_TIME.
+ Input format: [-][DD ]hh:mm:ss.ffffff
+
+ If the input string appears to be a DATETIME, error is returned.
+*/
+my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *ltime,
+ ulong max_hour, MYSQL_TIME_STATUS *status)
+{
+ my_bool neg;
+ const char *endptr;
+
+ my_time_status_init(status);
+ if (find_body(&neg, str, length, ltime, &status->warnings, &str, &length))
+ return TRUE;
+
+ /* Reject anything that might be parsed as a full TIMESTAMP */
+ if (is_datetime_body_candidate(str, length, FALSE, FALSE))
+ {
+ uint number_of_fields;
+ (void) str_to_datetime_or_date_body(str, length, ltime, 0, FALSE,
+ status, &number_of_fields, &endptr);
+ 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,
+ UINT_MAX32, status, &endptr) ||
+ (endptr < str + length && endptr[0] == '-'))
+ return TRUE;
+ return FALSE;
+}
+
+
+my_bool
+str_to_datetime_or_date_or_time(const char *str, size_t length,
+ MYSQL_TIME *to, ulonglong mode,
+ MYSQL_TIME_STATUS *status,
+ ulong time_max_hour,
+ ulong time_err_hour)
+{
+ my_bool neg;
+ DBUG_ASSERT(C_FLAGS_OK(mode));
+ my_time_status_init(status);
+ return
+ find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
+ str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
+ time_max_hour, time_err_hour,
+ FALSE, FALSE) ||
+ set_neg(neg, status, to);
+}
+
+
+my_bool
+str_to_datetime_or_date_or_interval_hhmmssff(const char *str, size_t length,
+ MYSQL_TIME *to, ulonglong mode,
+ MYSQL_TIME_STATUS *status,
+ ulong time_max_hour,
+ ulong time_err_hour)
+{
+ my_bool neg;
+ DBUG_ASSERT(C_FLAGS_OK(mode));
+ my_time_status_init(status);
+ return
+ find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
+ str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
+ time_max_hour, time_err_hour,
+ TRUE, FALSE) ||
+ set_neg(neg, status, to);
+}
+
+
+my_bool
+str_to_datetime_or_date_or_interval_day(const char *str, size_t length,
+ MYSQL_TIME *to, ulonglong mode,
+ MYSQL_TIME_STATUS *status,
+ ulong time_max_hour,
+ ulong time_err_hour)
+{
+ my_bool neg;
+ DBUG_ASSERT(C_FLAGS_OK(mode));
+ my_time_status_init(status);
+ /*
+ For backward compatibility we allow to parse non-delimited
+ values as DATE rather than as TIME:
+ EXTRACT(DAY FROM '11111')
+ */
+ return
+ find_body(&neg, str, length, to, &status->warnings, &str, &length) ||
+ str_to_datetime_or_date_or_time_body(str, length, to, mode, status,
+ time_max_hour, time_err_hour,
+ TRUE, TRUE) ||
+ set_neg(neg, status, to);
+}
+
+
+my_bool
+str_to_datetime_or_date(const char *str, size_t length, MYSQL_TIME *l_time,
+ ulonglong flags, MYSQL_TIME_STATUS *status)
+{
+ my_bool neg;
+ uint number_of_fields;
+ const char *endptr;
+ DBUG_ASSERT(C_FLAGS_OK(flags));
+ my_time_status_init(status);
+ return
+ find_body(&neg, str, length, l_time, &status->warnings, &str, &length) ||
+ str_to_datetime_or_date_body(str, length, l_time, flags, TRUE,
+ status, &number_of_fields, &endptr) ||
+ set_neg(neg, status, l_time);
+}
+
+
+
+/**
+ Convert a string to INTERVAL DAY TO SECOND.
+ Input format: [DD ]hh:mm:ss.ffffff
+
+ Datetime or date formats are not understood.
+
+ Optional leading spaces and signs must be scanned by the caller.
+ "str" should point to the first digit.
+
+ @param neg - set the value to be negative
+ @param str - the input string
+ @param length - length of "str"
+ @param[OUT] l_time - write the result here
+ @param max_hour - if the result hour value appears to be greater than
+ max_hour, then cut to result to 'max_hour:59:59.999999'
+ @param err_hour - if the hour appears to be greater than err_hour,
+ return an error (without cut)
+ @param status
+ @param endptr
+*/
+static my_bool
+str_to_DDhhmmssff_internal(my_bool neg, const char *str, size_t length,
+ MYSQL_TIME *l_time,
+ ulong max_hour, ulong err_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');
+ if (value >= 42949672955959ULL) /* i.e. UINT_MAX32 : 59 : 59 */
+ {
+ status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ goto err;
+ }
+ }
/* Skip all space after 'days' */
end_of_days= str;
@@ -514,6 +947,11 @@ my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
my_isdigit(&my_charset_latin1, str[1]))
{
date[0]= 0; /* Assume we found hours */
+ if (value >= UINT_MAX32)
+ {
+ status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+ goto err;
+ }
date[1]= (ulong) value;
state=2;
found_hours=1;
@@ -523,6 +961,7 @@ my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
{
/* String given as one number; assume HHMMSS format */
date[0]= 0;
+ DBUG_ASSERT(value <= ((ulonglong) UINT_MAX32) * 10000ULL);
date[1]= (ulong) (value/10000);
date[2]= (ulong) (value/100 % 100);
date[3]= (ulong) (value % 100);
@@ -557,7 +996,7 @@ my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
fractional:
/* Get fractional second part */
- if (!status->warnings && str < end && *str == '.')
+ if (str < end && *str == '.')
{
uint number_of_fields= 0;
str++;
@@ -605,6 +1044,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;
@@ -614,27 +1058,20 @@ 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, err_hour,
+ 6, &status->warnings))
return TRUE;
/* Check if there is garbage at end of the MYSQL_TIME specification */
if (str != end)
- {
- do
- {
- if (!my_isspace(&my_charset_latin1,*str))
- {
- status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
- break;
- }
- } while (++str != end);
- }
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
return FALSE;
err:
- bzero((char*) l_time, sizeof(*l_time));
- l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+ *endptr= str;
return TRUE;
}
@@ -643,8 +1080,11 @@ 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. if the hour is greater,
+ cut the time value to 'max_hour:59:59.999999'
+ ulong err_hour - if hour is greater than this value, return an error
uint dec
warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
@@ -658,13 +1098,16 @@ 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, ulong err_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};
- if (my_time->minute >= 60 || my_time->second >= 60)
+ if (my_time->minute >= 60 || my_time->second >= 60 ||
+ my_time->hour > err_hour)
{
*warning|= MYSQL_TIME_WARN_TRUNCATED;
return 1;
@@ -675,14 +1118,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];
@@ -1031,6 +1474,46 @@ static char* fmt_number(uint val, char *out, uint digits)
}
+static int my_mmssff_to_str(const MYSQL_TIME *ltime, char *to, uint fsp)
+{
+ char *pos= to;
+ if (fsp == AUTO_SEC_PART_DIGITS)
+ fsp= ltime->second_part ? TIME_SECOND_PART_DIGITS : 0;
+ DBUG_ASSERT(fsp <= TIME_SECOND_PART_DIGITS);
+ pos= fmt_number(ltime->minute, pos, 2);
+ *pos++= ':';
+ pos= fmt_number(ltime->second, pos, 2);
+ if (fsp)
+ {
+ *pos++= '.';
+ pos= fmt_number((uint)sec_part_shift(ltime->second_part, fsp), pos, fsp);
+ }
+ return (int) (pos - to);
+}
+
+
+int my_interval_DDhhmmssff_to_str(const MYSQL_TIME *ltime, char *to, uint fsp)
+{
+ uint hour= ltime->day * 24 + ltime->hour;
+ char *pos= to;
+ DBUG_ASSERT(!ltime->year);
+ DBUG_ASSERT(!ltime->month);
+
+ if(ltime->neg)
+ *pos++= '-';
+ if (hour >= 24)
+ {
+ pos= longlong10_to_str((longlong) hour / 24, pos, 10);
+ *pos++= ' ';
+ }
+ pos= fmt_number(hour % 24, pos, 2);
+ *pos++= ':';
+ pos+= my_mmssff_to_str(ltime, pos, fsp);
+ *pos= 0;
+ return (int) (pos-to);
+}
+
+
/*
Functions to convert time/date/datetime value to a string,
using default format.
@@ -1048,11 +1531,6 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
uint hour= day * 24 + l_time->hour;
char*pos= to;
- if (digits == AUTO_SEC_PART_DIGITS)
- digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
-
- DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
-
if(l_time->neg)
*pos++= '-';
@@ -1063,17 +1541,7 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
pos= fmt_number(hour, pos, 2);
*pos++= ':';
- pos= fmt_number(l_time->minute, pos, 2);
- *pos++= ':';
- pos= fmt_number(l_time->second, pos, 2);
-
- if (digits)
- {
- *pos++= '.';
- pos= fmt_number((uint)sec_part_shift(l_time->second_part, digits),
- pos, digits);
- }
-
+ pos+= my_mmssff_to_str(l_time, pos, digits);
*pos= 0;
return (int) (pos-to);
}
@@ -1095,12 +1563,6 @@ int my_date_to_str(const MYSQL_TIME *l_time, char *to)
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
{
char *pos= to;
-
- if (digits == AUTO_SEC_PART_DIGITS)
- digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0;
-
- DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS);
-
pos= fmt_number(l_time->year, pos, 4);
*pos++='-';
pos= fmt_number(l_time->month, pos, 2);
@@ -1109,17 +1571,7 @@ int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
*pos++=' ';
pos= fmt_number(l_time->hour, pos, 2);
*pos++= ':';
- pos= fmt_number(l_time->minute, pos, 2);
- *pos++= ':';
- pos= fmt_number(l_time->second, pos, 2);
-
- if (digits)
- {
- *pos++='.';
- pos= fmt_number((uint) sec_part_shift(l_time->second_part, digits), pos,
- digits);
- }
-
+ pos+= my_mmssff_to_str(l_time, pos, digits);
*pos= 0;
return (int)(pos - to);
}
@@ -1185,7 +1637,7 @@ int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
representation and form value of DATETIME type as side-effect.
SYNOPSIS
- number_to_datetime()
+ number_to_datetime_or_date()
nr - datetime value as number
time_res - pointer for structure for broken-down representation
flags - flags to use in validating date, as in str_to_datetime()
@@ -1206,10 +1658,12 @@ int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
Datetime value in YYYYMMDDHHMMSS format.
*/
-longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
- ulonglong flags, int *was_cut)
+longlong number_to_datetime_or_date(longlong nr, ulong sec_part,
+ MYSQL_TIME *time_res,
+ ulonglong flags, int *was_cut)
{
long part1,part2;
+ DBUG_ASSERT(C_FLAGS_OK(flags));
*was_cut= 0;
time_res->time_type=MYSQL_TIMESTAMP_DATE;
@@ -1274,13 +1728,17 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
!check_date(time_res, nr || sec_part, flags, was_cut))
{
if (time_res->time_type == MYSQL_TIMESTAMP_DATE && sec_part != 0)
- *was_cut= MYSQL_TIME_NOTE_TRUNCATED;
+ {
+ /* Date format, but with fractional digits, e.g. 20010203.5 */
+ *was_cut= MYSQL_TIME_NOTE_TRUNCATED;
+ time_res->second_part= 0;
+ }
return nr;
}
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
- if (nr || !(flags & TIME_NO_ZERO_DATE))
- *was_cut= 1;
+ if (nr || !(flags & C_TIME_NO_ZERO_DATE))
+ *was_cut= MYSQL_TIME_WARN_TRUNCATED;
return -1;
err:
@@ -1289,7 +1747,7 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
enum enum_mysql_timestamp_type save= time_res->time_type;
bzero((char*) time_res, sizeof(*time_res));
time_res->time_type= save; /* Restore range */
- *was_cut= 1; /* Found invalid date */
+ *was_cut= MYSQL_TIME_WARN_TRUNCATED; /* Found invalid date */
}
return -1;
}
@@ -1310,22 +1768,20 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
0 time value is valid, but was possibly truncated
-1 time value is invalid
*/
-int number_to_time(my_bool neg, ulonglong nr, ulong sec_part,
- MYSQL_TIME *ltime, int *was_cut)
+int number_to_time_only(my_bool neg, ulonglong nr, ulong sec_part,
+ ulong max_hour, MYSQL_TIME *ltime, int *was_cut)
{
- if (nr > 9999999 && nr < 99991231235959ULL && neg == 0)
- return number_to_datetime(nr, sec_part, ltime,
- TIME_INVALID_DATES, was_cut) < 0 ? -1 : 0;
-
+ static const ulonglong TIME_MAX_mmss= TIME_MAX_MINUTE*100 + TIME_MAX_SECOND;
+ ulonglong time_max_value= max_hour * 10000ULL + TIME_MAX_mmss;
*was_cut= 0;
ltime->year= ltime->month= ltime->day= 0;
ltime->time_type= MYSQL_TIMESTAMP_TIME;
ltime->neg= neg;
- if (nr > TIME_MAX_VALUE)
+ if (nr > time_max_value)
{
- nr= TIME_MAX_VALUE;
+ nr= time_max_value;
sec_part= TIME_MAX_SECOND_PART;
*was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
}