List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:March 30 2012 4:02pm
Subject:bzr push into mysql-trunk branch (Georgi.Kodinov:3826 to 3827) WL#5924
View as plain text  
 3827 Georgi Kodinov	2012-03-30
      WL#5924 : Add connect string processing to mysql
      
      Initial implementation.

    added:
      mysql-test/suite/perfschema/r/connect_attrs.result
      mysql-test/suite/perfschema/r/ddl_session_account_connect_attrs.result
      mysql-test/suite/perfschema/r/ddl_session_connect_attrs.result
      mysql-test/suite/perfschema/r/dml_session_account_connect_attrs.result
      mysql-test/suite/perfschema/r/dml_session_connect_attrs.result
      mysql-test/suite/perfschema/t/connect_attrs.test
      mysql-test/suite/perfschema/t/ddl_session_account_connect_attrs.test
      mysql-test/suite/perfschema/t/ddl_session_connect_attrs.test
      mysql-test/suite/perfschema/t/dml_session_account_connect_attrs.test
      mysql-test/suite/perfschema/t/dml_session_connect_attrs.test
      storage/perfschema/cursor_by_thread_connect_attr.cc
      storage/perfschema/cursor_by_thread_connect_attr.h
      storage/perfschema/table_session_account_connect_attrs.cc
      storage/perfschema/table_session_account_connect_attrs.h
      storage/perfschema/table_session_connect_attrs.cc
      storage/perfschema/table_session_connect_attrs.h
      storage/perfschema/unittest/pfs_connect_attr-t.cc
    modified:
      client/mysql.cc
      client/mysqladmin.cc
      client/mysqlbinlog.cc
      client/mysqlcheck.c
      client/mysqldump.c
      client/mysqlimport.c
      client/mysqlshow.c
      client/mysqlslap.c
      client/mysqltest.cc
      include/errmsg.h
      include/mysql.h
      include/mysql.h.pp
      include/mysql/psi/psi.h
      include/mysql/psi/psi_abi_v1.h.pp
      include/mysql_com.h
      include/sql_common.h
      libmysql/client_settings.h
      libmysqld/lib_sql.cc
      mysql-test/suite/perfschema/r/dml_handler.result
      mysql-test/suite/perfschema/r/information_schema.result
      mysql-test/suite/perfschema/r/pfs_upgrade.result
      mysql-test/suite/perfschema/r/schema.result
      mysql-test/suite/perfschema/r/table_schema.result
      mysys/psi_noop.c
      scripts/mysql_system_tables.sql
      sql-common/client.c
      sql/client_settings.h
      sql/sql_acl.cc
      storage/perfschema/CMakeLists.txt
      storage/perfschema/pfs.cc
      storage/perfschema/pfs_engine_table.cc
      storage/perfschema/pfs_engine_table.h
      storage/perfschema/pfs_instr.h
      storage/perfschema/unittest/CMakeLists.txt
      tests/mysql_client_test.c
 3826 Alexander Barkov	2012-03-30 [merge]
      BUG#12537203 post-fix
      
      Introducing a new class NameString, to avoid "name" and "name_length"
      desynchronization in the future.

    modified:
      sql/field.cc
      sql/item.cc
      sql/item.h
      sql/item_cmpfunc.cc
      sql/item_create.cc
      sql/item_func.cc
      sql/item_func.h
      sql/item_geofunc.cc
      sql/item_subselect.cc
      sql/item_sum.cc
      sql/item_timefunc.h
      sql/log_event.cc
      sql/partition_info.cc
      sql/procedure.cc
      sql/procedure.h
      sql/sql_acl.cc
      sql/sql_analyse.cc
      sql/sql_base.cc
      sql/sql_class.cc
      sql/sql_executor.cc
      sql/sql_lex.cc
      sql/sql_load.cc
      sql/sql_optimizer.cc
      sql/sql_parse.cc
      sql/sql_profile.cc
      sql/sql_resolver.cc
      sql/sql_show.cc
      sql/sql_tmp_table.cc
      sql/sql_update.cc
      sql/sql_view.cc
      sql/sql_yacc.yy
      sql/table.cc
=== modified file 'client/mysql.cc'
--- a/client/mysql.cc	2012-03-06 14:29:42 +0000
+++ b/client/mysql.cc	2012-03-30 16:01:10 +0000
@@ -1423,6 +1423,9 @@ sig_handler handle_kill_signal(int sig)
   }
 
   kill_mysql= mysql_init(kill_mysql);
+  mysql_options(kill_mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(kill_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysql");
   if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password,
                           "", opt_mysql_port, opt_mysql_unix_port,0))
   {
@@ -4508,6 +4511,9 @@ sql_real_connect(char *host,char *databa
   if (opt_default_auth && *opt_default_auth)
     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
+  mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, 
+                 "program_name", "mysql");
   if (!mysql_real_connect(&mysql, host, user, password,
                           database, opt_mysql_port, opt_mysql_unix_port,
                           connect_flag | CLIENT_MULTI_STATEMENTS))

=== modified file 'client/mysqladmin.cc'
--- a/client/mysqladmin.cc	2012-03-06 14:29:42 +0000
+++ b/client/mysqladmin.cc	2012-03-30 16:01:10 +0000
@@ -369,6 +369,9 @@ int main(int argc,char *argv[])
   if (opt_default_auth && *opt_default_auth)
     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
+  mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqladmin");
   if (sql_connect(&mysql, option_wait))
   {
     /*

=== modified file 'client/mysqlbinlog.cc'
--- a/client/mysqlbinlog.cc	2012-03-06 14:29:42 +0000
+++ b/client/mysqlbinlog.cc	2012-03-30 16:01:10 +0000
@@ -1630,6 +1630,9 @@ static Exit_status safe_connect()
     mysql_options(mysql, MYSQL_SHARED_MEMORY_BASE_NAME,
                   shared_memory_base_name);
 #endif
+  mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqltbinlog");
   if (!mysql_real_connect(mysql, host, user, pass, 0, port, sock, 0))
   {
     error("Failed on connect: %s", mysql_error(mysql));

=== modified file 'client/mysqlcheck.c'
--- a/client/mysqlcheck.c	2012-03-06 14:29:42 +0000
+++ b/client/mysqlcheck.c	2012-03-30 16:01:10 +0000
@@ -862,6 +862,9 @@ static int dbConnect(char *host, char *u
     mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
   mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
+  mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqlcheck");
   if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd,
          NULL, opt_mysql_port, opt_mysql_unix_port, 0)))
   {

=== modified file 'client/mysqldump.c'
--- a/client/mysqldump.c	2012-03-21 06:42:54 +0000
+++ b/client/mysqldump.c	2012-03-30 16:01:10 +0000
@@ -1502,6 +1502,9 @@ static int connect_to_db(char *host, cha
   if (opt_default_auth && *opt_default_auth)
     mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
+  mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqldump");
   if (!(mysql= mysql_real_connect(&mysql_connection,host,user,passwd,
                                   NULL,opt_mysql_port,opt_mysql_unix_port,
                                   0)))

=== modified file 'client/mysqlimport.c'
--- a/client/mysqlimport.c	2012-03-06 14:29:42 +0000
+++ b/client/mysqlimport.c	2012-03-30 16:01:10 +0000
@@ -453,6 +453,9 @@ static MYSQL *db_connect(char *host, cha
     mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
   mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset);
+  mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqlimport");
   if (!(mysql_real_connect(mysql,host,user,passwd,
                            database,opt_mysql_port,opt_mysql_unix_port,
                            0)))

=== modified file 'client/mysqlshow.c'
--- a/client/mysqlshow.c	2012-03-06 14:29:42 +0000
+++ b/client/mysqlshow.c	2012-03-30 16:01:10 +0000
@@ -143,6 +143,9 @@ int main(int argc, char **argv)
   if (opt_default_auth && *opt_default_auth)
     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
+  mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqlshow");
   if (!(mysql_real_connect(&mysql,host,user,opt_password,
 			   (first_argument_uses_wildcards) ? "" :
                            argv[0],opt_mysql_port,opt_mysql_unix_port,

=== modified file 'client/mysqlslap.c'
--- a/client/mysqlslap.c	2012-03-06 14:29:42 +0000
+++ b/client/mysqlslap.c	2012-03-30 16:01:10 +0000
@@ -355,6 +355,9 @@ int main(int argc, char **argv)
   if (opt_default_auth && *opt_default_auth)
     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
 
+  mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqlslap");
   if (!opt_only_print) 
   {
     if (!(mysql_real_connect(&mysql, host, user, opt_password,

=== modified file 'client/mysqltest.cc'
--- a/client/mysqltest.cc	2012-03-06 14:29:42 +0000
+++ b/client/mysqltest.cc	2012-03-30 16:01:10 +0000
@@ -5224,6 +5224,10 @@ void safe_connect(MYSQL* mysql, const ch
   verbose_msg("Connecting to server %s:%d (socket %s) as '%s'"
               ", connection '%s', attempt %d ...", 
               host, port, sock, user, name, failed_attempts);
+
+  mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                 "program_name", "mysqltest");
   while(!mysql_real_connect(mysql, host,user, pass, db, port, sock,
                             CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS))
   {
@@ -5325,6 +5329,8 @@ int connect_n_handle_errors(struct st_co
     dynstr_append_mem(ds, ";\n", 2);
   }
   
+  mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+  mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "mysqltest");
   while (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0,
                           CLIENT_MULTI_STATEMENTS))
   {

=== modified file 'include/errmsg.h'
--- a/include/errmsg.h	2011-09-07 10:08:09 +0000
+++ b/include/errmsg.h	2012-03-30 16:01:10 +0000
@@ -102,7 +102,8 @@ extern const char *client_errors[];	/* E
 #define CR_NEW_STMT_METADATA                    2057
 #define CR_ALREADY_CONNECTED                    2058
 #define CR_AUTH_PLUGIN_CANNOT_LOAD              2059
-#define CR_ERROR_LAST  /*Copy last error nr:*/  2059
+#define CR_DUPLICATE_CONNECTION_ATTR            2060
+#define CR_ERROR_LAST  /*Copy last error nr:*/  2060
 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */
 
 #endif /* ERRMSG_INCLUDED */

=== modified file 'include/mysql.h'
--- a/include/mysql.h	2011-12-09 21:08:37 +0000
+++ b/include/mysql.h	2012-03-30 16:01:10 +0000
@@ -170,7 +170,9 @@ enum mysql_option 
   MYSQL_OPT_BIND,
   MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CERT, 
   MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL_CAPATH, MYSQL_OPT_SSL_CIPHER,
-  MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH
+  MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH,
+  MYSQL_OPT_CONNECT_ATTR_RESET, MYSQL_OPT_CONNECT_ATTR_ADD,
+  MYSQL_OPT_CONNECT_ATTR_DELETE
 };
 
 /**
@@ -460,6 +462,8 @@ MYSQL_RES *	STDCALL mysql_list_tables(MY
 MYSQL_RES *	STDCALL mysql_list_processes(MYSQL *mysql);
 int		STDCALL mysql_options(MYSQL *mysql,enum mysql_option option,
 				      const void *arg);
+int		STDCALL mysql_options4(MYSQL *mysql,enum mysql_option option,
+                                       const void *arg1, const void *arg2);
 void		STDCALL mysql_free_result(MYSQL_RES *result);
 void		STDCALL mysql_data_seek(MYSQL_RES *result,
 					my_ulonglong offset);

=== modified file 'include/mysql.h.pp'
--- a/include/mysql.h.pp	2012-03-06 14:29:42 +0000
+++ b/include/mysql.h.pp	2012-03-30 16:01:10 +0000
@@ -267,7 +267,9 @@ enum mysql_option
   MYSQL_OPT_BIND,
   MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CERT,
   MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL_CAPATH, MYSQL_OPT_SSL_CIPHER,
-  MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH
+  MYSQL_OPT_SSL_CRL, MYSQL_OPT_SSL_CRLPATH,
+  MYSQL_OPT_CONNECT_ATTR_RESET, MYSQL_OPT_CONNECT_ATTR_ADD,
+  MYSQL_OPT_CONNECT_ATTR_DELETE
 };
 struct st_mysql_options_extention;
 struct st_mysql_options {
@@ -467,6 +469,8 @@ MYSQL_RES * mysql_list_tables(MYSQL *mys
 MYSQL_RES * mysql_list_processes(MYSQL *mysql);
 int mysql_options(MYSQL *mysql,enum mysql_option option,
           const void *arg);
+int mysql_options4(MYSQL *mysql,enum mysql_option option,
+                                       const void *arg1, const void *arg2);
 void mysql_free_result(MYSQL_RES *result);
 void mysql_data_seek(MYSQL_RES *result,
      my_ulonglong offset);

=== modified file 'include/mysql/psi/psi.h'
--- a/include/mysql/psi/psi.h	2012-03-05 10:57:53 +0000
+++ b/include/mysql/psi/psi.h	2012-03-30 16:01:10 +0000
@@ -1871,6 +1871,19 @@ typedef struct PSI_digest_locker* (*dige
   (struct PSI_digest_locker *locker, uint token, struct OPAQUE_LEX_YYSTYPE *yylval);
 
 /**
+  Stores an array of connection attributes
+  @param buffer         char array of length encoded connection attributes
+                        in network format
+  @param length         legnth of the data in buffer
+  @param from_cs        charset in which @buffer is encodded
+  @return state
+    @retval  non-0    attributes truncated
+    @retval  0        stored the attribute
+*/
+typedef int (*thread_set_connect_attrs_v1_t)(const char *buffer, uint length,
+                                             const void *from_cs);
+
+/**
   Performance Schema Interface, version 1.
   @since PSI_VERSION_1
 */
@@ -2065,6 +2078,8 @@ struct PSI_v1
   digest_start_v1_t digest_start;
   /** @sa digest_add_token_v1_t. */
   digest_add_token_v1_t digest_add_token;
+  /** @sa thread_set_connect_attrs_v1_t. */
+  thread_set_connect_attrs_v1_t thread_set_connect_attrs;
 };
 
 /** @} (end of group Group_PSI_v1) */

