summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
authorSeppo Jaakola <seppo.jaakola@codership.com>2013-12-04 10:32:43 +0200
committerSeppo Jaakola <seppo.jaakola@codership.com>2013-12-04 10:32:43 +0200
commit496e22cf3bd2a481fd3502d86e5a4e8228bf9823 (patch)
tree80549f8005fcf3236bfa004a5aea35e4e67b36ca /sql-common
parent45f484b8381a5923aec9c704e54c7f7bcfa02a40 (diff)
parent26f56089c734852dc31d98fd73e1d8f1750bd1a8 (diff)
downloadmariadb-git-496e22cf3bd2a481fd3502d86e5a4e8228bf9823.tar.gz
merge with MariaDB 5.6 bzr merge lp:maria --rtag:mariadb-10.0.6
and a number of fixes to make this buildable. Run also few short multi-master high conflict rate tests, with no issues
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/client.c310
-rw-r--r--sql-common/my_user.c28
2 files changed, 315 insertions, 23 deletions
diff --git a/sql-common/client.c b/sql-common/client.c
index 6942822f889..87cbf45a3c6 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -37,6 +37,7 @@
#include <my_global.h>
#include <my_default.h>
#include "mysql.h"
+#include "hash.h"
/* Remove client convenience wrappers */
#undef max_allowed_packet
@@ -1216,17 +1217,30 @@ 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(OPTS, X, STR) \
- if ((OPTS)->extension) \
- my_free((OPTS)->extension->X); \
- EXTENSION_SET(OPTS, X, (STR) ? my_strdup((STR), MYF(MY_WME)) : NULL);
+ do { \
+ if ((OPTS)->extension) \
+ my_free((OPTS)->extension->X); \
+ else \
+ ALLOCATE_EXTENSIONS(OPTS); \
+ (OPTS)->extension->X= ((STR) != NULL) ? \
+ my_strdup((STR), MYF(MY_WME)) : NULL; \
+ } while (0)
+
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
#define SET_SSL_OPTION(OPTS, opt_var, arg) \
@@ -2376,6 +2390,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,
@@ -2383,6 +2398,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);
@@ -2426,8 +2502,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;
@@ -2464,6 +2545,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);
@@ -2472,6 +2555,7 @@ error:
return res;
}
+#define MAX_CONNECTION_ATTR_STORAGE_LENGTH 65536
/**
sends a client authentication packet (second packet in the 3-way handshake)
@@ -2509,10 +2593,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;
@@ -2665,6 +2760,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))
{
@@ -3024,6 +3121,51 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd,
return my_connect(fd, name, namelen, mysql->options.connect_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,
@@ -3057,6 +3199,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 */
@@ -3757,6 +3902,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);
@@ -4320,7 +4466,8 @@ 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;
@@ -4345,6 +4492,145 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
case MYSQL_OPT_SSL_CRLPATH:
EXTENSION_SET_SSL_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);
}
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;
}