summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/client.c767
-rw-r--r--sql-common/client_plugin.c37
-rw-r--r--sql-common/my_time.c647
-rw-r--r--sql-common/my_user.c28
-rw-r--r--sql-common/mysql_async.c94
-rw-r--r--sql-common/pack.c96
6 files changed, 981 insertions, 688 deletions
diff --git a/sql-common/client.c b/sql-common/client.c
index ec992f80e8d..8c093abf7e0 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -35,12 +35,9 @@
*/
#include <my_global.h>
-
+#include <my_default.h>
#include "mysql.h"
-
-#ifndef __WIN__
-#include <netdb.h>
-#endif
+#include "hash.h"
/* Remove client convenience wrappers */
#undef max_allowed_packet
@@ -62,6 +59,7 @@ my_bool net_flush(NET *net);
#else /*EMBEDDED_LIBRARY*/
#define CLI_MYSQL_REAL_CONNECT STDCALL mysql_real_connect
#endif /*EMBEDDED_LIBRARY*/
+
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
@@ -70,6 +68,7 @@ my_bool net_flush(NET *net);
#include "mysqld_error.h"
#include "errmsg.h"
#include <violite.h>
+
#if !defined(__WIN__)
#include <my_pthread.h> /* because of signal() */
#endif /* !defined(__WIN__) */
@@ -77,14 +76,12 @@ my_bool net_flush(NET *net);
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
+
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
+
#if !defined(__WIN__)
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
#ifdef HAVE_SELECT_H
# include <select.h>
#endif
@@ -96,9 +93,7 @@ my_bool net_flush(NET *net);
# include <sys/un.h>
#endif
-#if defined(__WIN__)
-#define perror(A)
-#else
+#ifndef _WIN32
#include <errno.h>
#define SOCKET_ERROR -1
#endif
@@ -132,11 +127,7 @@ const char *def_shared_memory_base_name= default_shared_memory_base_name;
static void mysql_close_free_options(MYSQL *mysql);
static void mysql_close_free(MYSQL *mysql);
static void mysql_prune_stmt_list(MYSQL *mysql);
-static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length);
-
-#if !defined(__WIN__)
-static int wait_for_data(my_socket fd, uint timeout);
-#endif
+static int cli_report_progress(MYSQL *mysql, char *packet, uint length);
CHARSET_INFO *default_client_charset_info = &my_charset_latin1;
@@ -144,187 +135,36 @@ CHARSET_INFO *default_client_charset_info = &my_charset_latin1;
unsigned int mysql_server_last_errno;
char mysql_server_last_error[MYSQL_ERRMSG_SIZE];
-/****************************************************************************
- A modified version of connect(). my_connect() allows you to specify
- a timeout value, in seconds, that we should wait until we
- derermine we can't connect to a particular host. If timeout is 0,
- my_connect() will behave exactly like connect().
-
- Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
-*****************************************************************************/
-
-int my_connect(my_socket fd, const struct sockaddr *name, uint namelen,
- uint timeout)
-{
-#if defined(__WIN__)
- DBUG_ENTER("my_connect");
- DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen));
-#else
- int flags, res, s_err;
- DBUG_ENTER("my_connect");
- DBUG_PRINT("enter", ("fd: %d timeout: %u", fd, timeout));
-
- /*
- If they passed us a timeout of zero, we should behave
- exactly like the normal connect() call does.
- */
-
- if (timeout == 0)
- DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen));
-
- flags = fcntl(fd, F_GETFL, 0); /* Set socket to not block */
-#ifdef O_NONBLOCK
- fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
-#endif
-
- DBUG_PRINT("info", ("connecting non-blocking"));
- res= connect(fd, (struct sockaddr*) name, namelen);
- DBUG_PRINT("info", ("connect result: %d errno: %d", res, errno));
- s_err= errno; /* Save the error... */
- fcntl(fd, F_SETFL, flags);
- if ((res != 0) && (s_err != EINPROGRESS))
- {
- errno= s_err; /* Restore it */
- DBUG_RETURN(-1);
- }
- if (res == 0) /* Connected quickly! */
- DBUG_RETURN(0);
- DBUG_RETURN(wait_for_data(fd, timeout));
-#endif
-}
-
+/**
+ Convert the connect timeout option to a timeout value for VIO
+ functions (vio_socket_connect() and vio_io_wait()).
-/*
- Wait up to timeout seconds for a connection to be established.
+ @param mysql Connection handle (client side).
- We prefer to do this with poll() as there is no limitations with this.
- If not, we will use select()
+ @return The timeout value in milliseconds, or -1 if no timeout.
*/
-#if !defined(__WIN__)
-
-static int wait_for_data(my_socket fd, uint timeout)
+static int get_vio_connect_timeout(MYSQL *mysql)
{
-#ifdef HAVE_POLL
- struct pollfd ufds;
- int res;
- DBUG_ENTER("wait_for_data");
-
- DBUG_PRINT("info", ("polling"));
- ufds.fd= fd;
- ufds.events= POLLIN | POLLPRI;
- if (!(res= poll(&ufds, 1, (int) timeout*1000)))
- {
- DBUG_PRINT("info", ("poll timed out"));
- errno= EINTR;
- DBUG_RETURN(-1);
- }
- DBUG_PRINT("info",
- ("poll result: %d errno: %d revents: 0x%02d events: 0x%02d",
- res, errno, ufds.revents, ufds.events));
- if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI)))
- DBUG_RETURN(-1);
- /*
- At this point, we know that something happened on the socket.
- But this does not means that everything is alright.
- The connect might have failed. We need to retrieve the error code
- from the socket layer. We must return success only if we are sure
- that it was really a success. Otherwise we might prevent the caller
- from trying another address to connect to.
- */
- {
- int s_err;
- socklen_t s_len= sizeof(s_err);
-
- DBUG_PRINT("info", ("Get SO_ERROR from non-blocked connected socket."));
- res= getsockopt(fd, SOL_SOCKET, SO_ERROR, &s_err, &s_len);
- DBUG_PRINT("info", ("getsockopt res: %d s_err: %d", res, s_err));
- if (res)
- DBUG_RETURN(res);
- /* getsockopt() was successful, check the retrieved status value. */
- if (s_err)
- {
- errno= s_err;
- DBUG_RETURN(-1);
- }
- /* Status from connect() is zero. Socket is successfully connected. */
- }
- DBUG_RETURN(0);
-#else
- SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint);
- fd_set sfds;
- struct timeval tv;
- time_t start_time, now_time;
- int res, s_err;
- DBUG_ENTER("wait_for_data");
-
- if (fd >= FD_SETSIZE) /* Check if wrong error */
- DBUG_RETURN(0); /* Can't use timeout */
+ int timeout_ms;
+ uint timeout_sec;
/*
- Our connection is "in progress." We can use the select() call to wait
- up to a specified period of time for the connection to suceed.
- If select() returns 0 (after waiting howevermany seconds), our socket
- never became writable (host is probably unreachable.) Otherwise, if
- select() returns 1, then one of two conditions exist:
-
- 1. An error occured. We use getsockopt() to check for this.
- 2. The connection was set up sucessfully: getsockopt() will
- return 0 as an error.
-
- Thanks goes to Andrew Gierth <andrew@erlenstar.demon.co.uk>
- who posted this method of timing out a connect() in
- comp.unix.programmer on August 15th, 1997.
+ A timeout of 0 means no timeout. Also, the connect_timeout
+ option value is in seconds, while VIO timeouts are measured
+ in milliseconds. Hence, check for a possible overflow. In
+ case of overflow, set to no timeout.
*/
+ timeout_sec= mysql->options.connect_timeout;
- FD_ZERO(&sfds);
- FD_SET(fd, &sfds);
- /*
- select could be interrupted by a signal, and if it is,
- the timeout should be adjusted and the select restarted
- to work around OSes that don't restart select and
- implementations of select that don't adjust tv upon
- failure to reflect the time remaining
- */
- start_time= my_time(0);
- for (;;)
- {
- tv.tv_sec = (long) timeout;
- tv.tv_usec = 0;
-#if defined(HPUX10)
- if ((res = select(fd+1, NULL, (int*) &sfds, NULL, &tv)) > 0)
- break;
-#else
- if ((res = select(fd+1, NULL, &sfds, NULL, &tv)) > 0)
- break;
-#endif
- if (res == 0) /* timeout */
- DBUG_RETURN(-1);
- now_time= my_time(0);
- timeout-= (uint) (now_time - start_time);
- if (errno != EINTR || (int) timeout <= 0)
- DBUG_RETURN(-1);
- }
-
- /*
- select() returned something more interesting than zero, let's
- see if we have any errors. If the next two statements pass,
- we've got an open socket!
- */
-
- s_err=0;
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
- DBUG_RETURN(-1);
+ if (!timeout_sec || (timeout_sec > INT_MAX/1000))
+ timeout_ms= -1;
+ else
+ timeout_ms= (int) (timeout_sec * 1000);
- if (s_err)
- { /* getsockopt could succeed */
- errno = s_err;
- DBUG_RETURN(-1); /* but return an error... */
- }
- DBUG_RETURN(0); /* ok */
-#endif /* HAVE_POLL */
+ return timeout_ms;
}
-#endif /* !defined(__WIN__) */
+
/**
Set the internal error message to mysql handler
@@ -738,14 +578,14 @@ cli_safe_read(MYSQL *mysql)
restart:
if (net->vio != 0)
- len=my_net_read(net);
-
+ len= my_net_read_packet(net, 0);
+
if (len == packet_error || len == 0)
{
DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %lu",
vio_description(net->vio),len));
#ifdef MYSQL_SERVER
- if (net->vio && vio_was_interrupted(net->vio))
+ if (net->vio && (net->last_errno == ER_NET_READ_INTERRUPTED))
return (packet_error);
#endif /*MYSQL_SERVER*/
end_server(mysql);
@@ -757,7 +597,7 @@ restart:
{
if (len > 3)
{
- uchar *pos= net->read_pos+1;
+ char *pos= (char*) net->read_pos+1;
uint last_errno=uint2korr(pos);
if (last_errno == 65535 &&
@@ -775,9 +615,9 @@ restart:
pos+=2;
len-=2;
- if (protocol_41(mysql) && (char) pos[0] == '#')
+ if (protocol_41(mysql) && pos[0] == '#')
{
- strmake_buf(net->sqlstate, (char*) pos+1);
+ strmake_buf(net->sqlstate, pos+1);
pos+= SQLSTATE_LENGTH+1;
}
else
@@ -791,7 +631,7 @@ restart:
}
(void) strmake(net->last_error,(char*) pos,
- min((uint) len,(uint) sizeof(net->last_error)-1));
+ MY_MIN((uint) len,(uint) sizeof(net->last_error)-1));
}
else
set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
@@ -892,7 +732,10 @@ void free_old_query(MYSQL *mysql)
DBUG_ENTER("free_old_query");
if (mysql->fields)
free_root(&mysql->field_alloc,MYF(0));
- init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */
+ /* Assume rowlength < 8192 */
+ init_alloc_root(&mysql->field_alloc, 8192, 0,
+ MYF(mysql->options.use_thread_specific_memory ?
+ MY_THREAD_SPECIFIC : 0));
mysql->fields= 0;
mysql->field_count= 0; /* For API */
mysql->warning_count= 0;
@@ -1038,10 +881,11 @@ static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results)
1 error
*/
-static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length)
+static int cli_report_progress(MYSQL *mysql, char *pkt, uint length)
{
uint stage, max_stage, proc_length;
double progress;
+ uchar *packet= (uchar*)pkt;
uchar *start= packet;
if (length < 5)
@@ -1140,6 +984,7 @@ static const char *default_options[]=
"ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
"multi-results", "multi-statements", "multi-queries", "secure-auth",
"report-data-truncation", "plugin-dir", "default-auth",
+ "bind-address", "ssl-crl", "ssl-crlpath",
"enable-cleartext-plugin",
NullS
};
@@ -1152,6 +997,7 @@ enum option_id {
OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
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,
OPT_enable_cleartext_plugin,
OPT_keep_this_one_last
};
@@ -1172,11 +1018,11 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
{
options->init_commands= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY),
MYF(MY_WME));
- init_dynamic_array(options->init_commands,sizeof(char*),5,5);
+ my_init_dynamic_array(options->init_commands,sizeof(char*),5, 5, MYF(0));
}
if (!(tmp= my_strdup(cmd,MYF(MY_WME))) ||
- insert_dynamic(options->init_commands, (uchar*)&tmp))
+ insert_dynamic(options->init_commands, &tmp))
{
my_free(tmp);
return 1;
@@ -1185,31 +1031,57 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
return 0;
}
-#define EXTENSION_SET(OPTS, X, VAL) \
- if (!(OPTS)->extension) \
+
+#define ALLOCATE_EXTENSIONS(OPTS) \
(OPTS)->extension= (struct st_mysql_options_extention *) \
my_malloc(sizeof(struct st_mysql_options_extention), \
- MYF(MY_WME | MY_ZEROFILL)); \
- (OPTS)->extension->X= VAL;
+ MYF(MY_WME | MY_ZEROFILL)) \
+
+
+#define ENSURE_EXTENSIONS_PRESENT(OPTS) \
+ do { \
+ if (!(OPTS)->extension) \
+ ALLOCATE_EXTENSIONS(OPTS); \
+ } while (0)
+
+
+#define EXTENSION_SET_STRING_X(OPTS, X, STR, dup) \
+ do { \
+ if ((OPTS)->extension) \
+ my_free((OPTS)->extension->X); \
+ else \
+ ALLOCATE_EXTENSIONS(OPTS); \
+ (OPTS)->extension->X= ((STR) != NULL) ? \
+ dup((STR), MYF(MY_WME)) : NULL; \
+ } while (0)
+
+#define EXTENSION_SET_STRING(OPTS, X, STR) \
+ EXTENSION_SET_STRING_X(OPTS, X, STR, my_strdup)
-#define EXTENSION_SET_STRING(OPTS, X, STR) \
- if ((OPTS)->extension) \
- my_free((OPTS)->extension->X); \
- EXTENSION_SET(OPTS, X, my_strdup((STR), MYF(MY_WME)));
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-static char *set_ssl_option_unpack_path(const char *arg)
+#define SET_SSL_OPTION_X(OPTS, opt_var, arg, dup) \
+ my_free((OPTS)->opt_var); \
+ (OPTS)->opt_var= arg ? dup(arg, MYF(MY_WME)) : NULL;
+#define EXTENSION_SET_SSL_STRING_X(OPTS, X, STR, dup) \
+ EXTENSION_SET_STRING_X((OPTS), X, (STR), dup);
+
+static char *set_ssl_option_unpack_path(const char *arg, myf flags)
{
- char *opt_var= NULL;
- if (arg)
- {
- char buff[FN_REFLEN + 1];
- unpack_filename(buff, (char *)arg);
- opt_var= my_strdup(buff, MYF(MY_WME));
- }
- return opt_var;
+ char buff[FN_REFLEN + 1];
+ unpack_filename(buff, (char *)arg);
+ return my_strdup(buff, flags);
}
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
+
+#else
+#define SET_SSL_OPTION_X(OPTS, opt_var,arg, dup) do { } while(0)
+#define EXTENSION_SET_SSL_STRING_X(OPTS, X, STR, dup) do { } while(0)
+#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
+
+#define SET_SSL_OPTION(OPTS, opt_var,arg) SET_SSL_OPTION_X(OPTS, opt_var, arg, my_strdup)
+#define EXTENSION_SET_SSL_STRING(OPTS, X, STR) EXTENSION_SET_SSL_STRING_X(OPTS, X, STR, my_strdup)
+#define SET_SSL_PATH_OPTION(OPTS, opt_var,arg) SET_SSL_OPTION_X(OPTS, opt_var, arg, set_ssl_option_unpack_path)
+#define EXTENSION_SET_SSL_PATH_STRING(OPTS, X, STR) EXTENSION_SET_SSL_STRING_X(OPTS, X, STR, set_ssl_option_unpack_path)
void mysql_read_default_options(struct st_mysql_options *options,
const char *filename,const char *group)
@@ -1314,35 +1186,27 @@ void mysql_read_default_options(struct st_mysql_options *options,
case OPT_return_found_rows:
options->client_flag|=CLIENT_FOUND_ROWS;
break;
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
case OPT_ssl_key:
- my_free(options->ssl_key);
- options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
+ SET_SSL_OPTION(options, ssl_key, opt_arg);
break;
case OPT_ssl_cert:
- my_free(options->ssl_cert);
- options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME));
+ SET_SSL_OPTION(options, ssl_cert, opt_arg);
break;
case OPT_ssl_ca:
- my_free(options->ssl_ca);
- options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME));
+ SET_SSL_OPTION(options, ssl_ca, opt_arg);
break;
case OPT_ssl_capath:
- my_free(options->ssl_capath);
- options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME));
+ SET_SSL_OPTION(options, ssl_capath, opt_arg);
break;
case OPT_ssl_cipher:
- my_free(options->ssl_cipher);
- options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME));
+ SET_SSL_OPTION(options, ssl_cipher, opt_arg);
+ break;
+ case OPT_ssl_crl:
+ EXTENSION_SET_SSL_STRING(options, ssl_crl, opt_arg);
+ break;
+ case OPT_ssl_crlpath:
+ EXTENSION_SET_SSL_STRING(options, ssl_crlpath, opt_arg);
break;
-#else
- case OPT_ssl_key:
- case OPT_ssl_cert:
- case OPT_ssl_ca:
- case OPT_ssl_capath:
- case OPT_ssl_cipher:
- break;
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
case OPT_character_sets_dir:
my_free(options->charset_dir);
options->charset_dir = my_strdup(opt_arg, MYF(MY_WME));
@@ -1393,11 +1257,11 @@ void mysql_read_default_options(struct st_mysql_options *options,
options->secure_auth= TRUE;
break;
case OPT_report_data_truncation:
- options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1;
+ options->report_data_truncation= opt_arg ? MY_TEST(atoi(opt_arg)) : 1;
break;
case OPT_plugin_dir:
{
- char buff[FN_REFLEN], buff2[FN_REFLEN];
+ char buff[FN_REFLEN], buff2[FN_REFLEN], *buff2_ptr= buff2;
if (strlen(opt_arg) >= FN_REFLEN)
opt_arg[FN_REFLEN]= '\0';
if (my_realpath(buff, opt_arg, 0))
@@ -1407,7 +1271,7 @@ void mysql_read_default_options(struct st_mysql_options *options,
break;
}
convert_dirname(buff2, buff, NULL);
- EXTENSION_SET_STRING(options, plugin_dir, buff2);
+ EXTENSION_SET_STRING(options, plugin_dir, buff2_ptr);
}
break;
case OPT_default_auth:
@@ -1604,7 +1468,10 @@ MYSQL_DATA *cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
DBUG_RETURN(0);
}
- init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */
+ /* Assume rowlength < 8192 */
+ init_alloc_root(&result->alloc, 8192, 0,
+ MYF(mysql->options.use_thread_specific_memory ?
+ MY_THREAD_SPECIFIC : 0));
result->alloc.min_malloc=sizeof(MYSQL_ROWS);
prev_ptr= &result->data;
result->rows=0;
@@ -1798,12 +1665,11 @@ mysql_init(MYSQL *mysql)
/*
- Fill in SSL part of MYSQL structure and set 'use_ssl' flag.
+ Fill in SSL part of MYSQL structure.
NB! Errors are not reported until you do mysql_real_connect.
+ use_ssl is set in send_client_reply_packet if any ssl option is set.
*/
-#define strdup_if_not_null(A) (A) == 0 ? 0 : my_strdup((A),MYF(MY_WME))
-
my_bool STDCALL
mysql_ssl_set(MYSQL *mysql __attribute__((unused)) ,
const char *key __attribute__((unused)),
@@ -1812,21 +1678,18 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) ,
const char *capath __attribute__((unused)),
const char *cipher __attribute__((unused)))
{
+ my_bool result= 0;
DBUG_ENTER("mysql_ssl_set");
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- my_free(mysql->options.ssl_key);
- my_free(mysql->options.ssl_cert);
- my_free(mysql->options.ssl_ca);
- my_free(mysql->options.ssl_capath);
- my_free(mysql->options.ssl_cipher);
- mysql->options.ssl_key= set_ssl_option_unpack_path(key);
- mysql->options.ssl_cert= set_ssl_option_unpack_path(cert);
- mysql->options.ssl_ca= set_ssl_option_unpack_path(ca);
- mysql->options.ssl_capath= set_ssl_option_unpack_path(capath);
- mysql->options.ssl_cipher= strdup_if_not_null(cipher);
+ result= (mysql_options(mysql, MYSQL_OPT_SSL_KEY, key) |
+ mysql_options(mysql, MYSQL_OPT_SSL_CERT, cert) |
+ mysql_options(mysql, MYSQL_OPT_SSL_CA, ca) |
+ mysql_options(mysql, MYSQL_OPT_SSL_CAPATH, capath) |
+ mysql_options(mysql, MYSQL_OPT_SSL_CIPHER, cipher) ?
+ 1 : 0);
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
mysql->options.use_ssl= TRUE;
- DBUG_RETURN(0);
+ DBUG_RETURN(result);
}
@@ -1848,6 +1711,11 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
my_free(mysql->options.ssl_ca);
my_free(mysql->options.ssl_capath);
my_free(mysql->options.ssl_cipher);
+ if (mysql->options.extension)
+ {
+ my_free(mysql->options.extension->ssl_crl);
+ my_free(mysql->options.extension->ssl_crlpath);
+ }
if (ssl_fd)
SSL_CTX_free(ssl_fd->ssl_context);
my_free(mysql->connector_fd);
@@ -1856,6 +1724,11 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
mysql->options.ssl_ca = 0;
mysql->options.ssl_capath = 0;
mysql->options.ssl_cipher= 0;
+ if (mysql->options.extension)
+ {
+ mysql->options.extension->ssl_crl = 0;
+ mysql->options.extension->ssl_crlpath = 0;
+ }
mysql->options.use_ssl = FALSE;
mysql->connector_fd = 0;
DBUG_VOID_RETURN;
@@ -2397,6 +2270,7 @@ static auth_plugin_t old_password_client_plugin=
old_password_auth_client
};
+
struct st_mysql_client_plugin *mysql_client_builtins[]=
{
(struct st_mysql_client_plugin *)&native_password_client_plugin,
@@ -2404,6 +2278,67 @@ struct st_mysql_client_plugin *mysql_client_builtins[]=
0
};
+
+static uchar *
+write_length_encoded_string3(uchar *buf, char *string, size_t length)
+{
+ buf= net_store_length(buf, length);
+ memcpy(buf, string, length);
+ buf+= length;
+ return buf;
+}
+
+
+uchar *
+send_client_connect_attrs(MYSQL *mysql, uchar *buf)
+{
+ /* check if the server supports connection attributes */
+ if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS)
+ {
+
+ /* Always store the length if the client supports it */
+ buf= net_store_length(buf,
+ mysql->options.extension ?
+ mysql->options.extension->connection_attributes_length :
+ 0);
+
+ /* check if we have connection attributes */
+ if (mysql->options.extension &&
+ my_hash_inited(&mysql->options.extension->connection_attributes))
+ {
+ HASH *attrs= &mysql->options.extension->connection_attributes;
+ ulong idx;
+
+ /* loop over and dump the connection attributes */
+ for (idx= 0; idx < attrs->records; idx++)
+ {
+ LEX_STRING *attr= (LEX_STRING *) my_hash_element(attrs, idx);
+ LEX_STRING *key= attr, *value= attr + 1;
+
+ /* we can't have zero length keys */
+ DBUG_ASSERT(key->length);
+
+ buf= write_length_encoded_string3(buf, key->str, key->length);
+ buf= write_length_encoded_string3(buf, value->str, value->length);
+ }
+ }
+ }
+ return buf;
+}
+
+
+static size_t get_length_store_length(size_t length)
+{
+ /* as defined in net_store_length */
+ #define MAX_VARIABLE_STRING_LENGTH 9
+ uchar length_buffer[MAX_VARIABLE_STRING_LENGTH], *ptr;
+
+ ptr= net_store_length(length_buffer, length);
+
+ return ptr - &length_buffer[0];
+}
+
+
/* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */
typedef struct {
int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
@@ -2422,6 +2357,29 @@ typedef struct {
int last_read_packet_len; /**< the length of the last *read* packet */
} MCPVIO_EXT;
+
+/*
+ Write 1-8 bytes of string length header infromation to dest depending on
+ value of src_len, then copy src_len bytes from src to dest.
+
+ @param dest Destination buffer of size src_len+8
+ @param dest_end One byte past the end of the dest buffer
+ @param src Source buff of size src_len
+ @param src_end One byte past the end of the src buffer
+
+ @return pointer dest+src_len+header size or NULL if
+*/
+
+static uchar *write_length_encoded_string4(uchar *dst, size_t dst_len,
+ const uchar *src, size_t src_len)
+{
+ uchar *to= safe_net_store_length(dst, dst_len, src_len);
+ if (to == NULL)
+ return NULL;
+ memcpy(to, src, src_len);
+ return to + src_len;
+}
+
/**
sends a COM_CHANGE_USER command with a caller provided payload
@@ -2447,8 +2405,13 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
MYSQL *mysql= mpvio->mysql;
char *buff, *end;
int res= 1;
+ size_t connect_attrs_len=
+ (mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
+ mysql->options.extension) ?
+ mysql->options.extension->connection_attributes_length : 0;
- buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1);
+ buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN +
+ connect_attrs_len + 9 /* for the length of the attrs */);
end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
@@ -2485,6 +2448,8 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+ end= (char *) send_client_connect_attrs(mysql, (uchar *) end);
+
res= simple_command(mysql, COM_CHANGE_USER,
(uchar*)buff, (ulong)(end-buff), 1);
@@ -2493,6 +2458,7 @@ error:
return res;
}
+#define MAX_CONNECTION_ATTR_STORAGE_LENGTH 65536
/**
sends a client authentication packet (second packet in the 3-way handshake)
@@ -2515,7 +2481,7 @@ error:
1 charset number
23 reserved (always 0)
n user name, \0-terminated
- n plugin auth data (e.g. scramble), length (1 byte) coded
+ n plugin auth data (e.g. scramble), length encoded
n database name, \0-terminated
(if CLIENT_CONNECT_WITH_DB is set in the capabilities)
n client auth plugin name - \0-terminated string,
@@ -2530,10 +2496,21 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
MYSQL *mysql= mpvio->mysql;
NET *net= &mysql->net;
char *buff, *end;
+ size_t buff_size;
+ size_t connect_attrs_len=
+ (mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
+ mysql->options.extension) ?
+ mysql->options.extension->connection_attributes_length : 0;
+
+ DBUG_ASSERT(connect_attrs_len < MAX_CONNECTION_ATTR_STORAGE_LENGTH);
+
+ /*
+ see end= buff+32 below, fixed size of the packet is 32 bytes.
+ +9 because data is a length encoded binary where meta data size is max 9.
+ */
+ buff_size= 33 + USERNAME_LENGTH + data_len + 9 + NAME_LEN + NAME_LEN + connect_attrs_len + 9;
+ buff= my_alloca(buff_size);
- /* see end= buff+32 below, fixed size of the packet is 32 bytes */
- buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN);
-
mysql->client_flag|= mysql->options.client_flag;
mysql->client_flag|= CLIENT_CAPABILITIES;
@@ -2622,7 +2599,11 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
options->ssl_ca,
options->ssl_capath,
options->ssl_cipher,
- &ssl_init_error)))
+ &ssl_init_error,
+ options->extension ?
+ options->extension->ssl_crl : NULL,
+ options->extension ?
+ options->extension->ssl_crlpath : NULL)))
{
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate,
ER(CR_SSL_CONNECTION_ERROR), sslGetErrString(ssl_init_error));
@@ -2673,9 +2654,19 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
{
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
- *end++= data_len;
- memcpy(end, data, data_len);
- end+= data_len;
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
+ end= (char*)write_length_encoded_string4((uchar*)end,
+ buff_size, data, data_len);
+ else
+ {
+ if (data_len > 255)
+ goto error;
+ *end++= data_len;
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ if (end == NULL)
+ goto error;
}
else
{
@@ -2697,6 +2688,8 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+ end= (char *) send_client_connect_attrs(mysql, (uchar *) end);
+
/* Write authentication package */
if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
{
@@ -2824,21 +2817,21 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
switch (vio->type) {
case VIO_TYPE_TCPIP:
info->protocol= MYSQL_VIO_TCP;
- info->socket= vio->sd;
+ info->socket= vio_fd(vio);
return;
case VIO_TYPE_SOCKET:
info->protocol= MYSQL_VIO_SOCKET;
- info->socket= vio->sd;
+ info->socket= vio_fd(vio);
return;
case VIO_TYPE_SSL:
{
struct sockaddr addr;
SOCKET_SIZE_TYPE addrlen= sizeof(addr);
- if (getsockname(vio->sd, &addr, &addrlen))
+ if (getsockname(vio_fd(vio), &addr, &addrlen))
return;
info->protocol= addr.sa_family == AF_UNIX ?
MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
- info->socket= vio->sd;
+ info->socket= vio_fd(vio);
return;
}
#ifdef _WIN32
@@ -3041,20 +3034,67 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
static int
connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd,
- const struct sockaddr *name, uint namelen)
+ struct sockaddr *name, uint namelen)
{
+ int vio_timeout = get_vio_connect_timeout(mysql);
+
if (mysql->options.extension && mysql->options.extension->async_context &&
mysql->options.extension->async_context->active)
{
my_bool old_mode;
vio_blocking(net->vio, FALSE, &old_mode);
return my_connect_async(mysql->options.extension->async_context, fd,
- name, namelen, mysql->options.connect_timeout);
+ name, namelen, vio_timeout);
}
- return my_connect(fd, name, namelen, mysql->options.connect_timeout);
+ return vio_socket_connect(net->vio, name, namelen, vio_timeout);
}
+
+/** set some default attributes */
+static int
+set_connect_attributes(MYSQL *mysql, char *buff, size_t buf_len)
+{
+ int rc= 0;
+
+ /*
+ Clean up any values set by the client code. We want these options as
+ consistent as possible
+ */
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name");
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os");
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform");
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid");
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread");
+ rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version");
+
+ /*
+ Now let's set up some values
+ */
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+ "_client_name", "libmysql");
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+ "_client_version", PACKAGE_VERSION);
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+ "_os", SYSTEM_TYPE);
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+ "_platform", MACHINE_TYPE);
+#ifdef __WIN__
+ snprintf(buff, buf_len, "%lu", (ulong) GetCurrentProcessId());
+#else
+ snprintf(buff, buf_len, "%lu", (ulong) getpid());
+#endif
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buff);
+
+#ifdef __WIN__
+ snprintf(buff, buf_len, "%lu", (ulong) GetCurrentThreadId());
+ rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buff);
+#endif
+
+ return rc > 0 ? 1 : 0;
+}
+
+
MYSQL * STDCALL
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
const char *passwd, const char *db,
@@ -3088,6 +3128,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
DBUG_RETURN(0);
}
+ if (set_connect_attributes(mysql, buff, sizeof(buff)))
+ DBUG_RETURN(0);
+
mysql->methods= &client_methods;
net->vio = 0; /* If something goes wrong */
mysql->client_flag=0; /* For handshake */
@@ -3294,7 +3337,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
*/
DBUG_PRINT("info",("IPV6 getaddrinfo error %d", gai_errno));
set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
- ER(CR_UNKNOWN_HOST), host, errno);
+ ER(CR_UNKNOWN_HOST), host, gai_errno);
goto error;
}
@@ -3379,7 +3422,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
if (mysql->options.extension && mysql->options.extension->async_context)
net->vio->async_context= mysql->options.extension->async_context;
- if (my_net_init(net, net->vio))
+ if (my_net_init(net, net->vio, MYF(0)))
{
vio_delete(net->vio);
net->vio = 0;
@@ -3402,7 +3445,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
/* Get version info */
mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
if (mysql->options.connect_timeout &&
- vio_poll_read(net->vio, mysql->options.connect_timeout))
+ (vio_io_wait(net->vio, VIO_IO_EVENT_READ,
+ get_vio_connect_timeout(mysql)) < 1))
{
set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
ER(CR_SERVER_LOST_EXTENDED),
@@ -3613,6 +3657,9 @@ error:
/* Free alloced memory */
end_server(mysql);
mysql_close_free(mysql);
+ if (!(client_flag & CLIENT_REMEMBER_OPTIONS) &&
+ !mysql->options.extension->async_context)
+ mysql_close_free_options(mysql);
}
DBUG_RETURN(0);
}
@@ -3683,7 +3730,7 @@ my_bool mysql_reconnect(MYSQL *mysql)
}
if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd,
mysql->db, mysql->port, mysql->unix_socket,
- mysql->client_flag))
+ mysql->client_flag | CLIENT_REMEMBER_OPTIONS))
{
if (ctxt)
my_context_install_suspend_resume_hook(ctxt, NULL, NULL);
@@ -3787,6 +3834,7 @@ static void mysql_close_free_options(MYSQL *mysql)
struct mysql_async_context *ctxt= mysql->options.extension->async_context;
my_free(mysql->options.extension->plugin_dir);
my_free(mysql->options.extension->default_auth);
+ my_hash_free(&mysql->options.extension->connection_attributes);
if (ctxt)
{
my_context_destroy(&ctxt->async_context);
@@ -4026,7 +4074,6 @@ mysql_send_query(MYSQL* mysql, const char* query, ulong length)
DBUG_RETURN(simple_command(mysql, COM_QUERY, (uchar*) query, length, 1));
}
-
int STDCALL
mysql_real_query(MYSQL *mysql, const char *query, ulong length)
{
@@ -4237,7 +4284,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
mysql->options.protocol=MYSQL_PROTOCOL_PIPE; /* Force named pipe */
break;
case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
- if (!arg || test(*(uint*) arg))
+ if (!arg || MY_TEST(*(uint*) arg))
mysql->options.client_flag|= CLIENT_LOCAL_FILES;
else
mysql->options.client_flag&= ~CLIENT_LOCAL_FILES;
@@ -4284,11 +4331,14 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
mysql->options.secure_auth= *(my_bool *) arg;
break;
case MYSQL_REPORT_DATA_TRUNCATION:
- mysql->options.report_data_truncation= test(*(my_bool *) arg);
+ mysql->options.report_data_truncation= MY_TEST(*(my_bool*) arg);
break;
case MYSQL_OPT_RECONNECT:
mysql->reconnect= *(my_bool *) arg;
break;
+ case MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY:
+ mysql->options.use_thread_specific_memory= *(my_bool *) arg;
+ break;
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
if (*(my_bool*) arg)
mysql->options.client_flag|= CLIENT_SSL_VERIFY_SERVER_CERT;
@@ -4342,10 +4392,171 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
my_free(ctxt);
DBUG_RETURN(1);
}
- EXTENSION_SET(&(mysql->options), async_context, ctxt)
+ ENSURE_EXTENSIONS_PRESENT(&(mysql->options));
+ mysql->options.extension->async_context= ctxt;
if (mysql->net.vio)
mysql->net.vio->async_context= ctxt;
break;
+ case MYSQL_OPT_SSL_KEY:
+ SET_SSL_PATH_OPTION(&mysql->options,ssl_key, arg);
+ break;
+ case MYSQL_OPT_SSL_CERT:
+ SET_SSL_PATH_OPTION(&mysql->options, ssl_cert, arg);
+ break;
+ case MYSQL_OPT_SSL_CA:
+ SET_SSL_PATH_OPTION(&mysql->options,ssl_ca, arg);
+ break;
+ case MYSQL_OPT_SSL_CAPATH:
+ SET_SSL_PATH_OPTION(&mysql->options,ssl_capath, arg);
+ break;
+ case MYSQL_OPT_SSL_CIPHER:
+ SET_SSL_OPTION(&mysql->options,ssl_cipher, arg);
+ break;
+ case MYSQL_OPT_SSL_CRL:
+ EXTENSION_SET_SSL_PATH_STRING(&mysql->options, ssl_crl, arg);
+ break;
+ case MYSQL_OPT_SSL_CRLPATH:
+ EXTENSION_SET_SSL_PATH_STRING(&mysql->options, ssl_crlpath, arg);
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_RESET:
+ ENSURE_EXTENSIONS_PRESENT(&mysql->options);
+ if (my_hash_inited(&mysql->options.extension->connection_attributes))
+ {
+ my_hash_free(&mysql->options.extension->connection_attributes);
+ mysql->options.extension->connection_attributes_length= 0;
+ }
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_DELETE:
+ ENSURE_EXTENSIONS_PRESENT(&mysql->options);
+ if (my_hash_inited(&mysql->options.extension->connection_attributes))
+ {
+ size_t len;
+ uchar *elt;
+
+ len= arg ? strlen(arg) : 0;
+
+ if (len)
+ {
+ elt= my_hash_search(&mysql->options.extension->connection_attributes,
+ arg, len);
+ if (elt)
+ {
+ LEX_STRING *attr= (LEX_STRING *) elt;
+ LEX_STRING *key= attr, *value= attr + 1;
+
+ mysql->options.extension->connection_attributes_length-=
+ get_length_store_length(key->length) + key->length +
+ get_length_store_length(value->length) + value->length;
+
+ my_hash_delete(&mysql->options.extension->connection_attributes,
+ elt);
+
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+/**
+ A function to return the key from a connection attribute
+*/
+uchar *
+get_attr_key(LEX_STRING *part, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= part[0].length;
+ return (uchar *) part[0].str;
+}
+
+int STDCALL
+mysql_options4(MYSQL *mysql,enum mysql_option option,
+ const void *arg1, const void *arg2)
+{
+ DBUG_ENTER("mysql_option");
+ DBUG_PRINT("enter",("option: %d",(int) option));
+
+ switch (option)
+ {
+ case MYSQL_OPT_CONNECT_ATTR_ADD:
+ {
+ LEX_STRING *elt;
+ char *key, *value;
+ size_t key_len= arg1 ? strlen(arg1) : 0,
+ value_len= arg2 ? strlen(arg2) : 0;
+ size_t attr_storage_length= key_len + value_len;
+
+ /* we can't have a zero length key */
+ if (!key_len)
+ {
+ set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
+ DBUG_RETURN(1);
+ }
+
+ /* calculate the total storage length of the attribute */
+ attr_storage_length+= get_length_store_length(key_len);
+ attr_storage_length+= get_length_store_length(value_len);
+
+ ENSURE_EXTENSIONS_PRESENT(&mysql->options);
+
+ /*
+ Throw and error if the maximum combined length of the attribute value
+ will be greater than the maximum that we can safely transmit.
+ */
+ if (attr_storage_length +
+ mysql->options.extension->connection_attributes_length >
+ MAX_CONNECTION_ATTR_STORAGE_LENGTH)
+ {
+ set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
+ DBUG_RETURN(1);
+ }
+
+ if (!my_hash_inited(&mysql->options.extension->connection_attributes))
+ {
+ if (my_hash_init(&mysql->options.extension->connection_attributes,
+ &my_charset_bin, 0, 0, 0, (my_hash_get_key) get_attr_key,
+ my_free, HASH_UNIQUE))
+ {
+ set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
+ DBUG_RETURN(1);
+ }
+ }
+ if (!my_multi_malloc(MY_WME,
+ &elt, 2 * sizeof(LEX_STRING),
+ &key, key_len + 1,
+ &value, value_len + 1,
+ NULL))
+ {
+ set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
+ DBUG_RETURN(1);
+ }
+ elt[0].str= key; elt[0].length= key_len;
+ elt[1].str= value; elt[1].length= value_len;
+ if (key_len)
+ memcpy(key, arg1, key_len);
+ key[key_len]= 0;
+ if (value_len)
+ memcpy(value, arg2, value_len);
+ value[value_len]= 0;
+ if (my_hash_insert(&mysql->options.extension->connection_attributes,
+ (uchar *) elt))
+ {
+ /* can't insert the value */
+ my_free(elt);
+ set_mysql_error(mysql, CR_DUPLICATE_CONNECTION_ATTR,
+ unknown_sqlstate);
+ DBUG_RETURN(1);
+ }
+
+ mysql->options.extension->connection_attributes_length+=
+ attr_storage_length;
+
+ break;
+ }
+
default:
DBUG_RETURN(1);
}
@@ -4570,6 +4781,6 @@ my_socket STDCALL
mysql_get_socket(const MYSQL *mysql)
{
if (mysql->net.vio)
- return mysql->net.vio->sd;
+ return vio_fd(mysql->net.vio);
return INVALID_SOCKET;
}
diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c
index f31ddb22a6a..f93e50125c5 100644
--- a/sql-common/client_plugin.c
+++ b/sql-common/client_plugin.c
@@ -70,7 +70,7 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
loading the same plugin twice in parallel.
*/
struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
-static pthread_mutex_t LOCK_load_client_plugin;
+static mysql_mutex_t LOCK_load_client_plugin;
static int is_not_initialized(MYSQL *mysql, const char *name)
{
@@ -170,7 +170,7 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
goto err2;
}
- safe_mutex_assert_owner(&LOCK_load_client_plugin);
+ mysql_mutex_assert_owner(&LOCK_load_client_plugin);
p->next= plugin_list[plugin->type];
plugin_list[plugin->type]= p;
@@ -235,7 +235,7 @@ static void load_env_plugins(MYSQL *mysql)
This function must be called before any other client plugin function.
@retval 0 successful
- @retval != 0 error occured
+ @retval != 0 error occurred
*/
int mysql_client_plugin_init()
{
@@ -250,19 +250,19 @@ int mysql_client_plugin_init()
bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
- pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
- init_alloc_root(&mem_root, 128, 128);
+ mysql_mutex_init(0, &LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
+ init_alloc_root(&mem_root, 128, 128, MYF(0));
bzero(&plugin_list, sizeof(plugin_list));
initialized= 1;
- pthread_mutex_lock(&LOCK_load_client_plugin);
+ mysql_mutex_lock(&LOCK_load_client_plugin);
for (builtin= mysql_client_builtins; *builtin; builtin++)
add_plugin(&mysql, *builtin, 0, 0, unused);
- pthread_mutex_unlock(&LOCK_load_client_plugin);
+ mysql_mutex_unlock(&LOCK_load_client_plugin);
load_env_plugins(&mysql);
@@ -295,7 +295,7 @@ void mysql_client_plugin_deinit()
bzero(&plugin_list, sizeof(plugin_list));
initialized= 0;
free_root(&mem_root, MYF(0));
- pthread_mutex_destroy(&LOCK_load_client_plugin);
+ mysql_mutex_destroy(&LOCK_load_client_plugin);
DBUG_VOID_RETURN;
}
@@ -313,7 +313,7 @@ mysql_client_register_plugin(MYSQL *mysql,
if (is_not_initialized(mysql, plugin->name))
DBUG_RETURN(NULL);
- pthread_mutex_lock(&LOCK_load_client_plugin);
+ mysql_mutex_lock(&LOCK_load_client_plugin);
/* make sure the plugin wasn't loaded meanwhile */
if (find_plugin(plugin->name, plugin->type))
@@ -326,7 +326,7 @@ mysql_client_register_plugin(MYSQL *mysql,
else
plugin= add_plugin(mysql, plugin, 0, 0, unused);
- pthread_mutex_unlock(&LOCK_load_client_plugin);
+ mysql_mutex_unlock(&LOCK_load_client_plugin);
DBUG_RETURN(plugin);
}
@@ -348,7 +348,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
DBUG_RETURN (NULL);
}
- pthread_mutex_lock(&LOCK_load_client_plugin);
+ mysql_mutex_lock(&LOCK_load_client_plugin);
/* make sure the plugin wasn't loaded meanwhile */
if (type >= 0 && find_plugin(name, type))
@@ -375,8 +375,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
{
errmsg= "not a plugin";
- (void)dlclose(dlhandle);
- goto err;
+ goto errc;
}
plugin= (struct st_mysql_client_plugin*)sym;
@@ -384,30 +383,32 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
if (type >=0 && type != plugin->type)
{
errmsg= "type mismatch";
- goto err;
+ goto errc;
}
if (strcmp(name, plugin->name))
{
errmsg= "name mismatch";
- goto err;
+ goto errc;
}
if (type < 0 && find_plugin(name, plugin->type))
{
errmsg= "it is already loaded";
- goto err;
+ goto errc;
}
plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
- pthread_mutex_unlock(&LOCK_load_client_plugin);
+ mysql_mutex_unlock(&LOCK_load_client_plugin);
DBUG_PRINT ("leave", ("plugin loaded ok"));
DBUG_RETURN (plugin);
+errc:
+ dlclose(dlhandle);
err:
- pthread_mutex_unlock(&LOCK_load_client_plugin);
+ mysql_mutex_unlock(&LOCK_load_client_plugin);
DBUG_PRINT ("leave", ("plugin load error : %s", errmsg));
set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
diff --git a/sql-common/my_time.c b/sql-common/my_time.c
index 860f69a7f8e..7cf8692a3f6 100644
--- a/sql-common/my_time.c
+++ b/sql-common/my_time.c
@@ -1,5 +1,6 @@
/*
Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013, 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
@@ -24,10 +25,10 @@
ulonglong log_10_int[20]=
{
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
- ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000),
- ULL(1000000000000), ULL(10000000000000), ULL(100000000000000),
- ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000),
- ULL(1000000000000000000), ULL(10000000000000000000)
+ 100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
+ 1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
+ 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
+ 1000000000000000000ULL, 10000000000000000000ULL
};
@@ -80,6 +81,8 @@ 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)
{
+ if (ltime->time_type == MYSQL_TIMESTAMP_TIME)
+ return FALSE;
if (not_zero_date)
{
if (((flags & TIME_NO_ZERO_IN_DATE) &&
@@ -104,6 +107,143 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
return FALSE;
}
+static int get_number(uint *val, uint *number_of_fields, const char **str,
+ const char *end)
+{
+ const char *s = *str;
+
+ if (s >= end)
+ return 0;
+
+ if (!my_isdigit(&my_charset_latin1, *s))
+ return 1;
+ *val= *s++ - '0';
+
+ for (; s < end && my_isdigit(&my_charset_latin1, *s); s++)
+ *val= *val * 10 + *s - '0';
+ *str = s;
+ (*number_of_fields)++;
+ return 0;
+}
+
+static int get_digits(uint *val, uint *number_of_fields, const char **str,
+ const char *end, uint length)
+{
+ return get_number(val, number_of_fields, str, MY_MIN(end, *str + length));
+}
+
+static int get_punct(const char **str, const char *end)
+{
+ if (*str >= end)
+ return 0;
+ if (my_ispunct(&my_charset_latin1, **str))
+ {
+ (*str)++;
+ return 0;
+ }
+ return 1;
+}
+
+static int get_date_time_separator(uint *number_of_fields, ulonglong flags,
+ const char **str, const char *end)
+{
+ const char *s= *str;
+ if (s >= end)
+ return 0;
+
+ if (*s == 'T')
+ {
+ (*str)++;
+ return 0;
+ }
+
+ /*
+ now, this is tricky, for backward compatibility reasons.
+ cast("11:11:11.12.12.12" as datetime) should give 2011-11-11 12:12:12
+ 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.
+ */
+ if (my_ispunct(&my_charset_latin1, *s))
+ {
+ if (flags & TIME_DATETIME_ONLY)
+ {
+ /* see above, returning 1 is not enough, we need hard abort here */
+ *number_of_fields= 0;
+ return 1;
+ }
+
+ (*str)++;
+ return 0;
+ }
+
+ if (!my_isspace(&my_charset_latin1, *s))
+ return 1;
+
+ do
+ {
+ s++;
+ } while (my_isspace(&my_charset_latin1, *s));
+ *str= s;
+ return 0;
+}
+
+static int get_maybe_T(const char **str, const char *end)
+{
+ if (*str < end && **str == 'T')
+ (*str)++;
+ return 0;
+}
+
+static uint skip_digits(const char **str, const char *end)
+{
+ const char *start= *str, *s= *str;
+ while (s < end && my_isdigit(&my_charset_latin1, *s))
+ s++;
+ *str= s;
+ return s - start;
+}
+
+
+/**
+ Check datetime, date, or normalized time (i.e. time without days) range.
+ @param ltime Datetime value.
+ @returns
+ @retval FALSE on success
+ @retval TRUE on error
+*/
+my_bool check_datetime_range(const MYSQL_TIME *ltime)
+{
+ /*
+ In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
+ In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
+ */
+ return
+ ltime->year > 9999 || ltime->month > 12 || ltime->day > 31 ||
+ ltime->minute > 59 || ltime->second > 59 ||
+ ltime->second_part > TIME_MAX_SECOND_PART ||
+ (ltime->hour >
+ (uint) (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23));
+}
+
+
+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= (ulong) (tmp * log_10_int[6 - (*str - start)]);
+ else
+ *val= tmp;
+ if (skip_digits(str, end))
+ status->warnings|= MYSQL_TIME_NOTE_TRUNCATED;
+}
+
/*
Convert a timestamp string to a MYSQL_TIME value.
@@ -119,9 +259,8 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
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)
@@ -132,21 +271,12 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
The second part may have an optional .###### fraction part.
- NOTES
- This function should work with a format position vector as long as the
- following things holds:
- - All date are kept together and all time parts are kept together
- - Date and time parts must be separated by blank
- - Second fractions must come after second part and be separated
- by a '.'. (The second fractions are optional)
- - AM/PM must come after second fractions (or after seconds if no fractions)
- - Year must always been specified.
- - If time is before date, then we will use datetime format only if
- the argument consist of two parts, separated by space.
- Otherwise we will assume the argument is a date.
- - The hour part must be specified in hour-minute-second order.
+ 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
- RETURN VALUES
+ 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.
@@ -154,293 +284,126 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
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)
{
- uint UNINIT_VAR(field_length), UNINIT_VAR(year_length), digits, i, number_of_fields;
- uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
- uint add_hours= 0, start_loop;
- ulong not_zero_date, allow_space;
- my_bool is_internal_format;
- const char *pos, *UNINIT_VAR(last_field_pos);
- const char *end=str+length;
- const uchar *format_position;
- my_bool found_delimitier= 0, found_space= 0;
- uint frac_pos, frac_len;
+ const char *end=str+length, *pos;
+ uint number_of_fields= 0, digits, year_length, not_zero_date;
DBUG_ENTER("str_to_datetime");
- DBUG_PRINT("enter",("str: %.*s",length,str));
+ bzero(l_time, sizeof(*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);
}
- is_internal_format= 0;
- /* This has to be changed if want to activate different timestamp formats */
- format_position= internal_format_positions;
-
/*
Calculate number of digits in first part.
If length= 8 or >= 14 then year is of format YYYY.
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
*/
- for (pos=str;
- pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
- pos++)
- ;
+ pos= str;
+ digits= skip_digits(&pos, end);
- digits= (uint) (pos-str);
- start_loop= 0; /* Start of scan loop */
- date_len[format_position[0]]= 0; /* Length of year field */
- if (pos == end || *pos == '.')
+ if (pos < end && *pos == 'T') /* YYYYYMMDDHHMMSSThhmmss is supported too */
{
- /* Found date in internal format (only numbers like YYYYMMDD) */
- year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
- field_length= year_length;
- is_internal_format= 1;
- format_position= internal_format_positions;
+ pos++;
+ digits+= skip_digits(&pos, end);
}
- else
+ if (pos < end && *pos == '.' && digits >= 12) /* YYYYYMMDDHHMMSShhmmss.uuuuuu is supported too */
{
- if (format_position[0] >= 3) /* If year is after HHMMDD */
- {
- /*
- If year is not in first part then we have to determinate if we got
- a date field or a datetime field.
- We do this by checking if there is two numbers separated by
- space in the input.
- */
- while (pos < end && !my_isspace(&my_charset_latin1, *pos))
- pos++;
- while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
- pos++;
- if (pos == end)
- {
- if (flags & TIME_DATETIME_ONLY)
- {
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE); /* Can't be a full datetime */
- }
- /* Date field. Set hour, minutes and seconds to 0 */
- date[0]= date[1]= date[2]= date[3]= date[4]= 0;
- start_loop= 5; /* Start with first date part */
- }
- }
-
- field_length= format_position[0] == 0 ? 4 : 2;
+ pos++;
+ skip_digits(&pos, end); // ignore the return value
}
- /*
- Only allow space in the first "part" of the datetime field and:
- - after days, part seconds
- - before and after AM/PM (handled by code later)
-
- 2003-03-03 20:00:20 AM
- 20:00:20.000000 AM 03-03-2000
- */
- i= max((uint) format_position[0], (uint) format_position[1]);
- set_if_bigger(i, (uint) format_position[2]);
- allow_space= ((1 << i) | (1 << format_position[6]));
- allow_space&= (1 | 2 | 4 | 8);
-
- not_zero_date= 0;
- for (i = start_loop;
- i < MAX_DATE_PARTS-1 && str != end &&
- my_isdigit(&my_charset_latin1,*str);
- i++)
+ if (pos == end)
{
- const char *start= str;
- ulong tmp_value= (uint) (uchar) (*str++ - '0');
-
/*
- Internal format means no delimiters; every field has a fixed
- width. Otherwise, we scan until we find a delimiter and discard
- leading zeroes -- except for the microsecond part, where leading
- zeroes are significant, and where we never process more than six
- digits.
+ Found date in internal format
+ (only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]])
*/
- my_bool scan_until_delim= !is_internal_format &&
- ((i != format_position[6]));
-
- while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
- (scan_until_delim || --field_length))
- {
- tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
- str++;
- }
- date_len[i]= (uint) (str - start);
- if (tmp_value > 999999) /* Impossible date part */
- {
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
- }
- date[i]=tmp_value;
- not_zero_date|= tmp_value;
-
- /* Length of next field */
- field_length= format_position[i+1] == 0 ? 4 : 2;
-
- if ((last_field_pos= str) == end)
- {
- i++; /* Register last found part */
- break;
- }
- /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
- if (i == format_position[2] && *str == 'T')
- {
- str++; /* ISO8601: CCYYMMDDThhmmss */
- continue;
- }
- if (i == format_position[5]) /* Seconds */
- {
- if (*str == '.') /* Followed by part seconds */
- {
- str++;
- field_length= 6; /* 6 digits */
- }
- continue;
- }
- while (str != end &&
- (my_ispunct(&my_charset_latin1,*str) ||
- my_isspace(&my_charset_latin1,*str)))
- {
- if (my_isspace(&my_charset_latin1,*str))
- {
- if (!(allow_space & (1 << i)))
- {
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
- }
- found_space= 1;
- }
- str++;
- found_delimitier= 1; /* Should be a 'normal' date */
- }
- /* Check if next position is AM/PM */
- if (i == format_position[6]) /* Seconds, time for AM/PM */
- {
- i++; /* Skip AM/PM part */
- if (format_position[7] != 255) /* If using AM/PM */
- {
- if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
- {
- if (str[0] == 'p' || str[0] == 'P')
- add_hours= 12;
- else if (str[0] != 'a' && str[0] != 'A')
- continue; /* Not AM/PM */
- str+= 2; /* Skip AM/PM */
- /* Skip space after AM/PM */
- while (str != end && my_isspace(&my_charset_latin1,*str))
- str++;
- }
- }
- }
- last_field_pos= str;
+ 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)
+ || 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;
}
- if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
+ else
{
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE); /* Can't be a datetime */
+ const char *start= str;
+ if (get_number(&l_time->year, &number_of_fields, &str, end))
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ year_length= str - start;
+
+ 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;
}
- str= last_field_pos;
-
- number_of_fields= i - start_loop;
- while (i < MAX_DATE_PARTS)
+ /* we're ok if date part is correct. even if the rest is truncated */
+ if (number_of_fields < 3)
{
- date_len[i]= 0;
- date[i++]= 0;
+ l_time->time_type= MYSQL_TIMESTAMP_NONE;
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+ DBUG_RETURN(TRUE);
}
- if (!is_internal_format)
+ if (!status->warnings && str < end && *str == '.')
{
- year_length= date_len[(uint) format_position[0]];
- if (!year_length) /* Year must be specified */
- {
- *was_cut= 1;
- DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
- }
-
- l_time->year= date[(uint) format_position[0]];
- l_time->month= date[(uint) format_position[1]];
- l_time->day= date[(uint) format_position[2]];
- l_time->hour= date[(uint) format_position[3]];
- l_time->minute= date[(uint) format_position[4]];
- l_time->second= date[(uint) format_position[5]];
-
- frac_pos= (uint) format_position[6];
- frac_len= date_len[frac_pos];
- if (frac_len < 6)
- date[frac_pos]*= (uint) log_10_int[6 - frac_len];
- l_time->second_part= date[frac_pos];
-
- if (format_position[7] != (uchar) 255)
- {
- if (l_time->hour > 12)
- {
- *was_cut= 1;
- goto err;
- }
- l_time->hour= l_time->hour%12 + add_hours;
- }
- }
- else
- {
- l_time->year= date[0];
- l_time->month= date[1];
- l_time->day= date[2];
- l_time->hour= date[3];
- l_time->minute= date[4];
- l_time->second= date[5];
- if (date_len[6] < 6)
- date[6]*= (uint) log_10_int[6 - date_len[6]];
- l_time->second_part=date[6];
+ str++;
+ get_microseconds(&l_time->second_part, status,
+ &number_of_fields, &str, end);
}
- l_time->neg= 0;
+
+ not_zero_date = l_time->year || l_time->month || l_time->day ||
+ l_time->hour || l_time->minute || l_time->second ||
+ l_time->second_part;
if (year_length == 2 && not_zero_date)
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
- if (number_of_fields < 3 ||
- l_time->year > 9999 || l_time->month > 12 ||
- l_time->day > 31 || l_time->hour > 23 ||
- l_time->minute > 59 || l_time->second > 59)
+ 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)
{
- /* Only give warning for a zero date if there is some garbage after */
- if (!not_zero_date) /* If zero date */
- {
- for (; str != end ; str++)
- {
- if (!my_isspace(&my_charset_latin1, *str))
- {
- not_zero_date= 1; /* Give warning */
- break;
- }
- }
- }
- *was_cut= test(not_zero_date);
+ status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
goto err;
}
- if (check_date(l_time, not_zero_date != 0, flags, was_cut))
+ if (check_date(l_time, not_zero_date, flags, &status->warnings))
goto err;
l_time->time_type= (number_of_fields <= 3 ?
@@ -450,16 +413,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);
}
@@ -474,59 +438,60 @@ 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;
const char *end=str+length, *end_of_days;
- my_bool found_days,found_hours;
- uint state;
+ my_bool found_days,found_hours, neg= 0;
+ uint UNINIT_VAR(state);
- l_time->neg=0;
- *warning= 0;
+ my_time_status_init(status);
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
length--;
if (str != end && *str == '-')
{
- l_time->neg=1;
+ neg=1;
str++;
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;
/* 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');
@@ -536,7 +501,6 @@ str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
;
- LINT_INIT(state);
found_days=found_hours=0;
if ((uint) (end-str) > 1 && str != end_of_days &&
my_isdigit(&my_charset_latin1, *str))
@@ -592,24 +556,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 &&
@@ -618,7 +573,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)
{
@@ -641,8 +599,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= 0;
@@ -653,9 +614,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)
{
@@ -663,12 +624,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;
}
@@ -698,7 +664,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);
@@ -950,7 +919,8 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, uint *error_code)
#endif
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
- (long) days_at_timestart)*86400L + (long) t->hour*3600L +
+ (long) days_at_timestart) * SECONDS_IN_24H +
+ (long) t->hour*3600L +
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
3600);
@@ -1009,7 +979,7 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, uint *error_code)
/* shift back, if we were dealing with boundary dates */
- tmp+= shift*86400L;
+ tmp+= shift * SECONDS_IN_24H;
/*
This is possible for dates, which slightly exceed boundaries.
@@ -1189,6 +1159,27 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits)
}
+/**
+ Print a timestamp with an optional fractional part: XXXXX[.YYYYY]
+
+ @param tm The timestamp value to print.
+ @param OUT to The string pointer to print at.
+ @param dec Precision, in the range 0..6.
+ @return The length of the result string.
+*/
+int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
+{
+ char *pos= longlong10_to_str((longlong) tm->tv_sec, to, 10);
+ if (dec)
+ {
+ *pos++= '.';
+ pos= fmt_number((uint) sec_part_shift(tm->tv_usec, dec), pos, dec);
+ }
+ *pos= '\0';
+ return (int) (pos - to);
+}
+
+
/*
Convert datetime value specified as number to broken-down TIME
representation and form value of DATETIME type as side-effect.
@@ -1254,19 +1245,19 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
- if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
+ if (nr <= (YY_PART_YEAR-1)*10000000000LL+1231235959LL)
{
- nr= nr+LL(20000000000000); /* YYMMDDHHMMSS, 2000-2069 */
+ nr= nr+20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
goto ok;
}
- if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
+ if (nr < YY_PART_YEAR*10000000000LL+ 101000000LL)
goto err;
- if (nr <= LL(991231235959))
- nr= nr+LL(19000000000000); /* YYMMDDHHMMSS, 1970-1999 */
+ if (nr <= 991231235959LL)
+ nr= nr+19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
ok:
- part1=(long) (nr/LL(1000000));
- part2=(long) (nr - (longlong) part1*LL(1000000));
+ part1=(long) (nr/1000000LL);
+ part2=(long) (nr - (longlong) part1*1000000LL);
time_res->year= (int) (part1/10000L); part1%=10000L;
time_res->month= (int) part1 / 100;
time_res->day= (int) part1 % 100;
@@ -1290,7 +1281,7 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
/* 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;
- return LL(-1);
+ return -1;
err:
{
@@ -1300,7 +1291,7 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
time_res->time_type= save; /* Restore range */
*was_cut= 1; /* Found invalid date */
}
- return LL(-1);
+ return -1;
}
/*
@@ -1323,16 +1314,8 @@ int number_to_time(my_bool neg, ulonglong nr, ulong sec_part,
MYSQL_TIME *ltime, int *was_cut)
{
if (nr > 9999999 && nr < 99991231235959ULL && neg == 0)
- {
- if (number_to_datetime(nr, sec_part, ltime,
- TIME_INVALID_DATES, was_cut) < 0)
- return -1;
-
- ltime->year= ltime->month= ltime->day= 0;
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
- *was_cut= MYSQL_TIME_NOTE_TRUNCATED;
- return 0;
- }
+ return number_to_datetime(nr, sec_part, ltime,
+ TIME_INVALID_DATES, was_cut) < 0 ? -1 : 0;
*was_cut= 0;
ltime->year= ltime->month= ltime->day= 0;
@@ -1365,7 +1348,7 @@ ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
{
return ((ulonglong) (my_time->year * 10000UL +
my_time->month * 100UL +
- my_time->day) * ULL(1000000) +
+ my_time->day) * 1000000ULL +
(ulonglong) (my_time->hour * 10000UL +
my_time->minute * 100UL +
my_time->second));
@@ -1426,7 +1409,7 @@ ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
return TIME_to_ulonglong_time(my_time);
case MYSQL_TIMESTAMP_NONE:
case MYSQL_TIMESTAMP_ERROR:
- return ULL(0);
+ return 0;
default:
DBUG_ASSERT(0);
}
@@ -1444,7 +1427,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 +
diff --git a/sql-common/my_user.c b/sql-common/my_user.c
index 8d717ea7131..a486f77bc1e 100644
--- a/sql-common/my_user.c
+++ b/sql-common/my_user.c
@@ -30,34 +30,40 @@
host_name_str [OUT] Buffer to store host name part.
Must be not less than HOSTNAME_LENGTH + 1.
host_name_len [OUT] A place to store length of the host name part.
+
+ RETURN
+ 0 - if only a user was set, no '@' was found
+ 1 - if both user and host were set
*/
-void parse_user(const char *user_id_str, size_t user_id_len,
- char *user_name_str, size_t *user_name_len,
- char *host_name_str, size_t *host_name_len)
+int parse_user(const char *user_id_str, size_t user_id_len,
+ char *user_name_str, size_t *user_name_len,
+ char *host_name_str, size_t *host_name_len)
{
char *p= strrchr(user_id_str, '@');
if (!p)
{
- *user_name_len= 0;
+ *user_name_len= user_id_len;
*host_name_len= 0;
}
else
{
*user_name_len= (uint) (p - user_id_str);
*host_name_len= (uint) (user_id_len - *user_name_len - 1);
+ }
- if (*user_name_len > USERNAME_LENGTH)
- *user_name_len= USERNAME_LENGTH;
+ if (*user_name_len > USERNAME_LENGTH)
+ *user_name_len= USERNAME_LENGTH;
- if (*host_name_len > HOSTNAME_LENGTH)
- *host_name_len= HOSTNAME_LENGTH;
+ if (*host_name_len > HOSTNAME_LENGTH)
+ *host_name_len= HOSTNAME_LENGTH;
- memcpy(user_name_str, user_id_str, *user_name_len);
- memcpy(host_name_str, p + 1, *host_name_len);
- }
+ memcpy(user_name_str, user_id_str, *user_name_len);
+ memcpy(host_name_str, p + 1, *host_name_len);
user_name_str[*user_name_len]= 0;
host_name_str[*host_name_len]= 0;
+
+ return p != NULL;
}
diff --git a/sql-common/mysql_async.c b/sql-common/mysql_async.c
index c7e720076ea..decf48e0e69 100644
--- a/sql-common/mysql_async.c
+++ b/sql-common/mysql_async.c
@@ -56,7 +56,7 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b,
/* Asynchronous connect(); socket must already be set non-blocking. */
int
my_connect_async(struct mysql_async_context *b, my_socket fd,
- const struct sockaddr *name, uint namelen, uint timeout)
+ const struct sockaddr *name, uint namelen, int vio_timeout)
{
int res;
size_socket s_err_size;
@@ -90,15 +90,19 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
return res;
#endif
b->events_to_wait_for|= MYSQL_WAIT_WRITE;
- b->timeout_value= timeout;
- if (timeout)
+ if (vio_timeout >= 0)
+ {
+ b->timeout_value= vio_timeout;
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ }
+ else
+ b->timeout_value= 0;
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context);
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
- if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
return -1;
s_err_size= sizeof(res);
@@ -117,9 +121,15 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
(errno != EAGAIN && errno != EINTR))
+#ifdef _AIX
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+#endif
+
ssize_t
my_recv_async(struct mysql_async_context *b, int fd,
- unsigned char *buf, size_t size, uint timeout)
+ unsigned char *buf, size_t size, int timeout)
{
ssize_t res;
@@ -129,7 +139,7 @@ my_recv_async(struct mysql_async_context *b, int fd,
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_READ;
- if (timeout)
+ if (timeout >= 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
@@ -139,7 +149,7 @@ my_recv_async(struct mysql_async_context *b, int fd,
my_context_yield(&b->async_context);
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
- if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
return -1;
}
}
@@ -147,7 +157,7 @@ my_recv_async(struct mysql_async_context *b, int fd,
ssize_t
my_send_async(struct mysql_async_context *b, int fd,
- const unsigned char *buf, size_t size, uint timeout)
+ const unsigned char *buf, size_t size, int timeout)
{
ssize_t res;
@@ -157,7 +167,7 @@ my_send_async(struct mysql_async_context *b, int fd,
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_WRITE;
- if (timeout)
+ if (timeout >= 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
@@ -167,23 +177,40 @@ my_send_async(struct mysql_async_context *b, int fd,
my_context_yield(&b->async_context);
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
- if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
return -1;
}
}
my_bool
-my_poll_read_async(struct mysql_async_context *b, uint timeout)
+my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event,
+ int timeout)
{
- b->events_to_wait_for= MYSQL_WAIT_READ | MYSQL_WAIT_TIMEOUT;
- b->timeout_value= timeout;
+ switch (event)
+ {
+ case VIO_IO_EVENT_READ:
+ b->events_to_wait_for = MYSQL_WAIT_READ;
+ break;
+ case VIO_IO_EVENT_WRITE:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE;
+ break;
+ case VIO_IO_EVENT_CONNECT:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE | IF_WIN(0, MYSQL_WAIT_EXCEPT);
+ break;
+ }
+
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for |= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context);
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
- return (b->events_occured & MYSQL_WAIT_READ) ? 0 : 1;
+ return (b->events_occurred & MYSQL_WAIT_TIMEOUT) ? 0 : 1;
}
@@ -239,32 +266,27 @@ my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
}
#endif /* HAVE_OPENSSL */
+/*
+ Legacy support of the MariaDB 5.5 version, where timeouts where only in
+ seconds resolution. Applications that use this will be asked to set a timeout
+ at the nearest higher whole-seconds value.
+*/
unsigned int STDCALL
mysql_get_timeout_value(const MYSQL *mysql)
{
- return mysql->options.extension->async_context->timeout_value;
+ unsigned int timeout= mysql->options.extension->async_context->timeout_value;
+ /* Avoid overflow. */
+ if (timeout > UINT_MAX - 999)
+ return (timeout - 1)/1000 + 1;
+ else
+ return (timeout+999)/1000;
}
-/*
- In 10.0, VIO timeouts are in milliseconds, so we support getting the
- millisecond timeout value from async applications.
-
- In 5.5, timeouts are always in seconds, but we support the 10.0 version
- that provides milliseconds, so applications can work with either version
- of the library easily.
-
- When merging this to 10.0, this function must be removed and the 10.0
- version used.
-*/
unsigned int STDCALL
mysql_get_timeout_value_ms(const MYSQL *mysql)
{
- unsigned int timeout= mysql->options.extension->async_context->timeout_value;
- if (timeout <= UINT_MAX / 1000)
- return timeout*1000;
- else
- return UINT_MAX;
+ return mysql->options.extension->async_context->timeout_value;
}
@@ -327,7 +349,7 @@ mysql_get_timeout_value_ms(const MYSQL *mysql)
} \
\
b->active= 1; \
- b->events_occured= ready_status; \
+ b->events_occurred= ready_status; \
res= my_context_continue(&b->async_context); \
b->active= 0; \
if (res > 0) \
@@ -383,7 +405,7 @@ mysql_get_timeout_value_ms(const MYSQL *mysql)
} \
\
b->active= 1; \
- b->events_occured= ready_status; \
+ b->events_occurred= ready_status; \
res= my_context_continue(&b->async_context); \
b->active= 0; \
if (res > 0) \
@@ -433,7 +455,11 @@ MK_ASYNC_START_BODY(
parms.db= db;
parms.port= port;
parms.unix_socket= unix_socket;
- parms.client_flags= client_flags;
+ /*
+ async wrapper enforce the CLIENT_REMEMBER_OPTIONS flag to be
+ functional (otherwise it can't operate)
+ */
+ parms.client_flags= client_flags | CLIENT_REMEMBER_OPTIONS;
},
NULL,
r_ptr,
diff --git a/sql-common/pack.c b/sql-common/pack.c
index 02e91b5c3e3..4bb4a0b7a4e 100644
--- a/sql-common/pack.c
+++ b/sql-common/pack.c
@@ -49,7 +49,7 @@ ulong STDCALL net_field_length(uchar **packet)
/* The same as above but returns longlong */
my_ulonglong net_field_length_ll(uchar **packet)
{
- reg1 uchar *pos= *packet;
+ uchar *pos= *packet;
if (*pos < 251)
{
(*packet)++;
@@ -70,12 +70,47 @@ my_ulonglong net_field_length_ll(uchar **packet)
(*packet)+=4;
return (my_ulonglong) uint3korr(pos+1);
}
+ DBUG_ASSERT(*pos == 254);
(*packet)+=9; /* Must be 254 when here */
-#ifdef NO_CLIENT_LONGLONG
- return (my_ulonglong) uint4korr(pos+1);
-#else
return (my_ulonglong) uint8korr(pos+1);
-#endif
+}
+
+my_ulonglong safe_net_field_length_ll(uchar **packet, size_t packet_len)
+{
+ uchar *pos= *packet;
+ if (packet_len < 1)
+ goto err;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (my_ulonglong) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return (my_ulonglong) NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ if (packet_len < 3)
+ goto err;
+ (*packet)+=3;
+ return (my_ulonglong) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ if (packet_len < 4)
+ goto err;
+ (*packet)+=4;
+ return (my_ulonglong) uint3korr(pos+1);
+ }
+ if (packet_len < 9 || *pos != 254)
+ goto err;
+ (*packet)+=9;
+ return (my_ulonglong) uint8korr(pos+1);
+err:
+ *packet = NULL;
+ return 0;
}
/*
@@ -83,38 +118,69 @@ my_ulonglong net_field_length_ll(uchar **packet)
SYNOPSIS
net_store_length()
- pkg Store the packed integer here
+ packet Store the packed integer here
length integers to store
NOTES
This is mostly used to store lengths of strings.
- We have to cast the result for the LL() becasue of a bug in Forte CC
- compiler.
RETURN
- Position in 'pkg' after the packed length
+ Position in 'packet' after the packed length
*/
uchar *net_store_length(uchar *packet, ulonglong length)
{
- if (length < (ulonglong) LL(251))
+ if (length < 251)
+ {
+ *packet= (uchar) length;
+ return packet+1;
+ }
+ /* 251 is reserved for NULL */
+ if (length < 65536)
+ {
+ *packet++=252;
+ int2store(packet, (uint) length);
+ return packet+2;
+ }
+ if (length < 16777216)
+ {
+ *packet++=253;
+ int3store(packet, (ulong) length);
+ return packet+3;
+ }
+ *packet++=254;
+ int8store(packet,length);
+ return packet+8;
+}
+
+uchar *safe_net_store_length(uchar *packet, size_t packet_len, ulonglong length)
+{
+ if (length < 251)
{
- *packet=(uchar) length;
+ if (packet_len < 1)
+ return NULL;
+ *packet= (uchar) length;
return packet+1;
}
/* 251 is reserved for NULL */
- if (length < (ulonglong) LL(65536))
+ if (length < 65536)
{
+ if (packet_len < 3)
+ return NULL;
*packet++=252;
- int2store(packet,(uint) length);
+ int2store(packet, (uint) length);
return packet+2;
}
- if (length < (ulonglong) LL(16777216))
+ if (length < 16777216)
{
+ if (packet_len < 4)
+ return NULL;
*packet++=253;
- int3store(packet,(ulong) length);
+ int3store(packet, (ulong) length);
return packet+3;
}
+ if (packet_len < 9)
+ return NULL;
*packet++=254;
int8store(packet,length);
return packet+8;