=== modified file 'include/mysql/psi/psi_abi_v1.h.pp'
--- a/include/mysql/psi/psi_abi_v1.h.pp	2012-03-05 10:57:53 +0000
+++ b/include/mysql/psi/psi_abi_v1.h.pp	2012-03-30 16:01:10 +0000
@@ -499,6 +499,8 @@ typedef struct PSI_digest_locker * (*dig
   (struct PSI_statement_locker *locker);
 typedef struct PSI_digest_locker* (*digest_add_token_v1_t)
   (struct PSI_digest_locker *locker, uint token, struct OPAQUE_LEX_YYSTYPE *yylval);
+typedef int (*thread_set_connect_attrs_v1_t)(const char *buffer, uint length,
+                                             const void *from_cs);
 struct PSI_v1
 {
   register_mutex_v1_t register_mutex;
@@ -596,6 +598,7 @@ struct PSI_v1
   set_socket_thread_owner_v1_t set_socket_thread_owner;
   digest_start_v1_t digest_start;
   digest_add_token_v1_t digest_add_token;
+  thread_set_connect_attrs_v1_t thread_set_connect_attrs;
 };
 typedef struct PSI_v1 PSI;
 typedef struct PSI_mutex_info_v1 PSI_mutex_info;

=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h	2012-03-28 11:25:37 +0000
+++ b/include/mysql_com.h	2012-03-30 16:01:10 +0000
@@ -172,6 +172,7 @@ enum enum_server_command
 #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
 
 #define CLIENT_PLUGIN_AUTH  (1UL << 19) /* Client supports plugin authentication */
+#define CLIENT_CONNECT_ATTRS (1UL << 20) /* Client supports connection attributes */
 
 #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
 #define CLIENT_REMEMBER_OPTIONS (1UL << 31)
@@ -204,7 +205,8 @@ enum enum_server_command
                            CLIENT_PS_MULTI_RESULTS | \
                            CLIENT_SSL_VERIFY_SERVER_CERT | \
                            CLIENT_REMEMBER_OPTIONS | \
-                           CLIENT_PLUGIN_AUTH)
+                           CLIENT_PLUGIN_AUTH | \
+                           CLIENT_CONNECT_ATTRS)
 
 /*
   Switch off the flags that are optional and depending on build flags

=== modified file 'include/sql_common.h'
--- a/include/sql_common.h	2012-03-06 14:29:42 +0000
+++ b/include/sql_common.h	2012-03-30 16:01:10 +0000
@@ -23,6 +23,7 @@ extern "C" {
 #endif
 
 #include <mysql.h>
+#include <hash.h>
 
 extern const char	*unknown_sqlstate;
 extern const char	*cant_connect_sqlstate;
@@ -33,6 +34,8 @@ struct st_mysql_options_extention {
   char *default_auth;
   char *ssl_crl;				/* PEM CRL file */
   char *ssl_crlpath;				/* PEM directory of CRL-s? */
+  HASH connection_attributes;
+  size_t connection_attributes_length;
 };
 
 typedef struct st_mysql_methods
@@ -105,6 +108,7 @@ int mysql_client_plugin_init();
 void mysql_client_plugin_deinit();
 struct st_mysql_client_plugin;
 extern struct st_mysql_client_plugin *mysql_client_builtins[];
+uchar * send_client_connect_attrs(MYSQL *mysql, uchar *buf);
 
 #ifdef	__cplusplus
 }

=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h	2011-09-27 12:11:16 +0000
+++ b/libmysql/client_settings.h	2012-03-30 16:01:10 +0000
@@ -34,7 +34,8 @@ extern char *	mysql_unix_port;
                              CLIENT_SECURE_CONNECTION | \
                              CLIENT_MULTI_RESULTS | \
                              CLIENT_PS_MULTI_RESULTS | \
-                             CLIENT_PLUGIN_AUTH)
+                             CLIENT_PLUGIN_AUTH | \
+                             CLIENT_CONNECT_ATTRS)
 
 sig_handler my_pipe_sig_handler(int sig);
 void read_user_name(char *name);

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2012-03-13 13:16:27 +0000
+++ b/libmysqld/lib_sql.cc	2012-03-30 16:01:10 +0000
@@ -728,12 +728,36 @@ err:
 }
 
 
+static void
+emb_transfer_connect_attrs(MYSQL *mysql)
+{
+  if (mysql->options.extension &&
+      mysql->options.extension->connection_attributes_length)
+  {
+    uchar *buf;
+    THD *thd= (THD*)mysql->thd;
+    size_t length= mysql->options.extension->connection_attributes_length;
+
+    buf= (uchar *) my_alloca(length + 2);
+    send_client_connect_attrs(mysql, buf);
+    PSI_CALL(thread_set_connect_attrs)((char *) (buf + 2), length,
+                                       thd->charset());
+
+    my_afree(buf);
+  }
+}
+
+
 #ifdef NO_EMBEDDED_ACCESS_CHECKS
 int check_embedded_connection(MYSQL *mysql, const char *db)
 {
   int result;
   LEX_STRING db_str = { (char*)db, db ? strlen(db) : 0 };
   THD *thd= (THD*)mysql->thd;
+
+  /* the server does the same as the client */
+  mysql->server_capabilities= mysql->client_flag;
+
   thd_init_client_charset(thd, mysql->charset->number);
   thd->update_charset();
   Security_context *sctx= thd->security_ctx;
@@ -743,6 +767,7 @@ int check_embedded_connection(MYSQL *mys
   sctx->user= my_strdup(mysql->user, MYF(0));
   sctx->proxy_user[0]= 0;
   sctx->master_access= GLOBAL_ACLS;       // Full rights
+  emb_transfer_connect_attrs(mysql);
   /* Change database if necessary */
   if (!(result= (db && db[0] && mysql_change_db(thd, &db_str, FALSE))))
     my_ok(thd);
@@ -758,11 +783,17 @@ int check_embedded_connection(MYSQL *mys
     we emulate a COM_CHANGE_USER user here,
     it's easier than to emulate the complete 3-way handshake
   */
-  char buf[USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2], *end;
+  char *buf, *end;
   NET *net= &mysql->net;
   THD *thd= (THD*)mysql->thd;
   Security_context *sctx= thd->security_ctx;
+  size_t connect_attrs_len=
+    (mysql->server_capabilities & CLIENT_CONNECT_ATTRS &&
+     mysql->options.extension) ?
+    mysql->options.extension->connection_attributes_length : 0;
 
+  buf= my_alloca(USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2 +
+                 connect_attrs_len + 2);
   if (mysql->options.client_ip)
   {
     sctx->host= my_strdup(mysql->options.client_ip, MYF(0));
@@ -795,6 +826,13 @@ int check_embedded_connection(MYSQL *mys
   int2store(end, (ushort) mysql->charset->number);
   end+= 2;
 
+  end= strmake(end, "mysql_native_password", NAME_LEN) + 1;
+
+  /* the server does the same as the client */
+  mysql->server_capabilities= mysql->client_flag;
+
+  end= (char *) send_client_connect_attrs(mysql, (uchar *) end);
+
   /* acl_authenticate() takes the data from thd->net->read_pos */
   thd->net.read_pos= (uchar*)buf;
 
@@ -803,12 +841,14 @@ int check_embedded_connection(MYSQL *mys
     x_free(thd->security_ctx->user);
     goto err;
   }
+  my_afree(buf);
   return 0;
 err:
   strmake(net->last_error, thd->main_da.message(), sizeof(net->last_error)-1);
   memcpy(net->sqlstate,
          mysql_errno_to_sqlstate(thd->main_da.sql_errno()),
          sizeof(net->sqlstate)-1);
+  my_afree(buf);
   return 1;
 }
 #endif

=== added file 'mysql-test/suite/perfschema/r/connect_attrs.result'
--- a/mysql-test/suite/perfschema/r/connect_attrs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/r/connect_attrs.result	2012-03-30 16:01:10 +0000
@@ -0,0 +1,46 @@
+# must return 0, 6
+SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*)
+FROM PERFORMANCE_SCHEMA.session_connect_attrs
+WHERE ATTR_NAME IN ('_os', '_client_name', '_pid',
+'_client_version', '_platform', 'program_name')
+AND PROCESS_ID = CONNECTION_ID();
+SUM(ISNULL(ATTR_VALUE))	COUNT(*)
+0	6
+# must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+COUNT(DISTINCT PROCESS_ID)
+1
+# must return 0, 6
+SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*)
+FROM PERFORMANCE_SCHEMA.session_account_connect_attrs
+WHERE ATTR_NAME IN ('_os', '_client_name', '_pid',
+'_client_version', '_platform', 'program_name')
+AND PROCESS_ID = CONNECTION_ID();
+SUM(ISNULL(ATTR_VALUE))	COUNT(*)
+0	6
+# must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+COUNT(DISTINCT PROCESS_ID)
+1
+CREATE USER wl5924@localhost;
+# must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+COUNT(DISTINCT PROCESS_ID)
+1
+# must return 2
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+COUNT(DISTINCT PROCESS_ID)
+2
+# must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+COUNT(DISTINCT PROCESS_ID)
+1
+SELECT COUNT(DISTINCT PROCESS_ID)
+FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+ERROR 42000: SELECT command denied to user 'wl5924'@'localhost' for table 'session_connect_attrs'
+DROP USER wl5924@localhost;

=== added file 'mysql-test/suite/perfschema/r/ddl_session_account_connect_attrs.result'
--- a/mysql-test/suite/perfschema/r/ddl_session_account_connect_attrs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/r/ddl_session_account_connect_attrs.result	2012-03-30 16:01:10 +0000
@@ -0,0 +1,9 @@
+ALTER TABLE performance_schema.session_account_connect_attrs
+ADD COLUMN foo INTEGER;
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'
+TRUNCATE TABLE performance_schema.session_account_connect_attrs;
+ERROR HY000: Invalid performance_schema usage.
+ALTER TABLE performance_schema.session_account_connect_attrs ADD INDEX test_index(ATTR_NAME);
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'
+CREATE UNIQUE INDEX test_index ON performance_schema.session_account_connect_attrs(ATTR_NAME);
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'

=== added file 'mysql-test/suite/perfschema/r/ddl_session_connect_attrs.result'
--- a/mysql-test/suite/perfschema/r/ddl_session_connect_attrs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/r/ddl_session_connect_attrs.result	2012-03-30 16:01:10 +0000
@@ -0,0 +1,9 @@
+ALTER TABLE performance_schema.session_connect_attrs
+ADD COLUMN foo INTEGER;
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'
+TRUNCATE TABLE performance_schema.session_connect_attrs;
+ERROR HY000: Invalid performance_schema usage.
+ALTER TABLE performance_schema.session_connect_attrs ADD INDEX test_index(ATTR_NAME);
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'
+CREATE UNIQUE INDEX test_index ON performance_schema.session_connect_attrs(ATTR_NAME);
+ERROR 42000: Access denied for user 'root'@'localhost' to database 'performance_schema'

=== modified file 'mysql-test/suite/perfschema/r/dml_handler.result'
--- a/mysql-test/suite/perfschema/r/dml_handler.result	2012-02-28 14:40:36 +0000
+++ b/mysql-test/suite/perfschema/r/dml_handler.result	2012-03-30 16:01:10 +0000
@@ -9,45 +9,51 @@ SELECT COUNT(*) FROM table_list INTO @ta
 # For each table in the performance schema, attempt HANDLER...OPEN,
 # which should fail with an error 1031, ER_ILLEGAL_HA.
 
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=50;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=52;
 HANDLER performance_schema.users OPEN;
 ERROR HY000: Table storage engine for 'users' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=49;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=51;
 HANDLER performance_schema.threads OPEN;
 ERROR HY000: Table storage engine for 'threads' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=48;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=50;
 HANDLER performance_schema.table_lock_waits_summary_by_table OPEN;
 ERROR HY000: Table storage engine for 'table_lock_waits_summary_by_table' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=47;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=49;
 HANDLER performance_schema.table_io_waits_summary_by_table OPEN;
 ERROR HY000: Table storage engine for 'table_io_waits_summary_by_table' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=46;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=48;
 HANDLER performance_schema.table_io_waits_summary_by_index_usage OPEN;
 ERROR HY000: Table storage engine for 'table_io_waits_summary_by_index_usage' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=45;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=47;
 HANDLER performance_schema.socket_summary_by_instance OPEN;
 ERROR HY000: Table storage engine for 'socket_summary_by_instance' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=44;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=46;
 HANDLER performance_schema.socket_summary_by_event_name OPEN;
 ERROR HY000: Table storage engine for 'socket_summary_by_event_name' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=43;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=45;
 HANDLER performance_schema.socket_instances OPEN;
 ERROR HY000: Table storage engine for 'socket_instances' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=42;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=44;
 HANDLER performance_schema.setup_timers OPEN;
 ERROR HY000: Table storage engine for 'setup_timers' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=41;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=43;
 HANDLER performance_schema.setup_objects OPEN;
 ERROR HY000: Table storage engine for 'setup_objects' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=40;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=42;
 HANDLER performance_schema.setup_instruments OPEN;
 ERROR HY000: Table storage engine for 'setup_instruments' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=39;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=41;
 HANDLER performance_schema.setup_consumers OPEN;
 ERROR HY000: Table storage engine for 'setup_consumers' doesn't have this option
-SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=38;
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=40;
 HANDLER performance_schema.setup_actors OPEN;
 ERROR HY000: Table storage engine for 'setup_actors' doesn't have this option
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=39;
+HANDLER performance_schema.session_connect_attrs OPEN;
+ERROR HY000: Table storage engine for 'session_connect_attrs' doesn't have this option
+SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=38;
+HANDLER performance_schema.session_account_connect_attrs OPEN;
+ERROR HY000: Table storage engine for 'session_account_connect_attrs' doesn't have this option
 SELECT TABLE_NAME INTO @table_name FROM table_list WHERE id=37;
 HANDLER performance_schema.rwlock_instances OPEN;
 ERROR HY000: Table storage engine for 'rwlock_instances' doesn't have this option

=== added file 'mysql-test/suite/perfschema/r/dml_session_account_connect_attrs.result'
--- a/mysql-test/suite/perfschema/r/dml_session_account_connect_attrs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/r/dml_session_account_connect_attrs.result	2012-03-30 16:01:10 +0000
@@ -0,0 +1,25 @@
+SELECT * FROM performance_schema.session_account_connect_attrs
+LIMIT 1;
+SELECT * FROM performance_schema.session_account_connect_attrs
+where ATTR_NAME='FOO' OR ATTR_VALUE='BAR';
+INSERT INTO performance_schema.session_account_connect_attrs
+SET ATTR_NAME='FOO', ATTR_VALUE='BAR',
+ORDINAL_POSITION=100, PROCESS_ID=102;
+ERROR 42000: INSERT command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+UPDATE performance_schema.session_account_connect_attrs
+SET ATTR_NAME='FOO';
+ERROR 42000: UPDATE command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+UPDATE performance_schema.session_account_connect_attrs
+SET ATTR_NAME='FOO' WHERE ATTR_VALUE='BAR';
+ERROR 42000: UPDATE command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+DELETE FROM performance_schema.session_account_connect_attrs
+WHERE ATTR_VALUE='BAR';
+ERROR 42000: DELETE command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+DELETE FROM performance_schema.session_account_connect_attrs;
+ERROR 42000: DELETE command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+LOCK TABLES performance_schema.session_account_connect_attrs READ;
+ERROR 42000: SELECT, LOCK TABLES command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+UNLOCK TABLES;
+LOCK TABLES performance_schema.session_account_connect_attrs WRITE;
+ERROR 42000: SELECT, LOCK TABLES command denied to user 'root'@'localhost' for table 'session_account_connect_attrs'
+UNLOCK TABLES;

=== added file 'mysql-test/suite/perfschema/r/dml_session_connect_attrs.result'
--- a/mysql-test/suite/perfschema/r/dml_session_connect_attrs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/r/dml_session_connect_attrs.result	2012-03-30 16:01:10 +0000
@@ -0,0 +1,25 @@
+SELECT * FROM performance_schema.session_connect_attrs
+LIMIT 1;
+SELECT * FROM performance_schema.session_connect_attrs
+where ATTR_NAME='FOO' OR ATTR_VALUE='BAR';
+INSERT INTO performance_schema.session_connect_attrs
+SET ATTR_NAME='FOO', ATTR_VALUE='BAR',
+ORDINAL_POSITION=100, PROCESS_ID=102;
+ERROR 42000: INSERT command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+UPDATE performance_schema.session_connect_attrs
+SET ATTR_NAME='FOO';
+ERROR 42000: UPDATE command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+UPDATE performance_schema.session_connect_attrs
+SET ATTR_NAME='FOO' WHERE ATTR_VALUE='BAR';
+ERROR 42000: UPDATE command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+DELETE FROM performance_schema.session_connect_attrs
+WHERE ATTR_VALUE='BAR';
+ERROR 42000: DELETE command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+DELETE FROM performance_schema.session_connect_attrs;
+ERROR 42000: DELETE command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+LOCK TABLES performance_schema.session_connect_attrs READ;
+ERROR 42000: SELECT, LOCK TABLES command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+UNLOCK TABLES;
+LOCK TABLES performance_schema.session_connect_attrs WRITE;
+ERROR 42000: SELECT, LOCK TABLES command denied to user 'root'@'localhost' for table 'session_connect_attrs'
+UNLOCK TABLES;

=== modified file 'mysql-test/suite/perfschema/r/information_schema.result'
--- a/mysql-test/suite/perfschema/r/information_schema.result	2012-02-28 14:40:36 +0000
+++ b/mysql-test/suite/perfschema/r/information_schema.result	2012-03-30 16:01:10 +0000
@@ -39,6 +39,8 @@ performance_schema	mutex_instances	def
 performance_schema	objects_summary_global_by_type	def
 performance_schema	performance_timers	def
 performance_schema	rwlock_instances	def
+performance_schema	session_account_connect_attrs	def
+performance_schema	session_connect_attrs	def
 performance_schema	setup_actors	def
 performance_schema	setup_consumers	def
 performance_schema	setup_instruments	def
@@ -93,6 +95,8 @@ mutex_instances	BASE TABLE	PERFORMANCE_S
 objects_summary_global_by_type	BASE TABLE	PERFORMANCE_SCHEMA
 performance_timers	BASE TABLE	PERFORMANCE_SCHEMA
 rwlock_instances	BASE TABLE	PERFORMANCE_SCHEMA
+session_account_connect_attrs	BASE TABLE	PERFORMANCE_SCHEMA
+session_connect_attrs	BASE TABLE	PERFORMANCE_SCHEMA
 setup_actors	BASE TABLE	PERFORMANCE_SCHEMA
 setup_consumers	BASE TABLE	PERFORMANCE_SCHEMA
 setup_instruments	BASE TABLE	PERFORMANCE_SCHEMA
@@ -147,6 +151,8 @@ mutex_instances	10	Dynamic
 objects_summary_global_by_type	10	Dynamic
 performance_timers	10	Fixed
 rwlock_instances	10	Dynamic
+session_account_connect_attrs	10	Dynamic
+session_connect_attrs	10	Dynamic
 setup_actors	10	Fixed
 setup_consumers	10	Dynamic
 setup_instruments	10	Dynamic
@@ -201,6 +207,8 @@ mutex_instances	1000	0
 objects_summary_global_by_type	1000	0
 performance_timers	5	0
 rwlock_instances	1000	0
+session_account_connect_attrs	1000	0
+session_connect_attrs	1000	0
 setup_actors	1	0
 setup_consumers	12	0
 setup_instruments	1000	0
@@ -255,6 +263,8 @@ mutex_instances	0	0
 objects_summary_global_by_type	0	0
 performance_timers	0	0
 rwlock_instances	0	0
+session_account_connect_attrs	0	0
+session_connect_attrs	0	0
 setup_actors	0	0
 setup_consumers	0	0
 setup_instruments	0	0
@@ -309,6 +319,8 @@ mutex_instances	0	0	NULL
 objects_summary_global_by_type	0	0	NULL
 performance_timers	0	0	NULL
 rwlock_instances	0	0	NULL
+session_account_connect_attrs	0	0	NULL
+session_connect_attrs	0	0	NULL
 setup_actors	0	0	NULL
 setup_consumers	0	0	NULL
 setup_instruments	0	0	NULL
@@ -363,6 +375,8 @@ mutex_instances	NULL	NULL	NULL
 objects_summary_global_by_type	NULL	NULL	NULL
 performance_timers	NULL	NULL	NULL
 rwlock_instances	NULL	NULL	NULL
+session_account_connect_attrs	NULL	NULL	NULL
+session_connect_attrs	NULL	NULL	NULL
 setup_actors	NULL	NULL	NULL
 setup_consumers	NULL	NULL	NULL
 setup_instruments	NULL	NULL	NULL
@@ -417,6 +431,8 @@ mutex_instances	utf8_general_ci	NULL
 objects_summary_global_by_type	utf8_general_ci	NULL
 performance_timers	utf8_general_ci	NULL
 rwlock_instances	utf8_general_ci	NULL
+session_account_connect_attrs	utf8_bin	NULL
+session_connect_attrs	utf8_bin	NULL
 setup_actors	utf8_general_ci	NULL
 setup_consumers	utf8_general_ci	NULL
 setup_instruments	utf8_general_ci	NULL
@@ -471,6 +487,8 @@ mutex_instances	
 objects_summary_global_by_type	
 performance_timers	
 rwlock_instances	
+session_account_connect_attrs	
+session_connect_attrs	
 setup_actors	
 setup_consumers	
 setup_instruments	

=== modified file 'mysql-test/suite/perfschema/r/pfs_upgrade.result'
--- a/mysql-test/suite/perfschema/r/pfs_upgrade.result	2012-03-14 15:41:23 +0000
+++ b/mysql-test/suite/perfschema/r/pfs_upgrade.result	2012-03-30 16:01:10 +0000
@@ -60,7 +60,9 @@ ERROR 1050 (42S01) at line 1186: Table '
 ERROR 1050 (42S01) at line 1195: Table 'users' already exists
 ERROR 1050 (42S01) at line 1205: Table 'accounts' already exists
 ERROR 1050 (42S01) at line 1239: Table 'events_statements_summary_by_digest' already exists
-ERROR 1644 (HY000) at line 1864: Unexpected content found in the performance_schema database.
+ERROR 1050 (42S01) at line 1249: Table 'session_connect_attrs' already exists
+ERROR 1050 (42S01) at line 1255: Table 'session_account_connect_attrs' already exists
+ERROR 1644 (HY000) at line 1880: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 show tables like "user_table";
 Tables_in_performance_schema (user_table)
@@ -125,7 +127,9 @@ ERROR 1050 (42S01) at line 1186: Table '
 ERROR 1050 (42S01) at line 1195: Table 'users' already exists
 ERROR 1050 (42S01) at line 1205: Table 'accounts' already exists
 ERROR 1050 (42S01) at line 1239: Table 'events_statements_summary_by_digest' already exists
-ERROR 1644 (HY000) at line 1864: Unexpected content found in the performance_schema database.
+ERROR 1050 (42S01) at line 1249: Table 'session_connect_attrs' already exists
+ERROR 1050 (42S01) at line 1255: Table 'session_account_connect_attrs' already exists
+ERROR 1644 (HY000) at line 1880: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 show tables like "user_view";
 Tables_in_performance_schema (user_view)
@@ -188,7 +192,9 @@ ERROR 1050 (42S01) at line 1186: Table '
 ERROR 1050 (42S01) at line 1195: Table 'users' already exists
 ERROR 1050 (42S01) at line 1205: Table 'accounts' already exists
 ERROR 1050 (42S01) at line 1239: Table 'events_statements_summary_by_digest' already exists
-ERROR 1644 (HY000) at line 1864: Unexpected content found in the performance_schema database.
+ERROR 1050 (42S01) at line 1249: Table 'session_connect_attrs' already exists
+ERROR 1050 (42S01) at line 1255: Table 'session_account_connect_attrs' already exists
+ERROR 1644 (HY000) at line 1880: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 select name from mysql.proc where db='performance_schema';
 name
@@ -251,7 +257,9 @@ ERROR 1050 (42S01) at line 1186: Table '
 ERROR 1050 (42S01) at line 1195: Table 'users' already exists
 ERROR 1050 (42S01) at line 1205: Table 'accounts' already exists
 ERROR 1050 (42S01) at line 1239: Table 'events_statements_summary_by_digest' already exists
-ERROR 1644 (HY000) at line 1864: Unexpected content found in the performance_schema database.
+ERROR 1050 (42S01) at line 1249: Table 'session_connect_attrs' already exists
+ERROR 1050 (42S01) at line 1255: Table 'session_account_connect_attrs' already exists
+ERROR 1644 (HY000) at line 1880: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 select name from mysql.proc where db='performance_schema';
 name
@@ -314,7 +322,9 @@ ERROR 1050 (42S01) at line 1186: Table '
 ERROR 1050 (42S01) at line 1195: Table 'users' already exists
 ERROR 1050 (42S01) at line 1205: Table 'accounts' already exists
 ERROR 1050 (42S01) at line 1239: Table 'events_statements_summary_by_digest' already exists
-ERROR 1644 (HY000) at line 1864: Unexpected content found in the performance_schema database.
+ERROR 1050 (42S01) at line 1249: Table 'session_connect_attrs' already exists
+ERROR 1050 (42S01) at line 1255: Table 'session_account_connect_attrs' already exists
+ERROR 1644 (HY000) at line 1880: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 select name from mysql.event where db='performance_schema';
 name

=== modified file 'mysql-test/suite/perfschema/r/schema.result'
--- a/mysql-test/suite/perfschema/r/schema.result	2012-03-12 16:51:23 +0000
+++ b/mysql-test/suite/perfschema/r/schema.result	2012-03-30 16:01:10 +0000
@@ -44,6 +44,8 @@ mutex_instances
 objects_summary_global_by_type
 performance_timers
 rwlock_instances
+session_account_connect_attrs
+session_connect_attrs
 setup_actors
 setup_consumers
 setup_instruments

=== modified file 'mysql-test/suite/perfschema/r/table_schema.result'
--- a/mysql-test/suite/perfschema/r/table_schema.result	2012-03-12 16:51:23 +0000
+++ b/mysql-test/suite/perfschema/r/table_schema.result	2012-03-30 16:01:10 +0000
@@ -551,6 +551,14 @@ def	performance_schema	rwlock_instances	
 def	performance_schema	rwlock_instances	OBJECT_INSTANCE_BEGIN	2	NULL	NO	bigint	NULL	NULL	20	0	NULL	NULL	NULL	bigint(20) unsigned			select,insert,update,references	
 def	performance_schema	rwlock_instances	WRITE_LOCKED_BY_THREAD_ID	3	NULL	YES	int	NULL	NULL	10	0	NULL	NULL	NULL	int(11)			select,insert,update,references	
 def	performance_schema	rwlock_instances	READ_LOCKED_BY_COUNT	4	NULL	NO	int	NULL	NULL	10	0	NULL	NULL	NULL	int(10) unsigned			select,insert,update,references	
+def	performance_schema	session_account_connect_attrs	PROCESS_ID	1	NULL	NO	int	NULL	NULL	10	0	NULL	NULL	NULL	int(11)			select,insert,update,references	
+def	performance_schema	session_account_connect_attrs	ATTR_NAME	2	NULL	NO	varchar	32	96	NULL	NULL	NULL	utf8	utf8_bin	varchar(32)			select,insert,update,references	
+def	performance_schema	session_account_connect_attrs	ATTR_VALUE	3	NULL	YES	varchar	1024	3072	NULL	NULL	NULL	utf8	utf8_bin	varchar(1024)			select,insert,update,references	
+def	performance_schema	session_account_connect_attrs	ORDINAL_POSITION	4	NULL	YES	int	NULL	NULL	10	0	NULL	NULL	NULL	int(11)			select,insert,update,references	
+def	performance_schema	session_connect_attrs	PROCESS_ID	1	NULL	NO	int	NULL	NULL	10	0	NULL	NULL	NULL	int(11)			select,insert,update,references	
+def	performance_schema	session_connect_attrs	ATTR_NAME	2	NULL	NO	varchar	32	96	NULL	NULL	NULL	utf8	utf8_bin	varchar(32)			select,insert,update,references	
+def	performance_schema	session_connect_attrs	ATTR_VALUE	3	NULL	YES	varchar	1024	3072	NULL	NULL	NULL	utf8	utf8_bin	varchar(1024)			select,insert,update,references	
+def	performance_schema	session_connect_attrs	ORDINAL_POSITION	4	NULL	YES	int	NULL	NULL	10	0	NULL	NULL	NULL	int(11)			select,insert,update,references	
 def	performance_schema	setup_actors	HOST	1	%	NO	char	60	180	NULL	NULL	NULL	utf8	utf8_bin	char(60)			select,insert,update,references	
 def	performance_schema	setup_actors	USER	2	%	NO	char	16	48	NULL	NULL	NULL	utf8	utf8_bin	char(16)			select,insert,update,references	
 def	performance_schema	setup_actors	ROLE	3	%	NO	char	16	48	NULL	NULL	NULL	utf8	utf8_bin	char(16)			select,insert,update,references	

=== added file 'mysql-test/suite/perfschema/t/connect_attrs.test'
--- a/mysql-test/suite/perfschema/t/connect_attrs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/t/connect_attrs.test	2012-03-30 16:01:10 +0000
@@ -0,0 +1,73 @@
+# Session connect attributes test
+
+# although the connection attributes transfer code works
+# with embedded P_S is not active, so the test won't run.
+# TODO: remove this when P_S works with embedded.
+--source include/not_embedded.inc
+
+# make sure we're alone
+let $count_sessions= 1;
+--source include/wait_until_count_sessions.inc
+
+# basic PERFORMANCE_SCHEMA.session_connect_attrs tests
+
+# check the presense of the pre-defined attributes
+--echo # must return 0, 6
+SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*)
+  FROM PERFORMANCE_SCHEMA.session_connect_attrs
+  WHERE ATTR_NAME IN ('_os', '_client_name', '_pid',
+                      '_client_version', '_platform', 'program_name')
+    AND PROCESS_ID = CONNECTION_ID();
+
+# check the presense of the pre-defined attributes
+--echo # must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+
+
+# basic PERFORMANCE_SCHEMA.session_account_connect_attrs tests
+
+# check the presense of the pre-defined attributes
+--echo # must return 0, 6
+SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*)
+  FROM PERFORMANCE_SCHEMA.session_account_connect_attrs
+  WHERE ATTR_NAME IN ('_os', '_client_name', '_pid',
+                      '_client_version', '_platform', 'program_name')
+    AND PROCESS_ID = CONNECTION_ID();
+
+# check the presense of the pre-defined attributes
+--echo # must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+
+
+
+CREATE USER wl5924@localhost;
+
+connect(non_privileged_user,localhost,wl5924,,test);
+connection default;
+
+--echo # must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+
+--echo # must return 2
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+
+connection non_privileged_user;
+--echo # must return 1
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_account_connect_attrs;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+SELECT COUNT(DISTINCT PROCESS_ID)
+  FROM PERFORMANCE_SCHEMA.session_connect_attrs;
+
+connection default;
+disconnect non_privileged_user;
+
+DROP USER wl5924@localhost;
+
+# Wait till all disconnects are completed
+--source include/wait_until_count_sessions.inc

=== added file 'mysql-test/suite/perfschema/t/ddl_session_account_connect_attrs.test'
--- a/mysql-test/suite/perfschema/t/ddl_session_account_connect_attrs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/t/ddl_session_account_connect_attrs.test	2012-03-30 16:01:10 +0000
@@ -0,0 +1,15 @@
+--source include/not_embedded.inc
+--source include/have_perfschema.inc
+
+-- error ER_DBACCESS_DENIED_ERROR
+ALTER TABLE performance_schema.session_account_connect_attrs
+  ADD COLUMN foo INTEGER;
+
+-- error ER_WRONG_PERFSCHEMA_USAGE
+TRUNCATE TABLE performance_schema.session_account_connect_attrs;
+
+-- error ER_DBACCESS_DENIED_ERROR
+ALTER TABLE performance_schema.session_account_connect_attrs ADD INDEX test_index(ATTR_NAME);
+
+-- error ER_DBACCESS_DENIED_ERROR
+CREATE UNIQUE INDEX test_index ON performance_schema.session_account_connect_attrs(ATTR_NAME);

=== added file 'mysql-test/suite/perfschema/t/ddl_session_connect_attrs.test'
--- a/mysql-test/suite/perfschema/t/ddl_session_connect_attrs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/t/ddl_session_connect_attrs.test	2012-03-30 16:01:10 +0000
@@ -0,0 +1,15 @@
+--source include/not_embedded.inc
+--source include/have_perfschema.inc
+
+-- error ER_DBACCESS_DENIED_ERROR
+ALTER TABLE performance_schema.session_connect_attrs
+  ADD COLUMN foo INTEGER;
+
+-- error ER_WRONG_PERFSCHEMA_USAGE
+TRUNCATE TABLE performance_schema.session_connect_attrs;
+
+-- error ER_DBACCESS_DENIED_ERROR
+ALTER TABLE performance_schema.session_connect_attrs ADD INDEX test_index(ATTR_NAME);
+
+-- error ER_DBACCESS_DENIED_ERROR
+CREATE UNIQUE INDEX test_index ON performance_schema.session_connect_attrs(ATTR_NAME);

=== added file 'mysql-test/suite/perfschema/t/dml_session_account_connect_attrs.test'
--- a/mysql-test/suite/perfschema/t/dml_session_account_connect_attrs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/t/dml_session_account_connect_attrs.test	2012-03-30 16:01:10 +0000
@@ -0,0 +1,38 @@
+--source include/not_embedded.inc
+--source include/have_perfschema.inc
+
+--disable_result_log
+SELECT * FROM performance_schema.session_account_connect_attrs
+  LIMIT 1;
+
+SELECT * FROM performance_schema.session_account_connect_attrs
+  where ATTR_NAME='FOO' OR ATTR_VALUE='BAR';
+--enable_result_log
+
+--error ER_TABLEACCESS_DENIED_ERROR
+INSERT INTO performance_schema.session_account_connect_attrs
+  SET ATTR_NAME='FOO', ATTR_VALUE='BAR',
+  ORDINAL_POSITION=100, PROCESS_ID=102;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+UPDATE performance_schema.session_account_connect_attrs
+  SET ATTR_NAME='FOO';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+UPDATE performance_schema.session_account_connect_attrs
+  SET ATTR_NAME='FOO' WHERE ATTR_VALUE='BAR';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+DELETE FROM performance_schema.session_account_connect_attrs
+  WHERE ATTR_VALUE='BAR';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+DELETE FROM performance_schema.session_account_connect_attrs;
+
+-- error ER_TABLEACCESS_DENIED_ERROR
+LOCK TABLES performance_schema.session_account_connect_attrs READ;
+UNLOCK TABLES;
+
+-- error ER_TABLEACCESS_DENIED_ERROR
+LOCK TABLES performance_schema.session_account_connect_attrs WRITE;
+UNLOCK TABLES;

=== added file 'mysql-test/suite/perfschema/t/dml_session_connect_attrs.test'
--- a/mysql-test/suite/perfschema/t/dml_session_connect_attrs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/perfschema/t/dml_session_connect_attrs.test	2012-03-30 16:01:10 +0000
@@ -0,0 +1,38 @@
+--source include/not_embedded.inc
+--source include/have_perfschema.inc
+
+--disable_result_log
+SELECT * FROM performance_schema.session_connect_attrs
+  LIMIT 1;
+
+SELECT * FROM performance_schema.session_connect_attrs
+  where ATTR_NAME='FOO' OR ATTR_VALUE='BAR';
+--enable_result_log
+
+--error ER_TABLEACCESS_DENIED_ERROR
+INSERT INTO performance_schema.session_connect_attrs
+  SET ATTR_NAME='FOO', ATTR_VALUE='BAR',
+  ORDINAL_POSITION=100, PROCESS_ID=102;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+UPDATE performance_schema.session_connect_attrs
+  SET ATTR_NAME='FOO';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+UPDATE performance_schema.session_connect_attrs
+  SET ATTR_NAME='FOO' WHERE ATTR_VALUE='BAR';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+DELETE FROM performance_schema.session_connect_attrs
+  WHERE ATTR_VALUE='BAR';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+DELETE FROM performance_schema.session_connect_attrs;
+
+-- error ER_TABLEACCESS_DENIED_ERROR
+LOCK TABLES performance_schema.session_connect_attrs READ;
+UNLOCK TABLES;
+
+-- error ER_TABLEACCESS_DENIED_ERROR
+LOCK TABLES performance_schema.session_connect_attrs WRITE;
+UNLOCK TABLES;

=== modified file 'mysys/psi_noop.c'
--- a/mysys/psi_noop.c	2012-02-12 20:08:11 +0000
+++ b/mysys/psi_noop.c	2012-03-30 16:01:10 +0000
@@ -621,6 +621,14 @@ digest_add_token_noop(PSI_digest_locker 
   return NULL;
 }
 
+static int
+thread_set_connect_attrs_noop(const char *buffer __attribute__((unused)),
+                             uint length  __attribute__((unused)),
+                             const void *from_cs __attribute__((unused)))
+{
+  return 0;
+}
+
 static PSI PSI_noop=
 {
   register_mutex_noop,
@@ -716,7 +724,8 @@ static PSI PSI_noop=
   set_socket_info_noop,
   set_socket_thread_owner_noop,
   digest_start_noop,
-  digest_add_token_noop
+  digest_add_token_noop,
+  thread_set_connect_attrs_noop
 };
 
 /**

=== modified file 'scripts/mysql_system_tables.sql'
--- a/scripts/mysql_system_tables.sql	2012-03-14 15:41:23 +0000
+++ b/scripts/mysql_system_tables.sql	2012-03-30 16:01:10 +0000
@@ -1657,6 +1657,34 @@ PREPARE stmt FROM @str;
 EXECUTE stmt;
 DROP PREPARE stmt;
 
+--
+-- TABLE SESSION_CONNECT_ATTRS
+--
+
+SET @cmd="CREATE TABLE performance_schema.session_connect_attrs("
+  "PROCESS_ID INT NOT NULL,"
+  "ATTR_NAME VARCHAR(32) NOT NULL,"
+  "ATTR_VALUE VARCHAR(1024),"
+  "ORDINAL_POSITION INT"
+  ")ENGINE=PERFORMANCE_SCHEMA CHARACTER SET utf8 COLLATE utf8_bin;";
+
+SET @str = IF(@have_pfs = 1, @cmd, 'SET @dummy = 0');
+PREPARE stmt FROM @str;
+EXECUTE stmt;
+DROP PREPARE stmt;
+
+--
+-- TABLE SESSION_ACCOUNT_CONNECT_ATTRS
+--
+
+SET @cmd="CREATE TABLE performance_schema.session_account_connect_attrs "
+         " LIKE performance_schema.session_connect_attrs;";
+
+SET @str = IF(@have_pfs = 1, @cmd, 'SET @dummy = 0');
+PREPARE stmt FROM @str;
+EXECUTE stmt;
+DROP PREPARE stmt;
+
 CREATE TABLE IF NOT EXISTS proxies_priv (Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Proxied_host char(60) binary DEFAULT '' NOT NULL, Proxied_user char(16) binary DEFAULT '' NOT NULL, With_grant BOOL DEFAULT 0 NOT NULL, Grantor char(77) DEFAULT '' NOT NULL, Timestamp timestamp, PRIMARY KEY Host (Host,User,Proxied_host,Proxied_user), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User proxy privileges';
 
 -- Remember for later if proxies_priv table already existed

=== modified file 'sql-common/client.c'
--- a/sql-common/client.c	2011-12-09 21:08:37 +0000
+++ b/sql-common/client.c	2012-03-30 16:01:10 +0000
@@ -34,6 +34,7 @@
 #include <my_global.h>
 
 #include "mysql.h"
+#include "hash.h"
 
 /* Remove client convenience wrappers */
 #undef max_allowed_packet
@@ -1056,14 +1057,22 @@ static int add_init_command(struct st_my
   return 0;
 }
 
+#define ALLOCATE_EXTENSIONS(OPTS)                                \
+      (OPTS)->extension= (struct st_mysql_options_extention *)   \
+        my_malloc(sizeof(struct st_mysql_options_extention),     \
+                  MYF(MY_WME | MY_ZEROFILL))                     \
+
+#define ENSURE_EXTENSIONS_PRESENT(OPTS)                          \
+    if (!(OPTS)->extension)                                      \
+      ALLOCATE_EXTENSIONS(OPTS)                                  \
+
+
 #define EXTENSION_SET_STRING(OPTS, X, STR)                       \
     if ((OPTS)->extension)                                       \
       my_free((OPTS)->extension->X);                             \
     else                                                         \
-      (OPTS)->extension= (struct st_mysql_options_extention *)   \
-        my_malloc(sizeof(struct st_mysql_options_extention),     \
-                  MYF(MY_WME | MY_ZEROFILL));                    \
-    (OPTS)->extension->X= ((STR) != NULL) ?  \
+      ALLOCATE_EXTENSIONS(OPTS);                                 \
+    (OPTS)->extension->X= ((STR) != NULL) ?                      \
       my_strdup((STR), MYF(MY_WME)) : NULL
 
 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
@@ -2258,6 +2267,65 @@ struct st_mysql_client_plugin *mysql_cli
 };
 
 
+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 */
+    int2store(buf,
+              mysql->options.extension ?
+              mysql->options.extension->connection_attributes_length : 0);
+    buf+= 2;
+
+    /* 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);
+
+        /* store the key string */
+        buf= net_store_length(buf, key->length);
+        memcpy(buf, key->str, key->length);
+        buf+= key->length;
+
+        /* store the value string (optionally with zero length) */
+        buf= net_store_length(buf, value->length);
+        if (value->length)
+        {
+          memcpy(buf, value->str, value->length);
+          buf+= 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 {
@@ -2302,8 +2370,13 @@ static int send_change_user_packet(MCPVI
   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 + data_len + 1 + NAME_LEN + 2 + NAME_LEN);
+  buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN +
+                  connect_attrs_len + 2 /* for the length of the attrs */);
 
   end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
 
@@ -2340,6 +2413,8 @@ static int send_change_user_packet(MCPVI
   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);
 
@@ -2384,10 +2459,17 @@ static int send_client_reply_packet(MCPV
   MYSQL *mysql= mpvio->mysql;
   NET *net= &mysql->net;
   char *buff, *end;
+  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 < 65536);
 
   /* 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);
-  
+  buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN +
+                  connect_attrs_len + 2 /* for the length of the attrs */);
+
   mysql->client_flag|= mysql->options.client_flag;
   mysql->client_flag|= CLIENT_CAPABILITIES;
 
@@ -2541,6 +2623,8 @@ static int send_client_reply_packet(MCPV
   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))
   {
@@ -2877,6 +2961,55 @@ int run_plugin_auth(MYSQL *mysql, char *
   DBUG_RETURN (mysql->net.read_pos[0] != 0);
 }
 
+
+/** 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, "_command_line");
+  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(void));
+#else
+  snprintf(buff, buf_len, "%lu", (ulong) getpid());
+#endif
+  rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buff);
+
+#ifdef __WIN__
+  rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                      "_command_line", GetCommandLine());
+
+  snprintf(buff, buf_len, "%lu", (ulong) GetCurrentThreadId(void));
+  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,
@@ -2909,6 +3042,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
     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 */
@@ -3633,6 +3769,7 @@ static void mysql_close_free_options(MYS
   {
     my_free(mysql->options.extension->plugin_dir);
     my_free(mysql->options.extension->default_auth);
+    my_hash_free(&mysql->options.extension->connection_attributes);
     my_free(mysql->options.extension);
   }
   memset(&mysql->options, 0, sizeof(mysql->options));
@@ -4129,6 +4266,150 @@ mysql_options(MYSQL *mysql,enum mysql_op
   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;
+
+          my_hash_delete(&mysql->options.extension->connection_attributes,
+                         elt);
+
+          mysql->options.extension->connection_attributes_length-=
+            get_length_store_length(key->length) + key->length +
+            get_length_store_length(value->length) + value->length;
+        }
+      }
+    }
+    break;
+
+
+  default:
+    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;
+}
+
+#define MAX_CONNECTION_ATTR_STORAGE_LENGTH UINT_MAX
+
+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);
   }

=== modified file 'sql/client_settings.h'
--- a/sql/client_settings.h	2011-09-27 12:11:16 +0000
+++ b/sql/client_settings.h	2012-03-30 16:01:10 +0000
@@ -34,7 +34,8 @@
                              CLIENT_TRANSACTIONS |  \
                              CLIENT_PROTOCOL_41 |   \
                              CLIENT_SECURE_CONNECTION | \
-                             CLIENT_PLUGIN_AUTH)
+                             CLIENT_PLUGIN_AUTH | \
+                             CLIENT_CONNECT_ATTRS)
 
 #define read_user_name(A) {}
 #undef HAVE_SMEM

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2012-03-30 15:38:01 +0000
+++ b/sql/sql_acl.cc	2012-03-30 16:01:10 +0000
@@ -4617,14 +4617,12 @@ bool check_grant(THD *thd, ulong want_ac
       {
       case ACL_INTERNAL_ACCESS_GRANTED:
         /*
-          Currently,
-          -  the information_schema does not subclass ACL_internal_table_access,
-          there are no per table privilege checks for I_S,
-          - the performance schema does use per tables checks, but at most
-          returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
-          so this branch is not used.
+           Grant all access to the table to skip column checks.
+           Depend on the controls in the P_S table itself.
         */
-        DBUG_ASSERT(0);
+        tl->grant.privilege|= TMP_TABLE_ACLS;
+        tl->grant.want_privilege= 0;
+        continue;
       case ACL_INTERNAL_ACCESS_DENIED:
         goto err;
       case ACL_INTERNAL_ACCESS_CHECK_GRANT:
@@ -8398,6 +8396,33 @@ static bool find_mpvio_user(MPVIO_EXT *m
                       mpvio->acl_user->plugin.str));
   DBUG_RETURN(0);
 }
+
+
+static bool
+read_client_connect_attrs(char **ptr, size_t *max_bytes_available,
+                          const CHARSET_INFO *from_cs)
+{
+  uint length;
+  /* not enough bytes to hold the length */
+  if (*max_bytes_available < 2)
+    return true;
+
+  /* read the length */
+  length= uint2korr(*ptr);
+  *ptr+= 2;
+  *max_bytes_available-= 2;
+
+  /* length says there're more data than can fit into the packet */
+  if (length > *max_bytes_available)
+    return true;
+
+  if (PSI_CALL(thread_set_connect_attrs)(*ptr, length, from_cs) && log_warnings)
+    sql_print_warning("Connection attributes of length %u were truncated",
+                      length);
+  return false;
+}
+
+
 #endif
 
 /* the packet format is described in send_change_user_packet() */
@@ -8505,7 +8530,11 @@ static bool parse_com_change_user_packet
   else
   {
     if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
+    {
       client_plugin= native_password_plugin_name.str;
+      uint plugin_len= strlen(client_plugin);
+      ptr= client_plugin + plugin_len + 1;
+    }
     else
     {
       client_plugin=  old_password_plugin_name.str;
@@ -8520,6 +8549,14 @@ static bool parse_com_change_user_packet
     }
   }
 
+  size_t bytes_remaining_in_packet= end - ptr;
+
+  if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
+      ptr  + 2 < end &&
+      read_client_connect_attrs(&ptr, &bytes_remaining_in_packet,
+                                mpvio->charset_adapter->charset()))
+    return packet_error;
+
   DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
   /* 
     Remember the data part of the packet, to present it to plugin in 
@@ -8905,6 +8942,11 @@ skip_to_ssl:
   if (client_plugin == NULL)
     client_plugin= &empty_c_string[0];
 
+  if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
+      read_client_connect_attrs(&end, &bytes_remaining_in_packet,
+                                mpvio->charset_adapter->charset()))
+    return packet_error;
+
   char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
   char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
   uint dummy_errors;

=== modified file 'storage/perfschema/CMakeLists.txt'
--- a/storage/perfschema/CMakeLists.txt	2012-02-28 14:40:36 +0000
+++ b/storage/perfschema/CMakeLists.txt	2012-03-30 16:01:10 +0000
@@ -118,6 +118,9 @@ table_tiws_by_index_usage.h
 table_tiws_by_table.h
 table_tlws_by_table.h
 table_users.h
+cursor_by_thread_connect_attr.h
+table_session_connect_attrs.h
+table_session_account_connect_attrs.h
 cursor_by_account.cc
 cursor_by_host.cc
 cursor_by_thread.cc
@@ -189,6 +192,9 @@ table_tiws_by_index_usage.cc
 table_tiws_by_table.cc
 table_tlws_by_table.cc
 table_users.cc
+cursor_by_thread_connect_attr.cc
+table_session_connect_attrs.cc
+table_session_account_connect_attrs.cc
 )
 
 MYSQL_ADD_PLUGIN(perfschema ${PERFSCHEMA_SOURCES} STORAGE_ENGINE DEFAULT STATIC_ONLY)

=== added file 'storage/perfschema/cursor_by_thread_connect_attr.cc'
--- a/storage/perfschema/cursor_by_thread_connect_attr.cc	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/cursor_by_thread_connect_attr.cc	2012-03-30 16:01:10 +0000
@@ -0,0 +1,323 @@
+/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "my_global.h"
+#include "cursor_by_thread_connect_attr.h"
+
+static const TABLE_FIELD_TYPE field_types[]=
+{
+  {
+    { C_STRING_WITH_LEN("PROCESS_ID") },
+    { C_STRING_WITH_LEN("int(11)") },
+    { NULL, 0}
+  },
+  {
+    { C_STRING_WITH_LEN("ATTR_NAME") },
+    { C_STRING_WITH_LEN("varchar(32)") },
+    { NULL, 0}
+  },
+  {
+    { C_STRING_WITH_LEN("ATTR_VALUE") },
+    { C_STRING_WITH_LEN("varchar(1024)") },
+    { NULL, 0}
+  },
+  {
+    { C_STRING_WITH_LEN("ORDINAL_POSITION") },
+    { C_STRING_WITH_LEN("int(11)") },
+    { NULL, 0}
+  }
+};
+
+/** symbolic names for field offsets, keep in sync with field_types */
+enum field_offsets {
+  FO_PROCESS_ID,
+  FO_ATTR_NAME,
+  FO_ATTR_VALUE,
+  FO_ORDINAL_POSITION
+};
+
+TABLE_FIELD_DEF
+cursor_by_thread_connect_attr::m_field_def=
+{ 4, field_types };
+
+cursor_by_thread_connect_attr::cursor_by_thread_connect_attr(
+  const PFS_engine_table_share *share) :
+  PFS_engine_table(share, &m_pos), m_row_exists(false)
+{}
+
+/**
+  Take a length encoded string
+
+  @arg ptr  inout       the input string array
+  @arg dest             where to store the result
+  @arg dest_size        max size of @c dest
+  @arg copied_len       the actual length of the data copied
+  @arg start_ptr        pointer to the start of input
+  @arg input_length     the length of the incoming data
+  @arg copy_data        copy the data or just skip the input
+  @arg from_cs          character set in which @c ptr is encoded
+  @arg nchars_max       maximum number of characters to read
+  @return status
+    @retval true    parsing failed
+    @retval false   parsing succeeded
+*/
+static bool parse_length_encoded_string(const char **ptr,
+                                        char *dest, uint dest_size,
+                                        uint *copied_len,
+                                        const char *start_ptr, uint input_length,
+                                        bool copy_data,
+                                        const CHARSET_INFO *from_cs,
+                                        uint nchars_max)
+{
+  ulong copy_length, data_length;
+  const char *well_formed_error_pos= NULL, *cannot_convert_error_pos= NULL,
+        *from_end_pos= NULL;
+
+  copy_length= data_length= net_field_length((uchar **) ptr);
+
+  /* we don't tolerate NULL as a length */
+  if (data_length == NULL_LENGTH)
+    return true;
+
+  if (*ptr - start_ptr + data_length > input_length)
+    return true;
+
+  copy_length= well_formed_copy_nchars(&my_charset_utf8_bin, dest, dest_size,
+                                       from_cs, *ptr, data_length, nchars_max,
+                                       &well_formed_error_pos,
+                                       &cannot_convert_error_pos,
+                                       &from_end_pos);
+  *copied_len= copy_length;
+  (*ptr)+= data_length;
+
+  return false;
+}
+
+
+/**
+  Take the nth attribute name/value pair
+
+  Parse the attributes blob form the beginning, skipping the attributes
+  whose number is lower than the one we seek.
+  When we reach the attribute at an index we're looking for the values
+  are copied to the output parameters.
+  If parsing fails or no more attributes are found the function stops
+  and returns an error code.
+
+  @arg connect_attrs            pointer to the connect attributes blob
+  @arg connect_attrs_length     length of @c connect_attrs
+  @arg connect_attrs_cs         character set used to encode @c connect_attrs
+  @arg ordinal                  index of the attribute we need
+  @arg attr_name [out]          buffer to receive the attribute name
+  @arg max_attr_name            max size of @c attr_name in bytes
+  @arg attr_name_length [out]   number of bytes written in @attr_name
+  @arg attr_value [out]         buffer to receive the attribute name
+  @arg max_attr_value           max size of @c attr_value in bytes
+  @arg attr_value_length [out]  number of bytes written in @attr_value
+  @return status
+    @retval true    requested attribute pair is found and copied
+    @retval false   error. Either because of parsing or too few attributes.
+*/
+bool read_nth_attr(const char *connect_attrs, uint connect_attrs_length,
+                          const CHARSET_INFO *connect_attrs_cs,
+                          uint ordinal,
+                          char *attr_name, uint max_attr_name,
+                          uint *attr_name_length,
+                          char *attr_value, uint max_attr_value,
+                          uint *attr_value_length)
+{
+  uint idx;
+  const char *ptr;
+
+  for (ptr= connect_attrs, idx= 0;
+       (ptr - connect_attrs) < connect_attrs_length && idx <= ordinal;
+      idx++)
+  {
+    uint copy_length;
+    /* do the copying only if we absolutely have to */
+    bool fill_in_attr_name= idx == ordinal;
+    bool fill_in_attr_value= idx == ordinal;
+
+    /* read the key */
+    if (parse_length_encoded_string(&ptr,
+                                    attr_name, max_attr_name, &copy_length,
+                                    connect_attrs,
+				    connect_attrs_length,
+				    fill_in_attr_name,
+                                    connect_attrs_cs, 32) ||
+	!copy_length
+	)
+      return false;
+    if (idx == ordinal)
+      *attr_name_length= copy_length;
+
+
+    /* read the value */
+    if (parse_length_encoded_string(&ptr,
+                                    attr_value, max_attr_value, &copy_length,
+                                    connect_attrs,
+				    connect_attrs_length,
+				    fill_in_attr_value,
+                                    connect_attrs_cs, 1024))
+      return false;
+
+    if (idx == ordinal)
+      *attr_value_length= copy_length;
+
+    if (idx == ordinal)
+      return true;
+  }
+
+  return false;
+}
+
+
+int cursor_by_thread_connect_attr::rnd_next(void)
+{
+  PFS_thread *thread;
+  PFS_thread *current_thread= PFS_thread::get_current_thread();
+
+  for (m_pos.set_at(&m_next_pos);
+       m_pos.has_more_thread();
+       m_pos.next_thread())
+  {
+    thread= &thread_array[m_pos.m_index_1];
+
+    if (thread->m_lock.is_populated() && thread_fits(thread, current_thread))
+    {
+      make_row(thread, m_pos.m_index_2);
+      if (m_row_exists)
+      {
+        m_next_pos.set_after(&m_pos);
+	return 0;
+      }
+    }
+  }
+  return HA_ERR_END_OF_FILE;
+}
+
+
+int cursor_by_thread_connect_attr::rnd_pos(const void *pos)
+{
+  PFS_thread *thread;
+  PFS_thread *current_thread= PFS_thread::get_current_thread();
+
+  set_position(pos);
+  DBUG_ASSERT(m_pos.m_index_1 < thread_max);
+
+  thread= &thread_array[m_pos.m_index_1];
+  if (!thread->m_lock.is_populated() ||
+      !thread_fits(thread, current_thread))
+    return HA_ERR_RECORD_DELETED;
+
+  make_row(thread, m_pos.m_index_2);
+  if (m_row_exists)
+    return 0;
+
+  return HA_ERR_RECORD_DELETED;
+}
+
+
+void cursor_by_thread_connect_attr::reset_position(void)
+{
+  m_pos.reset();
+  m_next_pos.reset();
+}
+
+
+void cursor_by_thread_connect_attr::make_row(PFS_thread *pfs, uint ordinal)
+{
+  pfs_lock lock;
+  PFS_thread_class *safe_class;
+
+  m_row_exists= false;
+
+  /* Protect this reader against thread termination */
+  pfs->m_lock.begin_optimistic_lock(&lock);
+  safe_class= sanitize_thread_class(pfs->m_class);
+  if (unlikely(safe_class == NULL))
+    return;
+
+  /* populate the row */
+  if (read_nth_attr(pfs->m_connect_attrs, pfs->m_connect_attrs_length,
+                    pfs->m_connect_attrs_cs,
+                    ordinal,
+                    m_row.m_attr_name, (uint) sizeof(m_row.m_attr_name),
+                    &m_row.m_attr_name_length,
+                    m_row.m_attr_value, (uint) sizeof(m_row.m_attr_value),
+                    &m_row.m_attr_value_length))
+  {
+    /* we don't expect internal threads to have connection attributes */
+    DBUG_ASSERT(pfs->m_thread_id != 0);
+
+    m_row.m_ordinal_position= ordinal;
+    m_row.m_process_id= pfs->m_thread_id;
+  }
+  else
+    return;
+
+
+  if (pfs->m_lock.end_optimistic_lock(& lock))
+    m_row_exists= true;
+}
+
+
+int cursor_by_thread_connect_attr::read_row_values(TABLE *table,
+                                   unsigned char *buf,
+                                   Field **fields,
+                                   bool read_all)
+{
+  Field *f;
+
+  if (unlikely(!m_row_exists))
+    return HA_ERR_RECORD_DELETED;
+
+  /* Set the null bits */
+  DBUG_ASSERT(table->s->null_bytes == 1);
+  buf[0]= 0;
+
+  for (; (f= *fields) ; fields++)
+  {
+    if (read_all || bitmap_is_set(table->read_set, f->field_index))
+    {
+      switch(f->field_index)
+      {
+      case FO_PROCESS_ID:
+        if (m_row.m_process_id != 0)
+          set_field_ulong(f, m_row.m_process_id);
+        else
+          f->set_null();
+        break;
+      case FO_ATTR_NAME:
+        set_field_varchar_utf8(f, m_row.m_attr_name,
+			       m_row.m_attr_name_length);
+        break;
+      case FO_ATTR_VALUE:
+        if (m_row.m_attr_value_length)
+          set_field_varchar_utf8(f, m_row.m_attr_value,
+                                 m_row.m_attr_value_length);
+        else
+          f->set_null();
+        break;
+      case FO_ORDINAL_POSITION:
+	set_field_ulong(f, m_row.m_ordinal_position);
+        break;
+      default:
+        DBUG_ASSERT(false);
+      }
+    }
+  }
+  return 0;
+}

=== added file 'storage/perfschema/cursor_by_thread_connect_attr.h'
--- a/storage/perfschema/cursor_by_thread_connect_attr.h	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/cursor_by_thread_connect_attr.h	2012-03-30 16:01:10 +0000
@@ -0,0 +1,111 @@
+/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef CURSOR_BY_THREAD_CONNECT_ATTR_H
+#define CURSOR_BY_THREAD_CONNECT_ATTR_H
+
+#include "pfs_column_types.h"
+#include "pfs_engine_table.h"
+#include "pfs_instr.h"
+
+/**
+  \addtogroup Performance_schema_tables
+  @{
+*/
+
+struct pos_connect_attr_by_thread_by_attr
+: public PFS_double_index
+{
+  pos_connect_attr_by_thread_by_attr()
+    : PFS_double_index(0, 0)
+  {}
+
+  inline bool has_more_thread(void)
+  {
+    return (m_index_1 < thread_max);
+  }
+
+  inline void next_thread(void)
+  {
+    m_index_1++;
+    m_index_2= 0;
+  }
+
+  inline void reset(void)
+  {
+    m_index_1= 0;
+    m_index_2= 0;
+  }
+};
+
+/**
+  A row of PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS and
+  PERFORMANCE_SCHEMA.SESSION_ACCOUNT_CONNECT_ATTRS.
+*/
+struct row_session_connect_attrs
+{
+  /** Column PROCESS_ID. */
+  ulong m_process_id;
+  /** Column ATTR_NAME. In UTF-8 */
+  char m_attr_name[32 * 3];
+  /** Length in bytes of @c m_attr_name. */
+  uint m_attr_name_length;
+  /** Column ATTR_VALUE. In UTF-8 */
+  char m_attr_value[1024 * 3];
+  /** Length in bytes of @c m_attr_name. */
+  uint m_attr_value_length;
+  /** Column ORDINAL_POSITION. */
+  ulong m_ordinal_position;
+};
+
+
+/** Cursor CURSOR_BY_THREAD_CONNECT_ATTR. */
+class cursor_by_thread_connect_attr : public PFS_engine_table
+{
+public:
+  virtual int rnd_next();
+  virtual int rnd_pos(const void *pos);
+  virtual void reset_position(void);
+
+protected:
+  cursor_by_thread_connect_attr(const PFS_engine_table_share *share);
+  virtual int read_row_values(TABLE *table, unsigned char *buf,
+                              Field **fields, bool read_all);
+
+public:
+  ~cursor_by_thread_connect_attr()
+  {}
+
+protected:
+  void make_row(PFS_thread *thread, uint ordinal);
+  virtual bool thread_fits(PFS_thread *thread, PFS_thread *current_thread) = 0;
+
+private:
+  /** Current position. */
+  pos_connect_attr_by_thread_by_attr m_pos;
+  /** Next position. */
+  pos_connect_attr_by_thread_by_attr m_next_pos;
+
+protected:
+  /** Fields definition. */
+  static TABLE_FIELD_DEF m_field_def;
+  /** Current row. */
+  row_session_connect_attrs m_row;
+  /** True if the current row exists. */
+  bool m_row_exists;
+};
+
+/** @} */
+#endif

=== modified file 'storage/perfschema/pfs.cc'
--- a/storage/perfschema/pfs.cc	2012-03-19 19:29:28 +0000
+++ b/storage/perfschema/pfs.cc	2012-03-30 16:01:10 +0000
@@ -4873,6 +4873,37 @@ static void set_socket_thread_owner_v1(P
   pfs_socket->m_thread_owner= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS);
 }
 
+
+/**
+  Implementation of the thread attribute connection interface
+  @sa PSI_v1::thread_set_connect_attr.
+*/
+static int thread_set_connect_attrs_v1(const char *buffer, uint length,
+                                       const void *from_cs)
+{
+
+  PFS_thread *thd= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS);
+
+  DBUG_ASSERT(buffer != NULL);
+
+  if (likely(thd != NULL))
+  {
+    /* copy from the input buffer as much as we can fit */
+    uint copy_size= length < (uint) sizeof(thd->m_connect_attrs) ?
+      length : (uint) sizeof(thd->m_connect_attrs);
+
+    thd->m_lock.allocated_to_dirty();
+    memcpy(thd->m_connect_attrs, buffer, copy_size);
+    thd->m_connect_attrs_length= copy_size;
+    thd->m_connect_attrs_cs= (const CHARSET_INFO *) from_cs;
+    thd->m_lock.dirty_to_allocated();
+
+    return (copy_size == length ? 0 : 1);
+  }
+  return 0;
+}
+
+
 /**
   Implementation of the instrumentation interface.
   @sa PSI_v1.
@@ -4972,7 +5003,8 @@ PSI_v1 PFS_v1=
   set_socket_info_v1,
   set_socket_thread_owner_v1,
   pfs_digest_start_v1,
-  pfs_digest_add_token_v1
+  pfs_digest_add_token_v1,
+  thread_set_connect_attrs_v1,
 };
 
 static void* get_interface(int version)

=== modified file 'storage/perfschema/pfs_engine_table.cc'
--- a/storage/perfschema/pfs_engine_table.cc	2012-02-28 14:40:36 +0000
+++ b/storage/perfschema/pfs_engine_table.cc	2012-03-30 16:01:10 +0000
@@ -69,6 +69,8 @@
 #include "table_socket_instances.h"
 #include "table_socket_summary_by_instance.h"
 #include "table_socket_summary_by_event_name.h"
+#include "table_session_connect_attrs.h"
+#include "table_session_account_connect_attrs.h"
 
 /* For show status */
 #include "pfs_column_values.h"
@@ -143,6 +145,8 @@ static PFS_engine_table_share *all_share
   &table_socket_instances::m_share,
   &table_socket_summary_by_instance::m_share,
   &table_socket_summary_by_event_name::m_share,
+  &table_session_connect_attrs::m_share,
+  &table_session_account_connect_attrs::m_share,
   NULL
 };
 

=== modified file 'storage/perfschema/pfs_engine_table.h'
--- a/storage/perfschema/pfs_engine_table.h	2012-02-28 14:40:36 +0000
+++ b/storage/perfschema/pfs_engine_table.h	2012-03-30 16:01:10 +0000
@@ -263,7 +263,7 @@ public:
   ~PFS_readonly_acl()
   {}
 
-  ACL_internal_access_result check(ulong want_access, ulong *save_priv) const;
+  virtual ACL_internal_access_result check(ulong want_access, ulong *save_priv) const;
 };
 
 /** Singleton instance of PFS_readonly_acl. */

=== modified file 'storage/perfschema/pfs_instr.h'
--- a/storage/perfschema/pfs_instr.h	2012-03-19 19:29:28 +0000
+++ b/storage/perfschema/pfs_instr.h	2012-03-30 16:01:10 +0000
@@ -517,6 +517,13 @@ struct PFS_thread : PFS_connection_slice
   PFS_host *m_host;
   PFS_user *m_user;
   PFS_account *m_account;
+
+  /** a buffer for the connection attributes */
+  char m_connect_attrs[8196];
+  /** length used by @c m_connect_attrs */
+  uint m_connect_attrs_length;
+  /** character set in which @c m_connect_attrs are encoded */
+  const CHARSET_INFO *m_connect_attrs_cs;
 };
 
 extern PFS_single_stat *global_instr_class_waits_array;

=== added file 'storage/perfschema/table_session_account_connect_attrs.cc'
--- a/storage/perfschema/table_session_account_connect_attrs.cc	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/table_session_account_connect_attrs.cc	2012-03-30 16:01:10 +0000
@@ -0,0 +1,72 @@
+/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "table_session_account_connect_attrs.h"
+
+THR_LOCK table_session_account_connect_attrs::m_table_lock;
+
+class PFS_readonly_world_acl : public PFS_readonly_acl
+{
+public:
+  PFS_readonly_world_acl()
+  {}
+
+  ~PFS_readonly_world_acl()
+  {}
+  virtual ACL_internal_access_result check(ulong want_access, ulong *save_priv) const
+  {
+    ACL_internal_access_result res= PFS_readonly_acl::check(want_access, save_priv);
+    if (res == ACL_INTERNAL_ACCESS_CHECK_GRANT)
+      res= ACL_INTERNAL_ACCESS_GRANTED;
+    return res;
+  }
+};
+
+PFS_readonly_world_acl pfs_readonly_world_acl;
+
+
+PFS_engine_table_share
+table_session_account_connect_attrs::m_share=
+{
+  { C_STRING_WITH_LEN("session_account_connect_attrs") },
+  &pfs_readonly_world_acl,
+  &table_session_account_connect_attrs::create,
+  NULL, /* write_row */
+  NULL, /* delete_all_rows */
+  NULL, /* get_row_count */
+  1000, /* records */
+  sizeof(pos_connect_attr_by_thread_by_attr), /* ref length */
+  &m_table_lock,
+  &m_field_def,
+  false /* checked */
+};
+
+
+PFS_engine_table* table_session_account_connect_attrs::create()
+{
+  return new table_session_account_connect_attrs();
+}
+
+
+table_session_account_connect_attrs::table_session_account_connect_attrs()
+  : cursor_by_thread_connect_attr(&m_share)
+{}
+
+
+bool
+table_session_account_connect_attrs::thread_fits(PFS_thread *thread,
+                                                 PFS_thread *current_thread){
+  return thread->m_account == current_thread->m_account;
+}

=== added file 'storage/perfschema/table_session_account_connect_attrs.h'
--- a/storage/perfschema/table_session_account_connect_attrs.h	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/table_session_account_connect_attrs.h	2012-03-30 16:01:10 +0000
@@ -0,0 +1,51 @@
+/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TABLE_SESSION_ACCOUNT_CONNECT_ATTRS_H
+#define TABLE_SESSION_ACCOUNT_CONNECT_ATTRS_H
+
+#include "cursor_by_thread_connect_attr.h"
+/**
+  \addtogroup Performance_schema_tables
+  @{
+*/
+
+/** Table PERFORMANCE_SCHEMA.SESSION_ACCOUNT_CONNECT_ATTRS. */
+class table_session_account_connect_attrs :
+  public cursor_by_thread_connect_attr
+{
+public:
+  /** Table share */
+  static PFS_engine_table_share m_share;
+  /** Table builder */
+  static PFS_engine_table* create();
+
+protected:
+  table_session_account_connect_attrs();
+
+public:
+  ~table_session_account_connect_attrs()
+  {}
+
+private:
+  bool thread_fits(PFS_thread *thread, PFS_thread *current_thread);
+
+private:
+  /** Table share lock. */
+  static THR_LOCK m_table_lock;
+};
+
+/** @} */
+#endif

=== added file 'storage/perfschema/table_session_connect_attrs.cc'
--- a/storage/perfschema/table_session_connect_attrs.cc	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/table_session_connect_attrs.cc	2012-03-30 16:01:10 +0000
@@ -0,0 +1,51 @@
+/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include "table_session_connect_attrs.h"
+
+THR_LOCK table_session_connect_attrs::m_table_lock;
+
+PFS_engine_table_share
+table_session_connect_attrs::m_share=
+{
+  { C_STRING_WITH_LEN("session_connect_attrs") },
+  &pfs_readonly_acl,
+  &table_session_connect_attrs::create,
+  NULL, /* write_row */
+  NULL, /* delete_all_rows */
+  NULL, /* get_row_count */
+  1000, /* records */
+  sizeof(pos_connect_attr_by_thread_by_attr), /* ref length */
+  &m_table_lock,
+  &m_field_def,
+  false /* checked */
+};
+
+
+PFS_engine_table* table_session_connect_attrs::create()
+{
+  return new table_session_connect_attrs();
+}
+
+
+table_session_connect_attrs::table_session_connect_attrs()
+  : cursor_by_thread_connect_attr(&m_share)
+{}
+
+bool
+table_session_connect_attrs::thread_fits(PFS_thread *thread,
+                                                 PFS_thread *current_thread){
+  return true;
+}

=== added file 'storage/perfschema/table_session_connect_attrs.h'
--- a/storage/perfschema/table_session_connect_attrs.h	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/table_session_connect_attrs.h	2012-03-30 16:01:10 +0000
@@ -0,0 +1,50 @@
+/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef TABLE_SESSION_CONNECT_ATTRS_H
+#define TABLE_SESSION_CONNECT_ATTRS_H
+
+#include "cursor_by_thread_connect_attr.h"
+/**
+  \addtogroup Performance_schema_tables
+  @{
+*/
+
+/** Table PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS. */
+class table_session_connect_attrs : public cursor_by_thread_connect_attr
+{
+public:
+  /** Table share */
+  static PFS_engine_table_share m_share;
+  /** Table builder */
+  static PFS_engine_table* create();
+
+protected:
+  table_session_connect_attrs();
+
+public:
+  ~table_session_connect_attrs()
+  {}
+
+private:
+  bool thread_fits(PFS_thread *thread, PFS_thread *current_thread);
+
+private:
+  /** Table share lock. */
+  static THR_LOCK m_table_lock;
+};
+
+/** @} */
+#endif

=== modified file 'storage/perfschema/unittest/CMakeLists.txt'
--- a/storage/perfschema/unittest/CMakeLists.txt	2011-07-29 09:10:56 +0000
+++ b/storage/perfschema/unittest/CMakeLists.txt	2012-03-30 16:01:10 +0000
@@ -43,3 +43,8 @@ SET(tests
 FOREACH(testname ${tests})
   PFS_ADD_TEST(${testname})
 ENDFOREACH()
+
+# we need the server libs to test the blob parser
+ADD_EXECUTABLE(pfs_connect_attr-t pfs_connect_attr-t.cc)
+TARGET_LINK_LIBRARIES(pfs_connect_attr-t mytap perfschema sql binlog rpl master slave sql mysys ${SSL_LIBRARIES})
+ADD_TEST(pfs_connect_attr pfs_connect_attr-t)

=== added file 'storage/perfschema/unittest/pfs_connect_attr-t.cc'
--- a/storage/perfschema/unittest/pfs_connect_attr-t.cc	1970-01-01 00:00:00 +0000
+++ b/storage/perfschema/unittest/pfs_connect_attr-t.cc	2012-03-30 16:01:10 +0000
@@ -0,0 +1,335 @@
+/* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+
+  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
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software Foundation,
+  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include <my_pthread.h>
+#include <pfs_server.h>
+#include <pfs_instr_class.h>
+#include <pfs_instr.h>
+#include <pfs_global.h>
+#include <tap.h>
+
+
+#include <string.h>
+#include <memory.h>
+
+/* test helpers, to inspect data */
+bool read_nth_attr(const char *connect_attrs, uint connect_attrs_length,
+                          const CHARSET_INFO *connect_attrs_cs,
+                          uint ordinal,
+                          char *attr_name, uint max_attr_name,
+                          uint *attr_name_length,
+                          char *attr_value, uint max_attr_value,
+                          uint *attr_value_length);
+
+void test_blob_parser()
+{
+  char name[100], value[4096];
+  unsigned char packet[8192], *ptr;
+  uint name_len, value_len, idx, packet_length;
+  bool result;
+  const CHARSET_INFO *cs= &my_charset_utf8_bin;
+
+  diag("test_blob_parser");
+
+  result= read_nth_attr("", 0, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "zero length blob");
+
+
+  result= read_nth_attr("\x1", 1, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "invalid key length");
+
+
+  result= read_nth_attr("\x2k1\x1", 4, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "invalid value length");
+
+
+  result= read_nth_attr("\x2k1\x2v1", 6, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "one pair return");
+  ok(name_len == 2, "one pair attr name length");
+  ok(!strncmp(name, "k1", name_len), "one pair attr name");
+  ok(value_len == 2, "one pair value length");
+  ok(!strncmp(value, "v1", value_len), "one pair value");
+
+  result= read_nth_attr("\x2k1\x2v1", 6, cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "no second arg");
+
+  result= read_nth_attr("\x2k1\x2v1\x2k2\x2v2", 12, cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "two pairs return");
+  ok(name_len == 2, "two pairs attr name length");
+  ok(!strncmp(name, "k2", name_len), "two pairs attr name");
+  ok(value_len == 2, "two pairs value length");
+  ok(!strncmp(value, "v2", value_len), "two pairs value");
+
+  result= read_nth_attr("\x2k1\xff\x2k2\x2v2", 12, cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "two pairs first value bad return");
+
+  result= read_nth_attr("\x2k1\x2v1\x2k2\x2v2", 10, cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == false, "two pairs wrong global length");
+
+  result= read_nth_attr("\x21z123456789z123456789z123456789z12\x2v1", 37, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "attr name overflow");
+  ok(name_len == 32, "attr name overflow length");
+  ok(!strncmp(name, "z123456789z123456789z123456789z1", name_len),
+     "attr name overflow name");
+  ok(value_len == 2, "attr name overflow value length");
+  ok(!strncmp(value, "v1", value_len), "attr name overflow value");
+
+  packet[0]= 2;
+  packet[1]= 'k';
+  packet[2]= '1';
+  ptr= net_store_length(packet + 3, 1025);
+  for (idx= 0; idx < 1025; idx++)
+    *ptr++= '0' + (idx % 10);
+  packet_length= (uint) (ptr - packet);
+  result= read_nth_attr((char *) packet, packet_length, cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "attr value overflow");
+  ok(name_len == 2, "attr value overflow length");
+  ok(!strncmp(name, "k1", name_len), "attr value overflow name");
+  ok(value_len == 1024, "attr value overflow value length");
+  for (idx= 0; idx < 1024; idx++)
+  {
+    if (value[idx] != (char) ('0' + (idx % 10)))
+      break;
+  }
+  ok (idx == 1024, "attr value overflow value");
+
+  result= read_nth_attr("\x21z123456789z123456789z123456789z12\x2v1\x2k2\x2v2",
+                        43, cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "prev attr name overflow");
+  ok(name_len == 2, "prev attr name overflow length");
+  ok(!strncmp(name, "k2", name_len),
+     "prev attr name overflow name");
+  ok(value_len == 2, "prev attr name overflow value length");
+  ok(!strncmp(value, "v2", value_len), "prev attr name overflow value");
+
+
+  packet[1]= 'k';
+  packet[2]= '1';
+  packet[3]= 2;
+  packet[4]= 'v';
+  packet[5]= '1';
+
+  for(idx= 251; idx < 256; idx++)
+  {
+    packet[0]= idx;
+    result= read_nth_attr((char *) packet, 6, cs, 0,
+                          name, 32, &name_len, value, 1024, &value_len);
+    ok(result == false, "invalid string length %d", idx);
+  }
+}
+
+void test_multibyte_lengths()
+{
+  char name[100], value[4096];
+  uint name_len, value_len;
+  bool result;
+  const CHARSET_INFO *cs= &my_charset_utf8_bin;
+
+  unsigned char var_len_packet[] = {
+    252, 2, 0, 'k', '1',
+    253, 2, 0, 0, 'v', '1',
+    254, 2, 0, 0, 0, 0, 0, 0, 0, 'k', '2',
+    254, 2, 0, 0, 0, 0, 0, 0, 0, 'v', '2'
+  };
+
+  result= read_nth_attr((char *) var_len_packet, sizeof(var_len_packet), cs, 0,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "multibyte lengths return");
+  ok(name_len == 2, "multibyte lengths name length");
+  ok(!strncmp(name, "k1", name_len), "multibyte lengths attr name");
+  ok(value_len == 2, "multibyte lengths value length");
+  ok(!strncmp(value, "v1", value_len), "multibyte lengths value");
+
+  result= read_nth_attr((char *) var_len_packet, sizeof(var_len_packet), cs, 1,
+                        name, 32, &name_len, value, 1024, &value_len);
+  ok(result == true, "multibyte lengths second attr return");
+  ok(name_len == 2, "multibyte lengths second attr name length");
+  ok(!strncmp(name, "k2", name_len), "multibyte lengths second attr attr name");
+  ok(value_len == 2, "multibyte lengths value length");
+  ok(!strncmp(value, "v2", value_len), "multibyte lengths second attr value");
+}
+
+
+void test_utf8_parser()
+{
+  /* utf8 max byte length per character is 3*/
+  char name[33 * 3], value[1024 * 3], packet[1500 * 3], *ptr;
+  uint name_len, value_len;
+  bool result;
+  const CHARSET_INFO *cs= &my_charset_utf8_bin;
+
+  /* note : this is encoded in utf-8 */
+  const char *attr1= "���������������";
+  const char *attr2= "�������������������";
+
+  ptr= packet;
+  *ptr++= strlen(attr1);
+  memcpy(ptr, attr1, strlen(attr1));
+  ptr+= strlen(attr1);
+  *ptr++= strlen(val1);
+  memcpy(ptr, val1, strlen(val1));
+  ptr+= strlen(val1);
+
+  *ptr++= strlen(attr2);
+  memcpy(ptr, attr2, strlen(attr2));
+  ptr+= strlen(attr2);
+  *ptr++= strlen(val2);
+  memcpy(ptr, val2, strlen(val2));
+  ptr+= strlen(val2);
+
+  diag("test_utf8_parser attr pair #1");
+
+  result= read_nth_attr((char *) packet, ptr - packet, cs, 0,
+                        name, sizeof(name), &name_len,
+                        value, sizeof(value), &value_len);
+  ok(result == true, "return");
+  ok(name_len == strlen(attr1), "name length");
+  ok(!strncmp(name, attr1, name_len), "attr name");
+  ok(value_len == strlen(val1), "value length");
+  ok(!strncmp(value, val1, value_len), "value");
+
+  diag("test_utf8_parser attr pair #2");
+  result= read_nth_attr((char *) packet, ptr - packet, cs, 1,
+                        name, sizeof(name), &name_len,
+                        value, sizeof(value), &value_len);
+  ok(result == true, "return");
+  ok(name_len == strlen(attr2), "name length");
+  ok(!strncmp(name, attr2, name_len), "attr name");
+  ok(value_len == strlen(val2), "value length");
+  ok(!strncmp(value, val2, value_len), "value");
+}
+
+
+void test_utf8_parser_bad_encoding()
+{
+  /* utf8 max byte length per character is 3*/
+  char name[33 * 3], value[1024 * 3], packet[1500 * 3], *ptr;
+  uint name_len, value_len;
+  bool result;
+  const CHARSET_INFO *cs= &my_charset_utf8_bin;
+
+  /* note : this is encoded in utf-8 */
+  const char *attr= "�et;
+  *ptr++= strlen(attr);
+  memcpy(ptr, attr, strlen(attr));
+  ptr[0]= 0xFA; // invalid UTF-8 char
+  ptr+= strlen(attr);
+  *ptr++= strlen(val);
+  memcpy(ptr, val, strlen(val));
+  ptr+= strlen(val);
+
+  diag("test_utf8_parser_bad_encoding");
+
+  result= read_nth_attr((char *) packet, ptr - packet, cs, 0,
+                        name, sizeof(name), &name_len,
+                        value, sizeof(value), &value_len);
+  ok(result == false, "return");
+}
+
+const CHARSET_INFO *cs_cp1251;
+
+void test_cp1251_parser()
+{
+  /* utf8 max byte length per character is 3*/
+  char name[33 * 3], value[1024 * 3], packet[1500 * 3], *ptr;
+  uint name_len, value_len;
+  bool result;
+
+  /* note : this is ������������ in windows-1251 */
+  const char *attr1= "\xc3\xe5\xee\xf0\xe3\xe8";
+  /* note : this is ����char *val1= "\xca\xee\xe4\xe8\xed\xee\xe2";
+  /* note : this is �������������� in windows-1251 */
+  const char *attr2= "\xcf\xeb\xee\xe2\xe4\xe8\xe2";
+  /* note : this is ��������val2= "\xc1\xfa\xeb\xe3\xe0\xf0\xe8\xff";
+
+  ptr= packet;
+  *ptr++= strlen(attr1);
+  memcpy(ptr, attr1, strlen(attr1));
+  ptr+= strlen(attr1);
+  *ptr++= strlen(val1);
+  memcpy(ptr, val1, strlen(val1));
+  ptr+= strlen(val1);
+
+  *ptr++= strlen(attr2);
+  memcpy(ptr, attr2, strlen(attr2));
+  ptr+= strlen(attr2);
+  *ptr++= strlen(val2);
+  memcpy(ptr, val2, strlen(val2));
+  ptr+= strlen(val2);
+
+  diag("test_cp1251_parser attr pair #1");
+
+  result= read_nth_attr((char *) packet, ptr - packet, cs_cp1251, 0,
+                        name, sizeof(name), &name_len,
+                        value, sizeof(value), &value_len);
+  ok(result == true, "return");
+  /* need to compare to the UTF-8 equivalents */
+  ok(name_len == strlen("������������"), "name length");
+  ok(!strncmp(name, "���������rlen("��������������"), "value length");
+  ok(!strncmp(value, "�������est_cp1251_parser attr pair #2");
+  result= read_nth_attr((char *) packet, ptr - packet, cs_cp1251, 1,
+                        name, sizeof(name), &name_len,
+                        value, sizeof(value), &value_len);
+  ok(result == true, "return");
+  /* need to compare to the UTF-8 equivalents */
+  ok(name_len == strlen("����trncmp(name, "��������������", name_len), "attr name");
+  ok(value_len == strlen("����������������"), "value length");
+  ok(!strncmp(value, "��������oid do_all_tests()
+{
+  test_blob_parser();
+  test_multibyte_lengths();
+  test_utf8_parser();
+  test_utf8_parser_bad_encoding();
+  test_cp1251_parser();
+}
+
+int main(int, char **)
+{
+  MY_INIT("pfs-t");
+
+  cs_cp1251= get_charset_by_csname("cp1251", MY_CS_PRIMARY, MYF(0));
+  if (!cs_cp1251)
+    diag("skipping the cp1251 tests : missing character set");
+  plan(57 + (cs_cp1251 ? 10 : 0));
+  do_all_tests();
+  return 0;
+}

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2012-03-06 14:29:42 +0000
+++ b/tests/mysql_client_test.c	2012-03-30 16:01:10 +0000
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) l_mysql, 2011, Oracle and/or its affiliates. All rights reserved.
 
    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
@@ -116,7 +116,7 @@ if (!opt_silent) \
   fprintf(stdout, "  \n#####################################\n"); \
 }
 
-static void print_error(const char *msg);
+static void print_error(MYSQL * l_mysql, const char *msg);
 static void print_st_error(MYSQL_STMT *stmt, const char *msg);
 static void client_disconnect(MYSQL* mysql, my_bool drop_db);
 static void get_options(int *argc, char ***argv);
@@ -149,7 +149,8 @@ static void die(const char *file, int li
 }
 
 
-#define myerror(msg) print_error(msg)
+#define myerror(msg) print_error(mysql,msg)
+#define myerror2(l_mysql, msg) print_error(l_mysql,msg)
 #define mysterror(stmt, msg) print_st_error(stmt, msg)
 
 #define myquery(RES) \
@@ -160,6 +161,14 @@ static void die(const char *file, int li
   DIE_UNLESS(r == 0); \
 }
 
+#define myquery2(L_MYSQL,RES) \
+{ \
+  int r= (RES);                                \
+  if (r) \
+    myerror2(L_MYSQL,NULL); \
+  DIE_UNLESS(r == 0); \
+}
+
 #define myquery_r(r) \
 { \
 if (r) \
@@ -209,17 +218,17 @@ static int cmp_double(double *a, double 
 
 /* Print the error message */
 
-static void print_error(const char *msg)
+static void print_error(MYSQL *l_mysql, const char *msg)
 {
   if (!opt_silent)
   {
-    if (mysql && mysql_errno(mysql))
+    if (l_mysql && mysql_errno(l_mysql))
     {
       if (mysql->server_version)
         fprintf(stdout, "\n [MySQL-%s]", mysql->server_version);
       else
         fprintf(stdout, "\n [MySQL]");
-      fprintf(stdout, "[%d] %s\n", mysql_errno(mysql), mysql_error(mysql));
+      fprintf(stdout, "[%d] %s\n", mysql_errno(l_mysql), mysql_error(l_mysql));
     }
     else if (msg)
       fprintf(stderr, " [MySQL] %s\n", msg);
@@ -20165,6 +20174,114 @@ static void test_wl5968()
 }
 
 
+/*
+  WL#5924: Add connect string processing to mysql
+*/
+static void test_wl5924()
+{
+  int rc;
+  MYSQL *l_mysql;
+  MYSQL_RES *res;
+  MYSQL_ROW row;
+
+  myheader("test_wl5924");
+  l_mysql= mysql_client_init(NULL);
+  DIE_UNLESS(l_mysql != NULL);
+
+  /* we want a non-default character set */
+  rc= mysql_set_character_set(l_mysql, "cp1251");
+  DIE_UNLESS(rc == 0);
+
+  /* put in an attr */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key1", "value1");
+  DIE_UNLESS(rc == 0);
+
+  /* put a second attr */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key2", "value2");
+  DIE_UNLESS(rc == 0);
+
+  /* put the second attr again : should fail */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key2", "value2");
+  DIE_UNLESS(rc != 0);
+
+  /* delete the second attr */
+  rc= mysql_options(l_mysql, MYSQL_OPT_CONNECT_ATTR_DELETE,
+                    "key2");
+  DIE_UNLESS(rc == 0);
+
+  /* put the second attr again : should pass */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key2", "value2");
+  DIE_UNLESS(rc == 0);
+
+  /* full reset */
+  rc= mysql_options(l_mysql, MYSQL_OPT_CONNECT_ATTR_RESET, NULL);
+  DIE_UNLESS(rc == 0);
+
+  /* put the second attr again : should pass */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key2", "value2");
+  DIE_UNLESS(rc == 0);
+
+  /* full reset */
+  rc= mysql_options(l_mysql, MYSQL_OPT_CONNECT_ATTR_RESET, NULL);
+  DIE_UNLESS(rc == 0);
+
+  /* add a third attr */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key3", "value3");
+  DIE_UNLESS(rc == 0);
+
+  /* add a fourth attr */
+  rc= mysql_options4(l_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+                     "key4", "value4");
+  DIE_UNLESS(rc == 0);
+
+  /* add a non-ascii attr */
+  /* note : this is ������SQL_OPT_CONNECT_ATTR_ADD,
+                     "\xc3\xe5\xee\xf0\xe3\xe8",
+                     "\xca\xee\xe4\xe8\xed\xee\xe2");
+  DIE_UNLESS(rc == 0);
+
+  l_mysql= mysql_real_connect(l_mysql, opt_host, opt_user,
+                         opt_password, current_db, opt_port,
+                         opt_unix_socket, 0);
+  DIE_UNLESS(l_mysql != 0);
+
+  rc= mysql_query(l_mysql,
+                  "SELECT ATTR_NAME, ATTR_VALUE "
+                  " FROM PERFORMANCE_SCHEMA.session_account_connect_attrs"
+                  " WHERE ATTR_NAME IN ('key1','key2','key3','key4',"
+                  "  '\xc3\xe5\xee\xf0\xe3\xe8') AND"
+                  "  PROCESS_ID = CONNECTION_ID() ORDER BY ATTR_NAME");
+  myquery2(l_mysql,rc);
+  res= mysql_use_result(l_mysql);
+  DIE_UNLESS(res);
+
+  row= mysql_fetch_row(res);
+  DIE_UNLESS(row);
+  DIE_UNLESS(0 == strcmp(row[0], "key3"));
+  DIE_UNLESS(0 == strcmp(row[1], "value3"));
+
+  row= mysql_fetch_row(res);
+  DIE_UNLESS(row);
+  DIE_UNLESS(0 == strcmp(row[0], "key4"));
+  DIE_UNLESS(0 == strcmp(row[1], "value4"));
+
+  row= mysql_fetch_row(res);
+  DIE_UNLESS(row);
+  DIE_UNLESS(0 == strcmp(row[0], "\xc3\xe5\xee\xf0\xe3\xe8"));
+  DIE_UNLESS(0 == strcmp(row[1], "\xca\xee\xe4\xe8\xed\xee\xe2"));
+
+  mysql_free_result(res);
+  mysql_close(l_mysql);
+}
+
+
 static struct my_option client_test_long_options[] =
 {
   {"basedir", 'b', "Basedir for tests.", &opt_basedir,
@@ -20512,6 +20629,7 @@ static struct my_tests_st my_tests[]= {
   { "test_bug11754979", test_bug11754979 },
   { "test_bug13001491", test_bug13001491 },
   { "test_wl5968", test_wl5968 },
+  { "test_wl5924", test_wl5924 },
   { 0, 0 }
 };
 

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (Georgi.Kodinov:3826 to 3827) WL#5924Georgi Kodinov31 Mar