List:Commits« Previous MessageNext Message »
From:Jon Olav Hauglid Date:October 6 2010 9:33am
Subject:bzr push into mysql-5.5-runtime branch (jon.hauglid:3154 to 3155)
View as plain text  
 3155 Jon Olav Hauglid	2010-10-06 [merge]
      Merge from mysql-5.5-bugteam to mysql-5.5-runtime.

    added:
      include/mysql/client_plugin.h
      include/mysql/client_plugin.h.pp
      include/mysql/plugin_auth.h
      include/mysql/plugin_auth.h.pp
      include/mysql/plugin_auth_common.h
      mysql-test/include/have_plugin_auth.inc
      mysql-test/r/plugin_auth.result
      mysql-test/suite/sys_vars/r/external_user_basic.result
      mysql-test/suite/sys_vars/r/proxy_user_basic.result
      mysql-test/suite/sys_vars/t/external_user_basic.test
      mysql-test/suite/sys_vars/t/proxy_user_basic.test
      mysql-test/t/plugin_auth-master.opt
      mysql-test/t/plugin_auth.test
      plugin/auth/
      plugin/auth/CMakeLists.txt
      plugin/auth/Makefile.am
      plugin/auth/auth_socket.c
      plugin/auth/dialog.c
      plugin/auth/plug.in
      plugin/auth/test_plugin.c
      sql-common/client_plugin.c
    modified:
      Makefile.am
      client/CMakeLists.txt
      client/client_priv.h
      client/mysql.cc
      client/mysqltest.cc
      cmake/os/WindowsCache.cmake
      config.h.cmake
      configure.in
      include/Makefile.am
      include/errmsg.h
      include/my_global.h
      include/my_no_pthread.h
      include/my_sys.h
      include/mysql.h
      include/mysql.h.pp
      include/mysql/plugin.h
      include/mysql_com.h
      include/sha1.h
      include/sql_common.h
      libmysql/CMakeLists.txt
      libmysql/Makefile.shared
      libmysql/client_settings.h
      libmysql/errmsg.c
      libmysql/libmysql.c
      libmysql/libmysql.def
      libmysqld/CMakeLists.txt
      libmysqld/Makefile.am
      libmysqld/embedded_priv.h
      libmysqld/lib_sql.cc
      libmysqld/libmysqld.c
      mysql-test/collections/default.daily
      mysql-test/lib/mtr_cases.pm
      mysql-test/mysql-test-run.pl
      mysql-test/r/1st.result
      mysql-test/r/change_user.result
      mysql-test/r/connect.result
      mysql-test/r/events_bugs.result
      mysql-test/r/grant.result
      mysql-test/r/grant2.result
      mysql-test/r/grant_cache_no_prot.result
      mysql-test/r/grant_cache_ps_prot.result
      mysql-test/r/information_schema.result
      mysql-test/r/log_tables_upgrade.result
      mysql-test/r/mysql_upgrade.result
      mysql-test/r/mysql_upgrade_ssl.result
      mysql-test/r/mysqlcheck.result
      mysql-test/r/mysqld--help-win.result
      mysql-test/r/ps.result
      mysql-test/r/sp_notembedded.result
      mysql-test/r/system_mysql_db.result
      mysql-test/suite/federated/federated.result
      mysql-test/suite/funcs_1/r/innodb_trig_03e.result
      mysql-test/suite/funcs_1/r/is_columns_mysql.result
      mysql-test/suite/funcs_1/r/is_key_column_usage.result
      mysql-test/suite/funcs_1/r/is_statistics.result
      mysql-test/suite/funcs_1/r/is_statistics_mysql.result
      mysql-test/suite/funcs_1/r/is_table_constraints.result
      mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
      mysql-test/suite/funcs_1/r/is_tables_mysql.result
      mysql-test/suite/funcs_1/r/is_user_privileges.result
      mysql-test/suite/funcs_1/r/memory_trig_03e.result
      mysql-test/suite/funcs_1/r/myisam_trig_03e.result
      mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
      mysql-test/suite/funcs_1/r/processlist_priv_ps.result
      mysql-test/suite/funcs_1/t/is_user_privileges.test
      mysql-test/suite/perfschema/r/column_privilege.result
      mysql-test/suite/perfschema/r/pfs_upgrade.result
      mysql-test/suite/perfschema/r/privilege.result
      mysql-test/suite/rpl/r/rpl_do_grant.result
      mysql-test/suite/rpl/r/rpl_ignore_table.result
      mysql-test/suite/rpl/r/rpl_stm_000001.result
      mysql-test/t/change_user.test
      mysql-test/t/grant.test
      mysql-test/t/grant2.test
      mysql-test/t/system_mysql_db_fix40123.test
      mysql-test/t/system_mysql_db_fix50030.test
      mysql-test/t/system_mysql_db_fix50117.test
      mysys/sha1.c
      scripts/CMakeLists.txt
      scripts/mysql_system_tables.sql
      scripts/mysql_system_tables_data.sql
      scripts/mysql_system_tables_fix.sql
      sql-common/Makefile.am
      sql-common/client.c
      sql/CMakeLists.txt
      sql/Makefile.am
      sql/client_settings.h
      sql/ha_ndbcluster.cc
      sql/ha_ndbcluster_binlog.cc
      sql/lex.h
      sql/mysqld.cc
      sql/password.c
      sql/protocol.cc
      sql/protocol.h
      sql/set_var.cc
      sql/share/errmsg-utf8.txt
      sql/sp_head.h
      sql/sql_acl.cc
      sql/sql_acl.h
      sql/sql_audit.h
      sql/sql_builtin.cc.in
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_connect.cc
      sql/sql_connect.h
      sql/sql_insert.cc
      sql/sql_lex.cc
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_plugin.cc
      sql/sql_yacc.yy
      sql/structs.h
      sql/sys_vars.cc
      sql/sys_vars.h
      sql/table.cc
      tests/mysql_client_test.c
      unittest/mysys/CMakeLists.txt
 3154 Magne Mahre	2010-10-06
      Bug#56452 Assertion failed: thd->transaction.stmt.is_empty() || 
                thd->in_sub_stmt
            
      In a precursor patch for Bug#52044 
      (revid:bzr/kostja@stripped), a
      number of reorganizations of code was made. In addition some
      assertions were added to ensure the correct transactional state.
            
      The reorganization had a small glitch so statements that was
      active in the query cache was not followed by a
      statement commit/rollback (this code was removed). A section
      in the trans_commit_stmt/trans_rollback_stmt code is to
      clear the thd->transaction.stmt list of affected storage
      engines.  When a new statement is initiated, an assert
      introduced by the 523044 patch checks if this list is cleared.
      When the query cache is accessed, this list may be populated,
      and since it's not committed it will not be cleared.
            
      This fix adds explicit statement commit or rollback for
      statements that is contained in the query cache.

    modified:
      mysql-test/r/cache_innodb.result
      mysql-test/t/cache_innodb.test
      sql/sql_cache.cc
=== modified file 'Makefile.am'
--- a/Makefile.am	2010-10-04 11:52:59 +0000
+++ b/Makefile.am	2010-10-04 12:42:16 +0000
@@ -268,7 +268,9 @@ API_PREPROCESSOR_HEADER = $(top_srcdir)/
 			$(top_srcdir)/include/mysql/plugin_ftparser.h \
 			$(top_srcdir)/include/mysql.h \
 			$(top_srcdir)/include/mysql/psi/psi_abi_v1.h \
-			$(top_srcdir)/include/mysql/psi/psi_abi_v2.h
+			$(top_srcdir)/include/mysql/psi/psi_abi_v2.h \
+                        $(top_srcdir)/include/mysql/client_plugin.h \
+                        $(top_srcdir)/include/mysql/plugin_auth.h
 
 #
 # Rules for checking that the abi/api has not changed.

=== modified file 'client/CMakeLists.txt'
--- a/client/CMakeLists.txt	2010-09-06 11:26:23 +0000
+++ b/client/CMakeLists.txt	2010-09-20 14:17:32 +0000
@@ -72,3 +72,5 @@ ENDIF(WIN32)
 SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap 
 PROPERTIES HAS_CXX TRUE)
 
+ADD_DEFINITIONS(-DHAVE_DLOPEN)
+

=== modified file 'client/client_priv.h'
--- a/client/client_priv.h	2010-07-15 11:13:30 +0000
+++ b/client/client_priv.h	2010-08-09 08:32:50 +0000
@@ -84,6 +84,8 @@ enum options_client
   OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE,
   OPT_WRITE_BINLOG, OPT_DUMP_DATE,
   OPT_INIT_COMMAND,
+  OPT_PLUGIN_DIR,
+  OPT_DEFAULT_PLUGIN,
   OPT_MAX_CLIENT_OPTION
 };
 

=== modified file 'client/mysql.cc'
--- a/client/mysql.cc	2010-08-19 11:35:47 +0000
+++ b/client/mysql.cc	2010-09-20 14:17:32 +0000
@@ -166,6 +166,7 @@ static int wait_time = 5;
 static STATUS status;
 static ulong select_limit,max_join_size,opt_connect_timeout=0;
 static char mysql_charsets_dir[FN_REFLEN+1];
+static char *opt_plugin_dir= 0, *opt_default_auth;
 static const char *xmlmeta[] = {
   "&", "&",
   "<", "&lt;",
@@ -1564,6 +1565,13 @@ static struct my_option my_long_options[
   {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
     &show_warnings, &show_warnings, 0, GET_BOOL, NO_ARG,
     0, 0, 0, 0, 0, 0},
+  {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+   (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0,
+   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"default_auth", OPT_PLUGIN_DIR,
+    "Default authentication client-side plugin to use.",
+   (uchar**) &opt_default_auth, (uchar**) &opt_default_auth, 0,
+   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 };
 
@@ -4295,9 +4303,15 @@ sql_real_connect(char *host,char *databa
 
   mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
   
+  if (opt_plugin_dir && *opt_plugin_dir)
+    mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+  if (opt_default_auth && *opt_default_auth)
+    mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
+
   if (!mysql_real_connect(&mysql, host, user, password,
-			  database, opt_mysql_port, opt_mysql_unix_port,
-			  connect_flag | CLIENT_MULTI_STATEMENTS))
+                          database, opt_mysql_port, opt_mysql_unix_port,
+                          connect_flag | CLIENT_MULTI_STATEMENTS))
   {
     if (!silent ||
 	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&

=== modified file 'client/mysqltest.cc'
--- a/client/mysqltest.cc	2010-10-04 10:08:36 +0000
+++ b/client/mysqltest.cc	2010-10-04 12:42:16 +0000
@@ -36,6 +36,7 @@
 #include "client_priv.h"
 #include <mysql_version.h>
 #include <mysqld_error.h>
+#include <sql_common.h>
 #include <m_ctype.h>
 #include <my_dir.h>
 #include <hash.h>
@@ -191,6 +192,8 @@ static ulonglong timer_now(void);
 
 static ulong connection_retry_sleep= 100000; /* Microseconds */
 
+static char *opt_plugin_dir= 0;
+
 /* Precompiled re's */
 static my_regex_t ps_re;     /* the query can be run using PS protocol */
 static my_regex_t sp_re;     /* the query can be run as a SP */
@@ -3897,13 +3900,15 @@ void do_change_user(struct st_command *c
   }
 
   if (!ds_user.length)
+  {
     dynstr_set(&ds_user, mysql->user);
 
-  if (!ds_passwd.length)
-    dynstr_set(&ds_passwd, mysql->passwd);
+    if (!ds_passwd.length)
+      dynstr_set(&ds_passwd, mysql->passwd);
 
-  if (!ds_db.length)
-    dynstr_set(&ds_db, mysql->db);
+    if (!ds_db.length)
+      dynstr_set(&ds_db, mysql->db);
+  }
 
   DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
                       cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
@@ -5272,6 +5277,7 @@ void do_connect(struct st_command *comma
   static DYNAMIC_STRING ds_port;
   static DYNAMIC_STRING ds_sock;
   static DYNAMIC_STRING ds_options;
+  static DYNAMIC_STRING ds_default_auth;
 #ifdef HAVE_SMEM
   static DYNAMIC_STRING ds_shm;
 #endif
@@ -5283,7 +5289,8 @@ void do_connect(struct st_command *comma
     { "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
     { "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
     { "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
-    { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }
+    { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" },
+    { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" }
   };
 
   DBUG_ENTER("do_connect");
@@ -5433,6 +5440,12 @@ void do_connect(struct st_command *comma
   if (ds_database.length == 0)
     dynstr_set(&ds_database, opt_db);
 
+  if (opt_plugin_dir && *opt_plugin_dir)
+    mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+  if (ds_default_auth.length)
+    mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
+
   /* Special database to allow one to connect without a database name */
   if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
     dynstr_set(&ds_database, "");
@@ -6231,6 +6244,9 @@ static struct my_option my_long_options[
    "Number of seconds before connection timeout.",
    &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG,
    120, 0, 3600 * 12, 0, 0, 0},
+  {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+   (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0,
+   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 };
 

=== modified file 'cmake/os/WindowsCache.cmake'
--- a/cmake/os/WindowsCache.cmake	2010-07-25 17:30:18 +0000
+++ b/cmake/os/WindowsCache.cmake	2010-10-04 19:25:53 +0000
@@ -131,6 +131,7 @@ SET(HAVE_PASE_ENVIRONMENT CACHE  INTERNA
 SET(HAVE_PATHS_H CACHE  INTERNAL "")
 SET(HAVE_PCLOSE CACHE  INTERNAL "")
 SET(HAVE_PERROR 1 CACHE  INTERNAL "")
+SET(HAVE_PEERCRED CACHE INTERNAL "")
 SET(HAVE_POLL_H CACHE  INTERNAL "")
 SET(HAVE_POPEN CACHE  INTERNAL "")
 SET(HAVE_POLL CACHE INTERNAL "")

=== modified file 'config.h.cmake'
--- a/config.h.cmake	2010-09-29 12:09:07 +0000
+++ b/config.h.cmake	2010-10-04 12:42:16 +0000
@@ -602,6 +602,8 @@
 #cmakedefine PLUGINDIR "@PLUGINDIR@"
 #cmakedefine DEFAULT_SYSCONFDIR "@DEFAULT_SYSCONFDIR@"
 
+#cmakedefine SO_EXT "@CMAKE_SHARED_MODULE_SUFFIX@"
+
 #define PACKAGE "mysql"
 #define PACKAGE_BUGREPORT ""
 #define PACKAGE_NAME "MySQL Server"

=== modified file 'configure.in'
--- a/configure.in	2010-10-04 11:52:59 +0000
+++ b/configure.in	2010-10-05 11:33:54 +0000
@@ -1617,9 +1617,8 @@ case "$with_mysqld_ldflags " in
     ;;
 
   *)
-    # Check for dlopen, needed for user definable functions
+    # Check for dlopen, needed for user definable functions and plugins
     # This must be checked after threads on AIX
-    # We only need this for mysqld, not for the clients.
 
     my_save_LIBS="$LIBS"
     LIBS=""

=== modified file 'include/Makefile.am'
--- a/include/Makefile.am	2010-09-29 14:26:32 +0000
+++ b/include/Makefile.am	2010-10-04 12:42:16 +0000
@@ -24,7 +24,9 @@ HEADERS_ABI =		mysql.h mysql_com.h mysql
 			my_list.h my_alloc.h typelib.h mysql/plugin.h \
 			mysql/plugin_audit.h mysql/plugin_ftparser.h
 pkginclude_HEADERS =	$(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \
-			my_xml.h mysql_embed.h mysql/services.h \
+			my_xml.h mysql_embed.h mysql/plugin_auth.h \
+                        mysql/client_plugin.h mysql/plugin_auth_common.h \
+                        mysql/services.h \
 			mysql/service_my_snprintf.h mysql/service_thd_alloc.h \
 			mysql/service_thread_scheduler.h \
 			mysql/service_thd_wait.h \
@@ -56,7 +58,8 @@ pkgpsiinclude_HEADERS = mysql/psi/psi.h 
 EXTRA_DIST =        mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base \
 			CMakeLists.txt \
 			mysql/psi/psi_abi_v1.h.pp \
-			mysql/psi/psi_abi_v2.h.pp
+			mysql/psi/psi_abi_v2.h.pp \
+                        mysql/plugin_auth.h.pp mysql/client_plugin.h.pp
 
 # Remove built files and the symlinked directories
 CLEANFILES =            $(BUILT_SOURCES) readline openssl probes_mysql.d probes_mysql_nodtrace.h

=== modified file 'include/errmsg.h'
--- a/include/errmsg.h	2009-10-28 07:55:44 +0000
+++ b/include/errmsg.h	2010-08-09 08:32:50 +0000
@@ -101,7 +101,8 @@ extern const char *client_errors[];	/* E
 #define CR_STMT_CLOSED				2056
 #define CR_NEW_STMT_METADATA                    2057
 #define CR_ALREADY_CONNECTED                    2058
-#define CR_ERROR_LAST  /*Copy last error nr:*/  2058
+#define CR_AUTH_PLUGIN_CANNOT_LOAD              2059
+#define CR_ERROR_LAST  /*Copy last error nr:*/  2059
 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */
 
 #endif /* ERRMSG_INCLUDED */

=== modified file 'include/my_global.h'
--- a/include/my_global.h	2010-10-04 11:52:59 +0000
+++ b/include/my_global.h	2010-10-04 23:14:16 +0000
@@ -452,6 +452,16 @@ extern "C" int madvise(void *addr, size_
 #define LINT_INIT(var)
 #endif
 
+#ifndef SO_EXT
+#ifdef _WIN32
+#define SO_EXT ".dll"
+#elif defined(__APPLE__)
+#define SO_EXT ".dylib"
+#else
+#define SO_EXT ".so"
+#endif
+#endif
+
 /*
    Suppress uninitialized variable warning without generating code.
 
@@ -1355,8 +1365,10 @@ do { doubleget_union _tmp; \
 #define dlsym(lib, name) (void*)GetProcAddress((HMODULE)lib, name)
 #define dlopen(libname, unused) LoadLibraryEx(libname, NULL, 0)
 #define dlclose(lib) FreeLibrary((HMODULE)lib)
+#ifndef HAVE_DLOPEN
 #define HAVE_DLOPEN
 #endif
+#endif
 
 #ifdef HAVE_DLOPEN
 #if defined(HAVE_DLFCN_H)
@@ -1365,7 +1377,11 @@ do { doubleget_union _tmp; \
 #endif
 
 #ifndef HAVE_DLERROR
+#ifdef _WIN32
 #define dlerror() ""
+#else
+#define dlerror() "No support for dynamic loading (static build?)"
+#endif
 #endif
 
 

=== modified file 'include/my_no_pthread.h'
--- a/include/my_no_pthread.h	2009-12-16 18:02:21 +0000
+++ b/include/my_no_pthread.h	2010-08-09 08:32:50 +0000
@@ -47,6 +47,7 @@
 #define rw_wrlock(A)
 #define rw_unlock(A)
 #define rwlock_destroy(A)
+#define safe_mutex_assert_owner(mp)
 
 #define mysql_mutex_init(A, B, C) do {} while (0)
 #define mysql_mutex_lock(A) do {} while (0)

=== modified file 'include/my_sys.h'
--- a/include/my_sys.h	2010-07-23 20:15:07 +0000
+++ b/include/my_sys.h	2010-08-09 08:32:50 +0000
@@ -197,7 +197,7 @@ extern void my_large_free(uchar *ptr);
 #define my_alloca(SZ) alloca((size_t) (SZ))
 #define my_afree(PTR) {}
 #else
-#define my_alloca(SZ) my_malloc(SZ,MYF(0))
+#define my_alloca(SZ) my_malloc(SZ,MYF(MY_FAE))
 #define my_afree(PTR) my_free(PTR)
 #endif /* HAVE_ALLOCA */
 
@@ -824,6 +824,10 @@ extern void set_prealloc_root(MEM_ROOT *
 extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
                                 size_t prealloc_size);
 extern char *strdup_root(MEM_ROOT *root,const char *str);
+static inline char *safe_strdup_root(MEM_ROOT *root, const char *str)
+{
+  return str ? strdup_root(root, str) : 0;
+}
 extern char *strmake_root(MEM_ROOT *root,const char *str,size_t len);
 extern void *memdup_root(MEM_ROOT *root,const void *str, size_t len);
 extern int get_defaults_options(int argc, char **argv,

=== modified file 'include/mysql.h'
--- a/include/mysql.h	2010-09-07 07:49:47 +0000
+++ b/include/mysql.h	2010-10-04 12:42:16 +0000
@@ -167,9 +167,15 @@ enum mysql_option 
   MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
   MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
   MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
-  MYSQL_OPT_SSL_VERIFY_SERVER_CERT
+  MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH
 };
 
+/**
+  @todo remove the "extension", move st_mysql_options completely
+  out of mysql.h
+*/
+struct st_mysql_options_extention; 
+
 struct st_mysql_options {
   unsigned int connect_timeout, read_timeout, write_timeout;
   unsigned int port, protocol;
@@ -203,7 +209,7 @@ struct st_mysql_options {
   void (*local_infile_end)(void *);
   int (*local_infile_error)(void *, char *, unsigned int);
   void *local_infile_userdata;
-  void *extension;
+  struct st_mysql_options_extention *extension;
 };
 
 enum mysql_status 
@@ -639,38 +645,6 @@ enum enum_stmt_attr_type
 };
 
 
-typedef struct st_mysql_methods
-{
-  my_bool (*read_query_result)(MYSQL *mysql);
-  my_bool (*advanced_command)(MYSQL *mysql,
-			      enum enum_server_command command,
-			      const unsigned char *header,
-			      unsigned long header_length,
-			      const unsigned char *arg,
-			      unsigned long arg_length,
-			      my_bool skip_check,
-                              MYSQL_STMT *stmt);
-  MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
-			   unsigned int fields);
-  MYSQL_RES * (*use_result)(MYSQL *mysql);
-  void (*fetch_lengths)(unsigned long *to, 
-			MYSQL_ROW column, unsigned int field_count);
-  void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
-#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
-  MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
-  my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
-  int (*stmt_execute)(MYSQL_STMT *stmt);
-  int (*read_binary_rows)(MYSQL_STMT *stmt);
-  int (*unbuffered_fetch)(MYSQL *mysql, char **row);
-  void (*free_embedded_thd)(MYSQL *mysql);
-  const char *(*read_statistics)(MYSQL *mysql);
-  my_bool (*next_result)(MYSQL *mysql);
-  int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
-  int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
-#endif
-} MYSQL_METHODS;
-
-
 MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql);
 int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
                                unsigned long length);
@@ -733,18 +707,6 @@ int		STDCALL mysql_drop_db(MYSQL *mysql,
 #endif
 #define HAVE_MYSQL_REAL_CONNECT
 
-/*
-  The following functions are mainly exported because of mysqlbinlog;
-  They are not for general usage
-*/
-
-#define simple_command(mysql, command, arg, length, skip_check) \
-  (*(mysql)->methods->advanced_command)(mysql, command, 0,  \
-                                        0, arg, length, skip_check, NULL)
-#define stmt_command(mysql, command, arg, length, stmt) \
-  (*(mysql)->methods->advanced_command)(mysql, command, 0,  \
-                                        0, arg, length, 1, stmt)
-
 #ifdef	__cplusplus
 }
 #endif

=== modified file 'include/mysql.h.pp'
--- a/include/mysql.h.pp	2010-09-07 07:49:47 +0000
+++ b/include/mysql.h.pp	2010-10-04 12:42:16 +0000
@@ -130,13 +130,13 @@ void create_random_string(char *to, unsi
 void hash_password(unsigned long *to, const char *password, unsigned int password_len);
 void make_scrambled_password_323(char *to, const char *password);
 void scramble_323(char *to, const char *message, const char *password);
-my_bool check_scramble_323(const char *, const char *message,
+my_bool check_scramble_323(const unsigned char *reply, const char *message,
                            unsigned long *salt);
 void get_salt_from_password_323(unsigned long *res, const char *password);
 void make_password_from_salt_323(char *to, const unsigned long *salt);
 void make_scrambled_password(char *to, const char *password);
 void scramble(char *to, const char *message, const char *password);
-my_bool check_scramble(const char *reply, const char *message,
+my_bool check_scramble(const unsigned char *reply, const char *message,
                        const unsigned char *hash_stage2);
 void get_salt_from_password(unsigned char *res, const char *password);
 void make_password_from_salt(char *to, const unsigned char *hash_stage2);
@@ -262,8 +262,9 @@ enum mysql_option
   MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
   MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
   MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
-  MYSQL_OPT_SSL_VERIFY_SERVER_CERT
+  MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH
 };
+struct st_mysql_options_extention;
 struct st_mysql_options {
   unsigned int connect_timeout, read_timeout, write_timeout;
   unsigned int port, protocol;
@@ -293,7 +294,7 @@ struct st_mysql_options {
   void (*local_infile_end)(void *);
   int (*local_infile_error)(void *, char *, unsigned int);
   void *local_infile_userdata;
-  void *extension;
+  struct st_mysql_options_extention *extension;
 };
 enum mysql_status
 {
@@ -548,34 +549,6 @@ enum enum_stmt_attr_type
   STMT_ATTR_CURSOR_TYPE,
   STMT_ATTR_PREFETCH_ROWS
 };
-typedef struct st_mysql_methods
-{
-  my_bool (*read_query_result)(MYSQL *mysql);
-  my_bool (*advanced_command)(MYSQL *mysql,
-         enum enum_server_command command,
-         const unsigned char *header,
-         unsigned long header_length,
-         const unsigned char *arg,
-         unsigned long arg_length,
-         my_bool skip_check,
-                              MYSQL_STMT *stmt);
-  MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
-      unsigned int fields);
-  MYSQL_RES * (*use_result)(MYSQL *mysql);
-  void (*fetch_lengths)(unsigned long *to,
-   MYSQL_ROW column, unsigned int field_count);
-  void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
-  MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
-  my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
-  int (*stmt_execute)(MYSQL_STMT *stmt);
-  int (*read_binary_rows)(MYSQL_STMT *stmt);
-  int (*unbuffered_fetch)(MYSQL *mysql, char **row);
-  void (*free_embedded_thd)(MYSQL *mysql);
-  const char *(*read_statistics)(MYSQL *mysql);
-  my_bool (*next_result)(MYSQL *mysql);
-  int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
-  int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
-} MYSQL_METHODS;
 MYSQL_STMT * mysql_stmt_init(MYSQL *mysql);
 int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
                                unsigned long length);

=== added file 'include/mysql/client_plugin.h'
--- a/include/mysql/client_plugin.h	1970-01-01 00:00:00 +0000
+++ b/include/mysql/client_plugin.h	2010-10-04 12:54:41 +0000
@@ -0,0 +1,164 @@
+#ifndef MYSQL_CLIENT_PLUGIN_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+   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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/**
+  @file
+
+  MySQL Client Plugin API
+
+  This file defines the API for plugins that work on the client side
+*/
+#define MYSQL_CLIENT_PLUGIN_INCLUDED
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+/* known plugin types */
+#define MYSQL_CLIENT_reserved1               0
+#define MYSQL_CLIENT_reserved2               1
+#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN   2
+
+#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION  0x0100
+
+#define MYSQL_CLIENT_MAX_PLUGINS             3
+
+#define mysql_declare_client_plugin(X)          \
+     MYSQL_PLUGIN_EXPORT struct st_mysql_client_plugin_ ## X        \
+        _mysql_client_plugin_declaration_ = {   \
+          MYSQL_CLIENT_ ## X ## _PLUGIN,        \
+          MYSQL_CLIENT_ ## X ## _PLUGIN_INTERFACE_VERSION,
+#define mysql_end_client_plugin             }
+
+/* generic plugin header structure */
+#define MYSQL_CLIENT_PLUGIN_HEADER                      \
+  int type;                                             \
+  unsigned int interface_version;                       \
+  const char *name;                                     \
+  const char *author;                                   \
+  const char *desc;                                     \
+  unsigned int version[3];                              \
+  const char *license;                                  \
+  void *mysql_api;                                      \
+  int (*init)(char *, size_t, int, va_list);            \
+  int (*deinit)();                                      \
+  int (*options)(const char *option, const void *);
+
+struct st_mysql_client_plugin
+{
+  MYSQL_CLIENT_PLUGIN_HEADER
+};
+
+struct st_mysql;
+
+/******** authentication plugin specific declarations *********/
+#include <mysql/plugin_auth_common.h>
+
+struct st_mysql_client_plugin_AUTHENTICATION
+{
+  MYSQL_CLIENT_PLUGIN_HEADER
+  int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
+};
+
+/******** using plugins ************/
+
+/**
+  loads a plugin and initializes it
+
+  @param mysql  MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+                and last_errno/last_error, for error reporting
+  @param name   a name of the plugin to load
+  @param type   type of plugin that should be loaded, -1 to disable type check
+  @param argc   number of arguments to pass to the plugin initialization
+                function
+  @param ...    arguments for the plugin initialization function
+
+  @retval
+  a pointer to the loaded plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_load_plugin(struct st_mysql *mysql, const char *name, int type,
+                  int argc, ...);
+
+/**
+  loads a plugin and initializes it, taking va_list as an argument
+
+  This is the same as mysql_load_plugin, but take va_list instead of
+  a list of arguments.
+
+  @param mysql  MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+                and last_errno/last_error, for error reporting
+  @param name   a name of the plugin to load
+  @param type   type of plugin that should be loaded, -1 to disable type check
+  @param argc   number of arguments to pass to the plugin initialization
+                function
+  @param args   arguments for the plugin initialization function
+
+  @retval
+  a pointer to the loaded plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type,
+                    int argc, va_list args);
+
+/**
+  finds an already loaded plugin by name, or loads it, if necessary
+
+  @param mysql  MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+                and last_errno/last_error, for error reporting
+  @param name   a name of the plugin to load
+  @param type   type of plugin that should be loaded
+
+  @retval
+  a pointer to the plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type);
+
+/**
+  adds a plugin structure to the list of loaded plugins
+
+  This is useful if an application has the necessary functionality
+  (for example, a special load data handler) statically linked into
+  the application binary. It can use this function to register the plugin
+  directly, avoiding the need to factor it out into a shared object.
+
+  @param mysql  MYSQL structure. It is only used for error reporting
+  @param plugin an st_mysql_client_plugin structure to register
+
+  @retval
+  a pointer to the plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(struct st_mysql *mysql,
+                             struct st_mysql_client_plugin *plugin);
+
+/**
+  set plugin options
+
+  Can be used to set extra options and affect behavior for a plugin.
+  This function may be called multiple times to set several options
+
+  @param plugin an st_mysql_client_plugin structure
+  @param option a string which specifies the option to set
+  @param value  value for the option.
+
+  @retval 0 on success, 1 in case of failure
+**/
+int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin,
+                                 const char *option,
+                                 const void *value);
+#endif
+

=== added file 'include/mysql/client_plugin.h.pp'
--- a/include/mysql/client_plugin.h.pp	1970-01-01 00:00:00 +0000
+++ b/include/mysql/client_plugin.h.pp	2010-10-05 12:26:49 +0000
@@ -0,0 +1,42 @@
+#include <stdarg.h>
+#include <stdlib.h>
+struct st_mysql_client_plugin
+{
+  int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; const char *license; void *mysql_api; int (*init)(char *, size_t, int, va_list); int (*deinit)(); int (*options)(const char *option, const void *);
+};
+struct st_mysql;
+#include <mysql/plugin_auth_common.h>
+typedef struct st_plugin_vio_info
+{
+  enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+         MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+  int socket;
+} MYSQL_PLUGIN_VIO_INFO;
+typedef struct st_plugin_vio
+{
+  int (*read_packet)(struct st_plugin_vio *vio,
+                     unsigned char **buf);
+  int (*write_packet)(struct st_plugin_vio *vio,
+                      const unsigned char *packet,
+                      int packet_len);
+  void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+} MYSQL_PLUGIN_VIO;
+struct st_mysql_client_plugin_AUTHENTICATION
+{
+  int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; const char *license; void *mysql_api; int (*init)(char *, size_t, int, va_list); int (*deinit)(); int (*options)(const char *option, const void *);
+  int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
+};
+struct st_mysql_client_plugin *
+mysql_load_plugin(struct st_mysql *mysql, const char *name, int type,
+                  int argc, ...);
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type,
+                    int argc, va_list args);
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type);
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(struct st_mysql *mysql,
+                             struct st_mysql_client_plugin *plugin);
+int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin,
+                                 const char *option,
+                                 const void *value);

=== modified file 'include/mysql/plugin.h'
--- a/include/mysql/plugin.h	2010-08-30 14:07:40 +0000
+++ b/include/mysql/plugin.h	2010-09-20 14:17:32 +0000
@@ -83,7 +83,8 @@ typedef struct st_mysql_xid MYSQL_XID;
 #define MYSQL_INFORMATION_SCHEMA_PLUGIN  4  /* The I_S plugin type */
 #define MYSQL_AUDIT_PLUGIN           5  /* The Audit plugin type        */
 #define MYSQL_REPLICATION_PLUGIN     6	/* The replication plugin type */
-#define MYSQL_MAX_PLUGIN_TYPE_NUM    7  /* The number of plugin types   */
+#define MYSQL_AUTHENTICATION_PLUGIN  7  /* The authentication plugin type */
+#define MYSQL_MAX_PLUGIN_TYPE_NUM    8  /* The number of plugin types   */
 
 /* We use the following strings to define licenses for plugins */
 #define PLUGIN_LICENSE_PROPRIETARY 0

=== added file 'include/mysql/plugin_auth.h'
--- a/include/mysql/plugin_auth.h	1970-01-01 00:00:00 +0000
+++ b/include/mysql/plugin_auth.h	2010-09-20 16:38:27 +0000
@@ -0,0 +1,125 @@
+#ifndef MYSQL_PLUGIN_AUTH_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+   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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/**
+  @file
+
+  Authentication Plugin API.
+
+  This file defines the API for server authentication plugins.
+*/
+
+#define MYSQL_PLUGIN_AUTH_INCLUDED
+
+#include <mysql/plugin.h>
+
+#define MYSQL_AUTHENTICATION_INTERFACE_VERSION 0x0100
+
+#include <mysql/plugin_auth_common.h>
+
+/* defines for MYSQL_SERVER_AUTH_INFO.password_used */
+
+#define PASSWORD_USED_NO         0
+#define PASSWORD_USED_YES        1
+#define PASSWORD_USED_NO_MENTION 2
+
+
+/**
+  Provides server plugin access to authentication information
+*/
+typedef struct st_mysql_server_auth_info
+{
+  /**
+    User name as sent by the client and shown in USER().
+    NULL if the client packet with the user name was not received yet.
+  */
+  char *user_name;
+
+  /**
+    Length of user_name
+  */
+  unsigned int user_name_length;
+
+  /**
+    A corresponding column value from the mysql.user table for the
+    matching account name
+  */
+  const char *auth_string;
+
+  /**
+    Length of auth_string
+  */
+  unsigned long auth_string_length;
+
+  /**
+    Matching account name as found in the mysql.user table.
+    A plugin can override it with another name that will be
+    used by MySQL for authorization, and shown in CURRENT_USER()
+  */
+  char authenticated_as[MYSQL_USERNAME_LENGTH+1]; 
+
+
+  /**
+    The unique user name that was used by the plugin to authenticate.
+    Plugins should put null-terminated UTF-8 here.
+    Available through the @@EXTERNAL_USER variable.
+  */  
+  char external_user[512];
+
+  /**
+    This only affects the "Authentication failed. Password used: %s"
+    error message. has the following values : 
+    0 : %s will be NO.
+    1 : %s will be YES.
+    2 : there will be no %s.
+    Set it as appropriate or ignore at will.
+  */
+  int  password_used;
+
+  /**
+    Set to the name of the connected client if it can be resolved, or to 
+    the address otherwise
+  */
+  const char *host_or_ip;
+
+  /**
+    Length of host_or_ip
+  */
+  unsigned int host_or_ip_length;
+
+} MYSQL_SERVER_AUTH_INFO;
+
+/**
+  Server authentication plugin descriptor
+*/
+struct st_mysql_auth
+{
+  int interface_version;                        /**< version plugin uses */
+  /**
+    A plugin that a client must use for authentication with this server
+    plugin. Can be NULL to mean "any plugin".
+  */
+  const char *client_auth_plugin;
+  /**
+    Function provided by the plugin which should perform authentication (using
+    the vio functions if necessary) and return 0 if successful. The plugin can
+    also fill the info.authenticated_as field if a different username should be
+    used for authorization.
+  */
+  int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info);
+};
+#endif
+

=== added file 'include/mysql/plugin_auth.h.pp'
--- a/include/mysql/plugin_auth.h.pp	1970-01-01 00:00:00 +0000
+++ b/include/mysql/plugin_auth.h.pp	2010-10-05 12:26:49 +0000
@@ -0,0 +1,224 @@
+#include <mysql/plugin.h>
+#include <mysql/services.h>
+#include <mysql/service_my_snprintf.h>
+extern struct my_snprintf_service_st {
+  size_t (*my_snprintf_type)(char*, size_t, const char*, ...);
+  size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list);
+} *my_snprintf_service;
+size_t my_snprintf(char* to, size_t n, const char* fmt, ...);
+size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap);
+#include <mysql/service_thd_alloc.h>
+struct st_mysql_lex_string
+{
+  char *str;
+  size_t length;
+};
+typedef struct st_mysql_lex_string MYSQL_LEX_STRING;
+extern struct thd_alloc_service_st {
+  void *(*thd_alloc_func)(void*, unsigned int);
+  void *(*thd_calloc_func)(void*, unsigned int);
+  char *(*thd_strdup_func)(void*, const char *);
+  char *(*thd_strmake_func)(void*, const char *, unsigned int);
+  void *(*thd_memdup_func)(void*, const void*, unsigned int);
+  MYSQL_LEX_STRING *(*thd_make_lex_string_func)(void*, MYSQL_LEX_STRING *,
+                                        const char *, unsigned int, int);
+} *thd_alloc_service;
+void *thd_alloc(void* thd, unsigned int size);
+void *thd_calloc(void* thd, unsigned int size);
+char *thd_strdup(void* thd, const char *str);
+char *thd_strmake(void* thd, const char *str, unsigned int size);
+void *thd_memdup(void* thd, const void* str, unsigned int size);
+MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str,
+                                      const char *str, unsigned int size,
+                                      int allocate_lex_string);
+#include <mysql/service_thd_wait.h>
+typedef enum _thd_wait_type_e {
+  THD_WAIT_MUTEX= 1,
+  THD_WAIT_DISKIO= 2,
+  THD_WAIT_ROW_TABLE_LOCK= 3,
+  THD_WAIT_GLOBAL_LOCK= 4
+} thd_wait_type;
+extern struct thd_wait_service_st {
+  void (*thd_wait_begin_func)(void*, thd_wait_type);
+  void (*thd_wait_end_func)(void*);
+} *thd_wait_service;
+void thd_wait_begin(void* thd, thd_wait_type wait_type);
+void thd_wait_end(void* thd);
+#include <mysql/service_thread_scheduler.h>
+struct scheduler_functions;
+extern struct my_thread_scheduler_service {
+  int (*set)(struct scheduler_functions *scheduler);
+  int (*reset)();
+} *my_thread_scheduler_service;
+int my_thread_scheduler_set(struct scheduler_functions *scheduler);
+int my_thread_scheduler_reset();
+struct st_mysql_xid {
+  long formatID;
+  long gtrid_length;
+  long bqual_length;
+  char data[128];
+};
+typedef struct st_mysql_xid MYSQL_XID;
+enum enum_mysql_show_type
+{
+  SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG,
+  SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR,
+  SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE,
+  SHOW_always_last
+};
+struct st_mysql_show_var {
+  const char *name;
+  char *value;
+  enum enum_mysql_show_type type;
+};
+typedef int (*mysql_show_var_func)(void*, struct st_mysql_show_var*, char *);
+struct st_mysql_sys_var;
+struct st_mysql_value;
+typedef int (*mysql_var_check_func)(void* thd,
+                                    struct st_mysql_sys_var *var,
+                                    void *save, struct st_mysql_value *value);
+typedef void (*mysql_var_update_func)(void* thd,
+                                      struct st_mysql_sys_var *var,
+                                      void *var_ptr, const void *save);
+struct st_mysql_plugin
+{
+  int type;
+  void *info;
+  const char *name;
+  const char *author;
+  const char *descr;
+  int license;
+  int (*init)(void *);
+  int (*deinit)(void *);
+  unsigned int version;
+  struct st_mysql_show_var *status_vars;
+  struct st_mysql_sys_var **system_vars;
+  void * __reserved1;
+};
+#include "plugin_ftparser.h"
+#include "plugin.h"
+enum enum_ftparser_mode
+{
+  MYSQL_FTPARSER_SIMPLE_MODE= 0,
+  MYSQL_FTPARSER_WITH_STOPWORDS= 1,
+  MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2
+};
+enum enum_ft_token_type
+{
+  FT_TOKEN_EOF= 0,
+  FT_TOKEN_WORD= 1,
+  FT_TOKEN_LEFT_PAREN= 2,
+  FT_TOKEN_RIGHT_PAREN= 3,
+  FT_TOKEN_STOPWORD= 4
+};
+typedef struct st_mysql_ftparser_boolean_info
+{
+  enum enum_ft_token_type type;
+  int yesno;
+  int weight_adjust;
+  char wasign;
+  char trunc;
+  char prev;
+  char *quot;
+} MYSQL_FTPARSER_BOOLEAN_INFO;
+typedef struct st_mysql_ftparser_param
+{
+  int (*mysql_parse)(struct st_mysql_ftparser_param *,
+                     char *doc, int doc_len);
+  int (*mysql_add_word)(struct st_mysql_ftparser_param *,
+                        char *word, int word_len,
+                        MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info);
+  void *ftparser_state;
+  void *mysql_ftparam;
+  struct charset_info_st *cs;
+  char *doc;
+  int length;
+  int flags;
+  enum enum_ftparser_mode mode;
+} MYSQL_FTPARSER_PARAM;
+struct st_mysql_ftparser
+{
+  int interface_version;
+  int (*parse)(MYSQL_FTPARSER_PARAM *param);
+  int (*init)(MYSQL_FTPARSER_PARAM *param);
+  int (*deinit)(MYSQL_FTPARSER_PARAM *param);
+};
+struct st_mysql_daemon
+{
+  int interface_version;
+};
+struct st_mysql_information_schema
+{
+  int interface_version;
+};
+struct st_mysql_storage_engine
+{
+  int interface_version;
+};
+struct handlerton;
+ struct Mysql_replication {
+   int interface_version;
+ };
+struct st_mysql_value
+{
+  int (*value_type)(struct st_mysql_value *);
+  const char *(*val_str)(struct st_mysql_value *, char *buffer, int *length);
+  int (*val_real)(struct st_mysql_value *, double *realbuf);
+  int (*val_int)(struct st_mysql_value *, long long *intbuf);
+  int (*is_unsigned)(struct st_mysql_value *);
+};
+int thd_in_lock_tables(const void* thd);
+int thd_tablespace_op(const void* thd);
+long long thd_test_options(const void* thd, long long test_options);
+int thd_sql_command(const void* thd);
+const char *thd_proc_info(void* thd, const char *info);
+void **thd_ha_data(const void* thd, const struct handlerton *hton);
+void thd_storage_lock_wait(void* thd, long long value);
+int thd_tx_isolation(const void* thd);
+char *thd_security_context(void* thd, char *buffer, unsigned int length,
+                           unsigned int max_query_len);
+void thd_inc_row_count(void* thd);
+int mysql_tmpfile(const char *prefix);
+int thd_killed(const void* thd);
+unsigned long thd_get_thread_id(const void* thd);
+void thd_get_xid(const void* thd, MYSQL_XID *xid);
+void mysql_query_cache_invalidate4(void* thd,
+                                   const char *key, unsigned int key_length,
+                                   int using_trx);
+void *thd_get_ha_data(const void* thd, const struct handlerton *hton);
+void thd_set_ha_data(void* thd, const struct handlerton *hton,
+                     const void *ha_data);
+#include <mysql/plugin_auth_common.h>
+typedef struct st_plugin_vio_info
+{
+  enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+         MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+  int socket;
+} MYSQL_PLUGIN_VIO_INFO;
+typedef struct st_plugin_vio
+{
+  int (*read_packet)(struct st_plugin_vio *vio,
+                     unsigned char **buf);
+  int (*write_packet)(struct st_plugin_vio *vio,
+                      const unsigned char *packet,
+                      int packet_len);
+  void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+} MYSQL_PLUGIN_VIO;
+typedef struct st_mysql_server_auth_info
+{
+  char *user_name;
+  unsigned int user_name_length;
+  const char *auth_string;
+  unsigned long auth_string_length;
+  char authenticated_as[48 +1];
+  char external_user[512];
+  int password_used;
+  const char *host_or_ip;
+  unsigned int host_or_ip_length;
+} MYSQL_SERVER_AUTH_INFO;
+struct st_mysql_auth
+{
+  int interface_version;
+  const char *client_auth_plugin;
+  int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info);
+};

=== added file 'include/mysql/plugin_auth_common.h'
--- a/include/mysql/plugin_auth_common.h	1970-01-01 00:00:00 +0000
+++ b/include/mysql/plugin_auth_common.h	2010-08-09 08:32:50 +0000
@@ -0,0 +1,105 @@
+#ifndef MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+   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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/**
+  @file
+
+  This file defines constants and data structures that are the same for
+  both client- and server-side authentication plugins.
+*/
+#define MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
+
+/** the max allowed length for a user name */
+#define MYSQL_USERNAME_LENGTH 48
+
+/**
+  return values of the plugin authenticate_user() method.
+*/
+
+/**
+  Authentication failed. Additionally, all other CR_xxx values
+  (libmysql error code) can be used too.
+
+  The client plugin may set the error code and the error message directly
+  in the MYSQL structure and return CR_ERROR. If a CR_xxx specific error
+  code was returned, an error message in the MYSQL structure will be
+  overwritten. If CR_ERROR is returned without setting the error in MYSQL,
+  CR_UNKNOWN_ERROR will be user.
+*/
+#define CR_ERROR 0
+/**
+  Authentication (client part) was successful. It does not mean that the
+  authentication as a whole was successful, usually it only means
+  that the client was able to send the user name and the password to the
+  server. If CR_OK is returned, the libmysql reads the next packet expecting
+  it to be one of OK, ERROR, or CHANGE_PLUGIN packets.
+*/
+#define CR_OK -1
+/**
+  Authentication was successful.
+  It means that the client has done its part successfully and also that
+  a plugin has read the last packet (one of OK, ERROR, CHANGE_PLUGIN).
+  In this case, libmysql will not read a packet from the server,
+  but it will use the data at mysql->net.read_pos.
+
+  A plugin may return this value if the number of roundtrips in the
+  authentication protocol is not known in advance, and the client plugin
+  needs to read one packet more to determine if the authentication is finished
+  or not.
+*/
+#define CR_OK_HANDSHAKE_COMPLETE -2
+
+typedef struct st_plugin_vio_info
+{
+  enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+         MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+  int socket;     /**< it's set, if the protocol is SOCKET or TCP */
+#ifdef _WIN32
+  HANDLE handle;  /**< it's set, if the protocol is PIPE or MEMORY */
+#endif
+} MYSQL_PLUGIN_VIO_INFO;
+
+/**
+  Provides plugin access to communication channel
+*/
+typedef struct st_plugin_vio
+{
+  /**
+    Plugin provides a pointer reference and this function sets it to the
+    contents of any incoming packet. Returns the packet length, or -1 if
+    the plugin should terminate.
+  */
+  int (*read_packet)(struct st_plugin_vio *vio, 
+                     unsigned char **buf);
+  
+  /**
+    Plugin provides a buffer with data and the length and this
+    function sends it as a packet. Returns 0 on success, 1 on failure.
+  */
+  int (*write_packet)(struct st_plugin_vio *vio, 
+                      const unsigned char *packet, 
+                      int packet_len);
+
+  /**
+    Fills in a st_plugin_vio_info structure, providing the information
+    about the connection.
+  */
+  void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+
+} MYSQL_PLUGIN_VIO;
+
+#endif
+

=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h	2010-08-05 12:53:09 +0000
+++ b/include/mysql_com.h	2010-08-16 15:16:07 +0000
@@ -162,9 +162,17 @@ enum enum_server_command
 #define CLIENT_MULTI_RESULTS    (1UL << 17) /* Enable/disable multi-results */
 #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
 
+#define CLIENT_PLUGIN_AUTH  (1UL << 19) /* Client supports plugin authentication */
+
 #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
 #define CLIENT_REMEMBER_OPTIONS (1UL << 31)
 
+#ifdef HAVE_COMPRESS
+#define CAN_CLIENT_COMPRESS CLIENT_COMPRESS
+#else
+#define CAN_CLIENT_COMPRESS 0
+#endif
+
 /* Gather all possible capabilites (flags) supported by the server */
 #define CLIENT_ALL_FLAGS  (CLIENT_LONG_PASSWORD | \
                            CLIENT_FOUND_ROWS | \
@@ -186,7 +194,8 @@ enum enum_server_command
                            CLIENT_MULTI_RESULTS | \
                            CLIENT_PS_MULTI_RESULTS | \
                            CLIENT_SSL_VERIFY_SERVER_CERT | \
-                           CLIENT_REMEMBER_OPTIONS)
+                           CLIENT_REMEMBER_OPTIONS | \
+                           CLIENT_PLUGIN_AUTH)
 
 /*
   Switch off the flags that are optional and depending on build flags
@@ -518,14 +527,14 @@ void create_random_string(char *to, unsi
 void hash_password(unsigned long *to, const char *password, unsigned int password_len);
 void make_scrambled_password_323(char *to, const char *password);
 void scramble_323(char *to, const char *message, const char *password);
-my_bool check_scramble_323(const char *, const char *message,
+my_bool check_scramble_323(const unsigned char *reply, const char *message,
                            unsigned long *salt);
 void get_salt_from_password_323(unsigned long *res, const char *password);
 void make_password_from_salt_323(char *to, const unsigned long *salt);
 
 void make_scrambled_password(char *to, const char *password);
 void scramble(char *to, const char *message, const char *password);
-my_bool check_scramble(const char *reply, const char *message,
+my_bool check_scramble(const unsigned char *reply, const char *message,
                        const unsigned char *hash_stage2);
 void get_salt_from_password(unsigned char *res, const char *password);
 void make_password_from_salt(char *to, const unsigned char *hash_stage2);

=== modified file 'include/sha1.h'
--- a/include/sha1.h	2009-09-23 21:32:31 +0000
+++ b/include/sha1.h	2010-10-05 14:27:18 +0000
@@ -9,12 +9,13 @@
 
  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
+ 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
 
 /*
  This is the header file for code which implements the Secure
@@ -28,6 +29,38 @@
  Please read the file sha1.c for more information.
 
  Modified 2002 by Peter Zaitsev to better follow MySQL standards
+
+  Original Source from: http://www.faqs.org/rfcs/rfc3174.html
+
+  Copyright (C) The Internet Society (2001).  All Rights Reserved.
+
+  This document and translations of it may be copied and furnished to
+  others, and derivative works that comment on or otherwise explain it
+  or assist in its implementation may be prepared, copied, published
+  and distributed, in whole or in part, without restriction of any
+  kind, provided that the above copyright notice and this paragraph are
+  included on all such copies and derivative works.  However, this
+  document itself may not be modified in any way, such as by removing
+  the copyright notice or references to the Internet Society or other
+  Internet organizations, except as needed for the purpose of
+  developing Internet standards in which case the procedures for
+  copyrights defined in the Internet Standards process must be
+  followed, or as required to translate it into languages other than
+  English.
+
+  The limited permissions granted above are perpetual and will not be
+  revoked by the Internet Society or its successors or assigns.
+
+  This document and the information contained herein is provided on an
+  "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+  TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+  BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+  HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+  Acknowledgement 
+  Funding for the RFC Editor function is currently provided by the 
+  Internet Society. 
 */
 
 

=== modified file 'include/sql_common.h'
--- a/include/sql_common.h	2009-09-23 21:32:31 +0000
+++ b/include/sql_common.h	2010-08-09 08:32:50 +0000
@@ -16,14 +16,60 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
+#define SQL_COMMON_INCLUDED
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <mysql.h>
 
 extern const char	*unknown_sqlstate;
 extern const char	*cant_connect_sqlstate;
 extern const char	*not_error_sqlstate;
 
-#ifdef	__cplusplus
-extern "C" {
+struct st_mysql_options_extention {
+  char *plugin_dir;
+  char *default_auth;
+};
+
+typedef struct st_mysql_methods
+{
+  my_bool (*read_query_result)(MYSQL *mysql);
+  my_bool (*advanced_command)(MYSQL *mysql,
+			      enum enum_server_command command,
+			      const unsigned char *header,
+			      unsigned long header_length,
+			      const unsigned char *arg,
+			      unsigned long arg_length,
+			      my_bool skip_check,
+                              MYSQL_STMT *stmt);
+  MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+			   unsigned int fields);
+  MYSQL_RES * (*use_result)(MYSQL *mysql);
+  void (*fetch_lengths)(unsigned long *to, 
+			MYSQL_ROW column, unsigned int field_count);
+  void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
+  int (*read_change_user_result)(MYSQL *mysql);
+#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
+  MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
+  my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
+  int (*stmt_execute)(MYSQL_STMT *stmt);
+  int (*read_binary_rows)(MYSQL_STMT *stmt);
+  int (*unbuffered_fetch)(MYSQL *mysql, char **row);
+  void (*free_embedded_thd)(MYSQL *mysql);
+  const char *(*read_statistics)(MYSQL *mysql);
+  my_bool (*next_result)(MYSQL *mysql);
+  int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
 #endif
+} MYSQL_METHODS;
+
+#define simple_command(mysql, command, arg, length, skip_check) \
+  (*(mysql)->methods->advanced_command)(mysql, command, 0,  \
+                                        0, arg, length, skip_check, NULL)
+#define stmt_command(mysql, command, arg, length, stmt) \
+  (*(mysql)->methods->advanced_command)(mysql, command, 0,  \
+                                        0, arg, length, 1, stmt)
 
 extern CHARSET_INFO *default_client_charset_info;
 MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
@@ -45,6 +91,19 @@ void set_stmt_errmsg(MYSQL_STMT *stmt, N
 void set_stmt_error(MYSQL_STMT *stmt, int errcode, const char *sqlstate,
                     const char *err);
 void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate);
+void set_mysql_extended_error(MYSQL *mysql, int errcode, const char *sqlstate,
+                              const char *format, ...);
+
+/* client side of the pluggable authentication */
+struct st_plugin_vio_info;
+void mpvio_info(Vio *vio, struct st_plugin_vio_info *info);
+int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+                    const char *data_plugin, const char *db);
+int mysql_client_plugin_init();
+void mysql_client_plugin_deinit();
+struct st_mysql_client_plugin;
+extern struct st_mysql_client_plugin *mysql_client_builtins[];
+
 #ifdef	__cplusplus
 }
 #endif

=== modified file 'libmysql/CMakeLists.txt'
--- a/libmysql/CMakeLists.txt	2010-08-12 15:19:57 +0000
+++ b/libmysql/CMakeLists.txt	2010-08-16 15:16:07 +0000
@@ -140,6 +140,7 @@ SET(CLIENT_SOURCES
   errmsg.c
   ../sql-common/client.c 
   ../sql-common/my_time.c 
+  ../sql-common/client_plugin.c 
   ../sql/net_serv.cc
   ../sql-common/pack.c 
   ../sql/password.c
@@ -148,7 +149,7 @@ ADD_CONVENIENCE_LIBRARY(clientlib ${CLIE
 DTRACE_INSTRUMENT(clientlib)
 ADD_DEPENDENCIES(clientlib GenError)
 
-SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES})
+SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL})
 
 # Merge several convenience libraries into one big mysqlclient
 # and link them together into shared library.

=== modified file 'libmysql/Makefile.shared'
--- a/libmysql/Makefile.shared	2010-07-23 20:17:14 +0000
+++ b/libmysql/Makefile.shared	2010-08-09 08:32:50 +0000
@@ -23,6 +23,7 @@
 MYSQLDATAdir =			$(localstatedir)
 MYSQLSHAREdir =			$(pkgdatadir)
 MYSQLBASEdir=			$(prefix)
+pkgplugindir =			$(pkglibdir)/plugin
 ## We'll use CLIENT_EXTRA_LDFLAGS for threaded and non-threaded
 ## until someone complains that they need separate options.
 LDADD =				@CLIENT_EXTRA_LDFLAGS@ $(target)
@@ -70,26 +71,27 @@ mysysobjects1 =		my_init.lo my_static.lo
 			my_rename.lo my_chsize.lo my_sync.lo \
 			my_getsystime.lo my_symlink2.lo mf_same.lo
 sqlobjects =		net.lo
-sql_cmn_objects =	pack.lo client.lo my_time.lo
+sql_cmn_objects =	pack.lo client.lo my_time.lo client_plugin.lo
 
 # Not needed in the minimum library
 mysysobjects2 =		my_lib.lo mf_qsort.lo
 mysysobjects =		$(mysysobjects1) $(mysysobjects2)
 target_libadd =		$(mysysobjects) $(mystringsobjects) $(dbugobjects) \
  $(sql_cmn_objects) $(vio_objects) $(sqlobjects)
-target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ 
+target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ @LIBDL@
 vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo
 
 BUILT_SOURCES		= link_sources
 
 CLEANFILES =		$(target_libadd) $(SHLIBOBJS) \
 			$(target) $(BUILT_SOURCES)
-DEFS =			-DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \
-			-DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \
+DEFS =			-DDEFAULT_CHARSET_HOME='"$(MYSQLBASEdir)"' \
+			-DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
 			-DDEFAULT_HOME_ENV=MYSQL_HOME \
+ 			-DPLUGINDIR='"$(pkgplugindir)"' \
 			-DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \
-			-DDEFAULT_SYSCONFDIR="\"$(sysconfdir)\"" \
-			-DSHAREDIR="\"$(MYSQLSHAREdir)\"" -DDISABLE_DTRACE \
+			-DDEFAULT_SYSCONFDIR='"$(sysconfdir)"' \
+			-DSHAREDIR='"$(MYSQLSHAREdir)"' -DDISABLE_DTRACE \
 			$(target_defs)
 
 if HAVE_YASSL

=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h	2010-01-12 16:48:28 +0000
+++ b/libmysql/client_settings.h	2010-08-09 08:32:50 +0000
@@ -28,7 +28,8 @@ extern char *	mysql_unix_port;
                              CLIENT_PROTOCOL_41 | \
                              CLIENT_SECURE_CONNECTION | \
                              CLIENT_MULTI_RESULTS | \
-                             CLIENT_PS_MULTI_RESULTS)
+                             CLIENT_PS_MULTI_RESULTS | \
+                             CLIENT_PLUGIN_AUTH)
 
 sig_handler my_pipe_sig_handler(int sig);
 void read_user_name(char *name);
@@ -67,7 +68,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt);
 int cli_read_binary_rows(MYSQL_STMT *stmt);
 int cli_unbuffered_fetch(MYSQL *mysql, char **row);
 const char * cli_read_statistics(MYSQL *mysql);
-int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd);
+int cli_read_change_user_result(MYSQL *mysql);
 
 #ifdef EMBEDDED_LIBRARY
 int init_embedded_server(int argc, char **argv, char **groups);

=== modified file 'libmysql/errmsg.c'
--- a/libmysql/errmsg.c	2010-07-23 20:16:29 +0000
+++ b/libmysql/errmsg.c	2010-08-09 08:32:50 +0000
@@ -84,7 +84,8 @@ const char *client_errors[]=
   "Lost connection to MySQL server at '%s', system error: %d",
   "Statement closed indirectly because of a preceeding %s() call",
   "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again",
-  "This handle is already connected. Use a separate handle for each connection."
+  "This handle is already connected. Use a separate handle for each connection.",
+  "Authentication plugin '%s' cannot be loaded: %s",
   ""
 };
 

=== modified file 'libmysql/libmysql.c'
--- a/libmysql/libmysql.c	2010-09-09 12:36:57 +0000
+++ b/libmysql/libmysql.c	2010-10-04 12:42:16 +0000
@@ -126,6 +126,8 @@ int STDCALL mysql_server_init(int argc _
     if (my_init())				/* Will init threads */
       return 1;
     init_client_errs();
+    if (mysql_client_plugin_init())
+      return 1;
     if (!mysql_port)
     {
       char *env;
@@ -196,6 +198,8 @@ void STDCALL mysql_server_end()
   if (!mysql_client_init)
     return;
 
+  mysql_client_plugin_deinit();
+
 #ifdef EMBEDDED_LIBRARY
   end_embedded_server();
 #endif
@@ -345,44 +349,14 @@ mysql_connect(MYSQL *mysql,const char *h
   Change user and database
 **************************************************************************/
 
-int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
-{
-  NET *net= &mysql->net;
-  ulong pkt_length;
-
-  pkt_length= cli_safe_read(mysql);
-  
-  if (pkt_length == packet_error)
-    return 1;
-
-  if (pkt_length == 1 && net->read_pos[0] == 254 &&
-      mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-  {
-    /*
-      By sending this very specific reply server asks us to send scrambled
-      password in old format. The reply contains scramble_323.
-    */
-    scramble_323(buff, mysql->scramble, passwd);
-    if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) ||
-        net_flush(net))
-    {
-      set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate);
-      return 1;
-    }
-    /* Read what server thinks about out new auth message report */
-    if (cli_safe_read(mysql) == packet_error)
-      return 1;
-  }
-  return 0;
-}
-
 my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,
 				  const char *passwd, const char *db)
 {
-  char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2];
-  char *end= buff;
   int rc;
   CHARSET_INFO *saved_cs= mysql->charset;
+  char *saved_user= mysql->user;
+  char *saved_passwd= mysql->passwd;
+  char *saved_db= mysql->db;
 
   DBUG_ENTER("mysql_change_user");
 
@@ -396,49 +370,11 @@ my_bool	STDCALL mysql_change_user(MYSQL 
 
   /* Use an empty string instead of NULL. */
 
-  if (!user)
-    user="";
-  if (!passwd)
-    passwd="";
-
-  /*
-    Store user into the buffer.
-    Advance position as strmake returns a pointer to the closing NUL.
-  */
-  end= strmake(end, user, USERNAME_LENGTH) + 1;
-
-  /* write scrambled password according to server capabilities */
-  if (passwd[0])
-  {
-    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-    {
-      *end++= SCRAMBLE_LENGTH;
-      scramble(end, mysql->scramble, passwd);
-      end+= SCRAMBLE_LENGTH;
-    }
-    else
-    {
-      scramble_323(end, mysql->scramble, passwd);
-      end+= SCRAMBLE_LENGTH_323 + 1;
-    }
-  }
-  else
-    *end++= '\0';                               /* empty password */
-  /* Add database if needed */
-  end= strmake(end, db ? db : "", NAME_LEN) + 1;
-
-  /* Add character set number. */
-
-  if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-  {
-    int2store(end, (ushort) mysql->charset->number);
-    end+= 2;
-  }
-
-  /* Write authentication package */
-  simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1);
+  mysql->user= (char*)(user ? user : "");
+  mysql->passwd= (char*)(passwd ? passwd : "");
+  mysql->db= 0;
 
-  rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd);
+  rc= run_plugin_auth(mysql, 0, 0, 0, db);
 
   /*
     The server will close all statements no matter was the attempt
@@ -448,18 +384,21 @@ my_bool	STDCALL mysql_change_user(MYSQL 
   if (rc == 0)
   {
     /* Free old connect information */
-    my_free(mysql->user);
-    my_free(mysql->passwd);
-    my_free(mysql->db);
+    my_free(saved_user);
+    my_free(saved_passwd);
+    my_free(saved_db);
 
     /* alloc new connect information */
-    mysql->user=  my_strdup(user,MYF(MY_WME));
-    mysql->passwd=my_strdup(passwd,MYF(MY_WME));
-    mysql->db=    db ? my_strdup(db,MYF(MY_WME)) : 0;
+    mysql->user= my_strdup(mysql->user, MYF(MY_WME));
+    mysql->passwd= my_strdup(mysql->passwd, MYF(MY_WME));
+    mysql->db= db ? my_strdup(db, MYF(MY_WME)) : 0;
   }
   else
   {
     mysql->charset= saved_cs;
+    mysql->user= saved_user;
+    mysql->passwd= saved_passwd;
+    mysql->db= saved_db;
   }
 
   DBUG_RETURN(rc);

=== modified file 'libmysql/libmysql.def'
--- a/libmysql/libmysql.def	2009-12-16 14:34:11 +0000
+++ b/libmysql/libmysql.def	2010-10-04 12:54:41 +0000
@@ -104,3 +104,4 @@ EXPORTS
 	mysql_server_end
 	mysql_set_character_set
 	mysql_get_character_set_info
+        mysql_plugin_options

=== modified file 'libmysqld/CMakeLists.txt'
--- a/libmysqld/CMakeLists.txt	2010-08-18 11:29:04 +0000
+++ b/libmysqld/CMakeLists.txt	2010-09-20 14:17:32 +0000
@@ -42,6 +42,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
            ../libmysql/libmysql.c ../libmysql/errmsg.c ../client/get_password.c
            ../sql-common/client.c ../sql-common/my_time.c 
            ../sql-common/my_user.c ../sql-common/pack.c
+           ../sql-common/client_plugin.c
            ../sql/password.c ../sql/discover.cc ../sql/derror.cc 
            ../sql/field.cc ../sql/field_conv.cc
            ../sql/filesort.cc ../sql/gstream.cc

=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am	2010-09-01 03:38:53 +0000
+++ b/libmysqld/Makefile.am	2010-10-04 12:42:16 +0000
@@ -39,7 +39,7 @@ pkglib_LIBRARIES =	libmysqld.a
 SUBDIRS =		. examples
 libmysqld_sources=	libmysqld.c lib_sql.cc emb_qcache.cc
 libmysqlsources =	errmsg.c get_password.c libmysql.c client.c pack.c \
-                        my_time.c
+                        my_time.c client_plugin.c
 
 noinst_HEADERS =	embedded_priv.h emb_qcache.h
 

=== modified file 'libmysqld/embedded_priv.h'
--- a/libmysqld/embedded_priv.h	2006-12-31 00:32:21 +0000
+++ b/libmysqld/embedded_priv.h	2010-08-09 08:32:50 +0000
@@ -15,6 +15,8 @@
 
 /* Prototypes for the embedded version of MySQL */
 
+#include <sql_common.h>
+
 C_MODE_START
 void lib_connection_phase(NET *net, int phase);
 void init_embedded_mysql(MYSQL *mysql, int client_flag);

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2010-09-02 18:37:04 +0000
+++ b/libmysqld/lib_sql.cc	2010-10-04 12:42:16 +0000
@@ -35,7 +35,6 @@ C_MODE_START
 #include <mysql.h>
 #undef ER
 #include "errmsg.h"
-#include <sql_common.h>
 #include "embedded_priv.h"
 
 extern unsigned int mysql_server_last_errno;
@@ -414,11 +413,10 @@ static MYSQL_RES * emb_store_result(MYSQ
   return mysql_store_result(mysql);
 }
 
-int emb_read_change_user_result(MYSQL *mysql, 
-				char *buff __attribute__((unused)),
-				const char *passwd __attribute__((unused)))
+int emb_read_change_user_result(MYSQL *mysql)
 {
-  return mysql_errno(mysql);
+  mysql->net.read_pos= (uchar*)""; // fake an OK packet
+  return mysql_errno(mysql) ? packet_error : 1 /* length of the OK packet */;
 }
 
 MYSQL_METHODS embedded_methods= 
@@ -429,6 +427,7 @@ MYSQL_METHODS embedded_methods= 
   emb_store_result,
   emb_fetch_lengths, 
   emb_flush_use_result,
+  emb_read_change_user_result,
   emb_list_fields,
   emb_read_prepare_result,
   emb_stmt_execute,
@@ -437,7 +436,6 @@ MYSQL_METHODS embedded_methods= 
   emb_free_embedded_thd,
   emb_read_statistics,
   emb_read_query_result,
-  emb_read_change_user_result,
   emb_read_rows_from_cursor
 };
 
@@ -605,6 +603,7 @@ void init_embedded_mysql(MYSQL *mysql, i
   THD *thd = (THD *)mysql->thd;
   thd->mysql= mysql;
   mysql->server_version= server_version;
+  mysql->client_flag= client_flag;
   init_alloc_root(&mysql->field_alloc, 8192, 0);
 }
 
@@ -668,14 +667,20 @@ err:
 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;
   thd_init_client_charset(thd, mysql->charset->number);
   thd->update_charset();
   Security_context *sctx= thd->security_ctx;
   sctx->host_or_ip= sctx->host= (char*) my_localhost;
   strmake(sctx->priv_host, (char*) my_localhost,  MAX_HOSTNAME-1);
-  sctx->priv_user= sctx->user= my_strdup(mysql->user, MYF(0));
-  result= check_user(thd, COM_CONNECT, NULL, 0, db, true);
+  strmake(sctx->priv_user, mysql->user,  USERNAME_LENGTH-1);
+  sctx->user= my_strdup(mysql->user, MYF(0));
+  sctx->proxy_user[0]= 0;
+  sctx->master_access= GLOBAL_ACLS;       // Full rights
+  /* Change database if necessary */
+  if (!(result= (db && db[0] && mysql_change_db(thd, &db_str, FALSE))))
+    my_ok(thd);
   thd->protocol->end_statement();
   emb_read_query_result(mysql);
   return result;
@@ -684,14 +689,15 @@ int check_embedded_connection(MYSQL *mys
 #else
 int check_embedded_connection(MYSQL *mysql, const char *db)
 {
+  /*
+    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;
+  NET *net= &mysql->net;
   THD *thd= (THD*)mysql->thd;
   Security_context *sctx= thd->security_ctx;
-  int result;
-  char scramble_buff[SCRAMBLE_LENGTH];
-  int passwd_len;
 
-  thd_init_client_charset(thd, mysql->charset->number);
-  thd->update_charset();
   if (mysql->options.client_ip)
   {
     sctx->host= my_strdup(mysql->options.client_ip, MYF(0));
@@ -702,37 +708,43 @@ int check_embedded_connection(MYSQL *mys
   sctx->host_or_ip= sctx->host;
 
   if (acl_check_host(sctx->host, sctx->ip))
-  {
-    result= ER_HOST_NOT_PRIVILEGED;
     goto err;
-  }
 
-  sctx->user= my_strdup(mysql->user, MYF(0));
+  /* construct a COM_CHANGE_USER packet */
+  end= strmake(buf, mysql->user, USERNAME_LENGTH) + 1;
+
+  memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble
+  thd->scramble[SCRAMBLE_LENGTH]= 0;
+
   if (mysql->passwd && mysql->passwd[0])
   {
-    memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble
-    thd->scramble[SCRAMBLE_LENGTH]= 0;
-    scramble(scramble_buff, thd->scramble, mysql->passwd);
-    passwd_len= SCRAMBLE_LENGTH;
+    *end++= SCRAMBLE_LENGTH;
+    scramble(end, thd->scramble, mysql->passwd);
+    end+= SCRAMBLE_LENGTH;
   }
   else
-    passwd_len= 0;
+    *end++= 0;
 
-  if((result= check_user(thd, COM_CONNECT, 
-			 scramble_buff, passwd_len, db, true)))
-     goto err;
+  end= strmake(end, db ? db : "", NAME_LEN) + 1;
 
-  return 0;
-err:
+  int2store(end, (ushort) mysql->charset->number);
+  end+= 2;
+
+  /* acl_authenticate() takes the data from thd->net->read_pos */
+  thd->net.read_pos= (uchar*)buf;
+
+  if (acl_authenticate(thd, 0, end - buf))
   {
-    NET *net= &mysql->net;
-    strmake(net->last_error, thd->stmt_da->message(),
-            sizeof(net->last_error)-1);
-    memcpy(net->sqlstate,
-           mysql_errno_to_sqlstate(thd->stmt_da->sql_errno()),
-           sizeof(net->sqlstate)-1);
+    x_free(thd->security_ctx->user);
+    goto err;
   }
-  return result;
+  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);
+  return 1;
 }
 #endif
 

=== modified file 'libmysqld/libmysqld.c'
--- a/libmysqld/libmysqld.c	2010-07-15 11:16:06 +0000
+++ b/libmysqld/libmysqld.c	2010-08-09 08:32:50 +0000
@@ -17,7 +17,6 @@
 #include <mysql.h>
 #include <mysqld_error.h>
 #include <my_pthread.h>
-#include "embedded_priv.h"
 #include <my_sys.h>
 #include <mysys_err.h>
 #include <m_string.h>
@@ -28,6 +27,7 @@
 #include <signal.h>
 #include <time.h>
 #include <sql_common.h>
+#include "embedded_priv.h"
 #include "client_settings.h"
 #ifdef	 HAVE_PWD_H
 #include <pwd.h>
@@ -165,7 +165,11 @@ mysql_real_connect(MYSQL *mysql,const ch
   client_flag|=CLIENT_CAPABILITIES;
   if (client_flag & CLIENT_MULTI_STATEMENTS)
     client_flag|= CLIENT_MULTI_RESULTS;
-  client_flag&= ~CLIENT_COMPRESS;
+  /*
+    no compression in embedded as we don't send any data,
+    and no pluggable auth, as we cannot do a client-server dialog
+  */
+  client_flag&= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH);
   if (db)
     client_flag|=CLIENT_CONNECT_WITH_DB;
 

=== modified file 'mysql-test/collections/default.daily'
--- a/mysql-test/collections/default.daily	2010-06-02 12:01:42 +0000
+++ b/mysql-test/collections/default.daily	2010-10-05 11:33:54 +0000
@@ -2,5 +2,4 @@ perl mysql-test-run.pl --timer --force -
 perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=ps_row         --vardir=var-ps_row   --ps-protocol --mysqld=--binlog-format=row
 perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=embedded       --vardir=var-emebbed  --embedded
 perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=funcs_1        --vardir=var-funcs_1                                              --suite=funcs_1
-perl mysql-test-run.pl --timer --force --parallel=auto                                                 --comment=rpl_ndb_row    --vardir=var-rpl_ndb_row            --mysqld=--binlog-format=row  --suite=rpl_ndb,ndb
 perl mysql-test-run.pl --timer --force --parallel=auto --experimental=collections/default.experimental --comment=rpl_binlog_row --vardir=var-rpl_binlog_row --mysqld=--binlog-format=row --suite=rpl,binlog --skip-ndb

=== added file 'mysql-test/include/have_plugin_auth.inc'
--- a/mysql-test/include/have_plugin_auth.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/have_plugin_auth.inc	2010-08-09 08:32:50 +0000
@@ -0,0 +1,4 @@
+disable_query_log;
+--require r/true.require
+select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE` FROM INFORMATION_SCHEMA.PLUGINS 
+  WHERE PLUGIN_NAME='test_plugin_server';

=== modified file 'mysql-test/lib/mtr_cases.pm'
--- a/mysql-test/lib/mtr_cases.pm	2010-08-30 13:19:46 +0000
+++ b/mysql-test/lib/mtr_cases.pm	2010-10-05 11:33:54 +0000
@@ -926,7 +926,7 @@ sub collect_one_test_case {
     {
       # Ndb is not supported, skip it
       $tinfo->{'skip'}= 1;
-      $tinfo->{'comment'}= "No ndbcluster support";
+      $tinfo->{'comment'}= "No ndbcluster support or ndb tests not enabled";
       return $tinfo;
     }
     elsif ( $::opt_skip_ndbcluster )

=== modified file 'mysql-test/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl	2010-09-30 10:53:36 +0000
+++ b/mysql-test/mysql-test-run.pl	2010-10-05 11:33:54 +0000
@@ -131,6 +131,9 @@ my $opt_start_dirty;
 my $opt_start_exit;
 my $start_only;
 
+my $auth_filename;              # the name of the authentication test plugin
+my $auth_plugin;                # the path to the authentication test plugin
+
 END {
   if ( defined $opt_tmpdir_pid and $opt_tmpdir_pid == $$ )
   {
@@ -281,7 +284,8 @@ sub testcase_timeout ($) {
 
 our $opt_warnings= 1;
 
-our $opt_skip_ndbcluster= 0;
+our $opt_include_ndbcluster= 0;
+our $opt_skip_ndbcluster= 1;
 
 my $exe_ndbd;
 my $exe_ndb_mgmd;
@@ -877,6 +881,7 @@ sub command_line_setup {
              # Control what test suites or cases to run
              'force'                    => \$opt_force,
              'with-ndbcluster-only'     => \&collect_option,
+             'include-ndbcluster'       => \$opt_include_ndbcluster,
              'skip-ndbcluster|skip-ndb' => \$opt_skip_ndbcluster,
              'suite|suites=s'           => \$opt_suites,
              'skip-rpl'                 => \&collect_option,
@@ -1057,6 +1062,22 @@ sub command_line_setup {
                                     "$basedir/sql/share/charsets",
                                     "$basedir/share/charsets");
 
+  # Look for client test plugin 
+  if (IS_WINDOWS)
+  {
+    $auth_filename = "auth_test_plugin.dll";
+  }
+  else
+  {
+    $auth_filename = "auth_test_plugin.so";
+  }
+  $auth_plugin=
+  mtr_file_exists(vs_config_dirs('plugin/auth/',$auth_filename),
+    "$basedir/plugin/auth/.libs/" . $auth_filename,
+    "$basedir/lib/mysql/plugin/" . $auth_filename,
+    "$basedir/lib/plugin/" . $auth_filename);
+
+
   if (using_extern())
   {
     # Connect to the running mysqld and find out what it supports
@@ -1944,6 +1965,24 @@ sub environment_setup {
     ($lib_udf_example ? dirname($lib_udf_example) : "");
 
   # --------------------------------------------------------------------------
+  # Add the path where mysqld will find the auth test plugin (dialog.so/dll)
+  # --------------------------------------------------------------------------
+  if ($auth_plugin)
+  {
+    $ENV{'PLUGIN_AUTH'}= basename($auth_plugin);
+    $ENV{'PLUGIN_AUTH_OPT'}= "--plugin-dir=".dirname($auth_plugin);
+
+    $ENV{'PLUGIN_AUTH_LOAD'}="--plugin_load=test_plugin_server=".$auth_filename;
+  }
+  else
+  {
+    $ENV{'PLUGIN_AUTH'}= "";
+    $ENV{'PLUGIN_AUTH_OPT'}="--plugin-dir=";
+    $ENV{'PLUGIN_AUTH_LOAD'}="";
+  }
+  
+
+  # --------------------------------------------------------------------------
   # Add the path where mysqld will find ha_example.so
   # --------------------------------------------------------------------------
   if ($mysql_version_id >= 50100) {
@@ -2471,6 +2510,11 @@ sub vs_config_dirs ($$) {
 sub check_ndbcluster_support ($) {
   my $mysqld_variables= shift;
 
+  if ($opt_include_ndbcluster)
+  {
+    $opt_skip_ndbcluster= 0;
+  }
+
   if ($opt_skip_ndbcluster)
   {
     mtr_report(" - skipping ndbcluster");
@@ -5032,6 +5076,10 @@ sub start_mysqltest ($) {
   mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir);
   mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir);
   mtr_add_arg($args, "--logdir=%s/log", $opt_vardir);
+  if ($auth_plugin)
+  {
+    mtr_add_arg($args, "--plugin_dir=%s", dirname($auth_plugin));
+  }
 
   # Log line number and time  for each line in .test file
   mtr_add_arg($args, "--mark-progress")
@@ -5510,7 +5558,8 @@ Options to control what test suites or c
 
   force                 Continue to run the suite after failure
   with-ndbcluster-only  Run only tests that include "ndb" in the filename
-  skip-ndb[cluster]     Skip all tests that need cluster
+  skip-ndb[cluster]     Skip all tests that need cluster. Default.
+  include-ndb[cluster]  Enable all tests that need cluster
   do-test=PREFIX or REGEX
                         Run test cases which name are prefixed with PREFIX
                         or fulfills REGEX

=== modified file 'mysql-test/r/1st.result'
--- a/mysql-test/r/1st.result	2010-01-12 01:47:27 +0000
+++ b/mysql-test/r/1st.result	2010-08-09 08:32:50 +0000
@@ -21,6 +21,7 @@ ndb_binlog_index
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv

=== modified file 'mysql-test/r/change_user.result'
--- a/mysql-test/r/change_user.result	2009-12-22 09:35:56 +0000
+++ b/mysql-test/r/change_user.result	2010-08-09 08:32:50 +0000
@@ -1,3 +1,39 @@
+grant select on test.* to test_nopw;
+grant select on test.* to test_oldpw identified by password "09301740536db389";
+grant select on test.* to test_newpw identified by "newpw";
+select user(), current_user(), database();
+user()	current_user()	database()
+root@localhost	root@localhost	test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<root@localhost>	<root@localhost>	test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_nopw@localhost>	<test_nopw@%>	NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_oldpw@localhost>	<test_oldpw@%>	NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_newpw@localhost>	<test_newpw@%>	NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<root@localhost>	<root@localhost>	NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_nopw@localhost>	<test_nopw@%>	test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_oldpw@localhost>	<test_oldpw@%>	test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<test_newpw@localhost>	<test_newpw@%>	test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>')	concat('<', current_user(), '>')	database()
+<root@localhost>	<root@localhost>	test
+drop user test_nopw;
+drop user test_oldpw;
+drop user test_newpw;
 Bug#20023
 SELECT @@session.sql_big_selects;
 @@session.sql_big_selects

=== modified file 'mysql-test/r/connect.result'
--- a/mysql-test/r/connect.result	2009-03-06 14:56:17 +0000
+++ b/mysql-test/r/connect.result	2010-08-09 08:32:50 +0000
@@ -15,6 +15,7 @@ ndb_binlog_index
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv
@@ -48,6 +49,7 @@ ndb_binlog_index
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv
@@ -89,6 +91,7 @@ ndb_binlog_index
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv

=== modified file 'mysql-test/r/events_bugs.result'
--- a/mysql-test/r/events_bugs.result	2009-12-22 09:35:56 +0000
+++ b/mysql-test/r/events_bugs.result	2010-08-09 08:32:50 +0000
@@ -568,6 +568,7 @@ USE test;
 SHOW GRANTS FOR CURRENT_USER;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 SET GLOBAL event_scheduler = ON;
 CREATE TABLE events_test.event_log
 (id int KEY AUTO_INCREMENT, ev_nm char(40), ev_cnt int, ev_tm timestamp)

=== modified file 'mysql-test/r/grant.result'
--- a/mysql-test/r/grant.result	2010-08-05 12:53:09 +0000
+++ b/mysql-test/r/grant.result	2010-08-16 15:16:07 +0000
@@ -13,8 +13,48 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'loc
 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
 grant delete on mysqltest.* to mysqltest_1@localhost;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	SPECIFIED	EDH-RSA-DES-CBC3-SHA			0	0	0	0
+Host	localhost
+User	mysqltest_1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	SPECIFIED
+ssl_cipher	EDH-RSA-DES-CBC3-SHA
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -44,15 +84,95 @@ delete from mysql.user where user='mysql
 flush privileges;
 grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	0	0	0
+Host	localhost
+User	mysqltest_1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	10
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
 grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	20	30	0
+Host	localhost
+User	mysqltest_1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	10
+max_updates	20
+max_connections	30
+max_user_connections	0
+plugin	
+authentication_string	
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
@@ -164,6 +284,7 @@ Warnings:
 Warning	1364	Field 'ssl_cipher' doesn't have a default value
 Warning	1364	Field 'x509_issuer' doesn't have a default value
 Warning	1364	Field 'x509_subject' doesn't have a default value
+Warning	1364	Field 'authentication_string' doesn't have a default value
 insert into mysql.db (host, db, user, select_priv) values
 ('localhost', 'a%', 'test11', 'Y'), ('localhost', 'ab%', 'test11', 'Y');
 alter table mysql.db order by db asc;
@@ -625,16 +746,19 @@ show grants for root@localhost;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
 GRANT SELECT ON `�`.* TO 'root'@'localhost'
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 flush privileges;
 show grants for root@localhost;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
 GRANT SELECT ON `�`.* TO 'root'@'localhost'
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop database �;
 revoke all privileges on �.* from root@localhost;
 show grants for root@localhost;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 set names latin1;
 create user mysqltest_7@;
 set password for mysqltest_7@ = password('systpass');

=== modified file 'mysql-test/r/grant2.result'
--- a/mysql-test/r/grant2.result	2010-01-07 15:39:11 +0000
+++ b/mysql-test/r/grant2.result	2010-08-09 08:32:50 +0000
@@ -11,7 +11,7 @@ grant create user on *.* to mysqltest_1@
 create user mysqltest_2@localhost;
 grant select on `my\_1`.* to mysqltest_2@localhost;
 grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
-ERROR 42000: You must have privileges to update tables in the mysql database to be able to change passwords for others
+ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysql'
 grant update on mysql.* to mysqltest_1@localhost;
 grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
 grant select on `my\_1`.* to mysqltest_3@localhost;
@@ -287,6 +287,7 @@ Warnings:
 Warning	1364	Field 'ssl_cipher' doesn't have a default value
 Warning	1364	Field 'x509_issuer' doesn't have a default value
 Warning	1364	Field 'x509_subject' doesn't have a default value
+Warning	1364	Field 'authentication_string' doesn't have a default value
 create user mysqltest_A@'%';
 rename user mysqltest_B@'%' to mysqltest_C@'%';
 drop user mysqltest_C@'%';
@@ -334,7 +335,7 @@ delete from mysql.user where user like '
 flush privileges;
 drop database mysqltest_1;
 set password = password("changed");
-ERROR 42000: Access denied for user ''@'localhost' to database 'mysql'
+ERROR 42000: Can't find any matching row in the user table
 lock table mysql.user write;
 flush privileges;
 grant all on *.* to 'mysqltest_1'@'localhost';
@@ -354,6 +355,7 @@ Warnings:
 Warning	1364	Field 'ssl_cipher' doesn't have a default value
 Warning	1364	Field 'x509_issuer' doesn't have a default value
 Warning	1364	Field 'x509_subject' doesn't have a default value
+Warning	1364	Field 'authentication_string' doesn't have a default value
 INSERT INTO mysql.db (host, db, user, select_priv) VALUES
 ('%','TESTDB','mysqltest_1','Y');
 FLUSH PRIVILEGES;

=== modified file 'mysql-test/r/grant_cache_no_prot.result'
--- a/mysql-test/r/grant_cache_no_prot.result	2009-05-20 13:27:44 +0000
+++ b/mysql-test/r/grant_cache_no_prot.result	2010-08-09 08:32:50 +0000
@@ -7,9 +7,11 @@ flush status;
 show grants for current_user;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 create database if not exists mysqltest;
 create table mysqltest.t1 (a int,b int,c int);
 create table mysqltest.t2 (a int,b int,c int);

=== modified file 'mysql-test/r/grant_cache_ps_prot.result'
--- a/mysql-test/r/grant_cache_ps_prot.result	2009-05-15 10:15:56 +0000
+++ b/mysql-test/r/grant_cache_ps_prot.result	2010-08-09 08:32:50 +0000
@@ -7,9 +7,11 @@ flush status;
 show grants for current_user;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 create database if not exists mysqltest;
 create table mysqltest.t1 (a int,b int,c int);
 create table mysqltest.t2 (a int,b int,c int);

=== modified file 'mysql-test/r/information_schema.result'
--- a/mysql-test/r/information_schema.result	2010-07-15 15:46:41 +0000
+++ b/mysql-test/r/information_schema.result	2010-08-09 08:32:50 +0000
@@ -88,6 +88,7 @@ host
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv
@@ -684,6 +685,7 @@ Alter_routine_priv	select,insert,update,
 max_questions	select,insert,update,references
 max_connections	select,insert,update,references
 max_user_connections	select,insert,update,references
+authentication_string	select,insert,update,references
 use test;
 create function sub1(i int) returns int
 return i+1;
@@ -870,7 +872,7 @@ AND table_name not like 'ndb%' AND table
 GROUP BY TABLE_SCHEMA;
 table_schema	count(*)
 information_schema	30
-mysql	22
+mysql	23
 create table t1 (i int, j int);
 create trigger trg1 before insert on t1 for each row
 begin

=== modified file 'mysql-test/r/log_tables_upgrade.result'
--- a/mysql-test/r/log_tables_upgrade.result	2010-09-22 08:15:41 +0000
+++ b/mysql-test/r/log_tables_upgrade.result	2010-10-04 12:42:16 +0000
@@ -27,6 +27,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.renamed_general_log                          OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK

=== modified file 'mysql-test/r/mysql_upgrade.result'
--- a/mysql-test/r/mysql_upgrade.result	2010-09-22 08:15:41 +0000
+++ b/mysql-test/r/mysql_upgrade.result	2010-10-04 12:42:16 +0000
@@ -15,6 +15,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK
@@ -43,6 +44,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK
@@ -71,6 +73,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK
@@ -101,6 +104,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK
@@ -135,6 +139,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK
@@ -172,6 +177,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK

=== modified file 'mysql-test/r/mysql_upgrade_ssl.result'
--- a/mysql-test/r/mysql_upgrade_ssl.result	2010-09-22 08:15:41 +0000
+++ b/mysql-test/r/mysql_upgrade_ssl.result	2010-10-04 12:42:16 +0000
@@ -17,6 +17,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log                                     OK
 mysql.tables_priv                                  OK

=== modified file 'mysql-test/r/mysqlcheck.result'
--- a/mysql-test/r/mysqlcheck.result	2010-01-15 11:42:15 +0000
+++ b/mysql-test/r/mysqlcheck.result	2010-08-09 08:32:50 +0000
@@ -18,6 +18,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log
 note     : The storage engine for the table doesn't support optimize
@@ -43,6 +44,7 @@ mysql.ndb_binlog_index                  
 mysql.plugin                                       OK
 mysql.proc                                         OK
 mysql.procs_priv                                   OK
+mysql.proxy_priv                                   OK
 mysql.servers                                      OK
 mysql.slow_log
 note     : The storage engine for the table doesn't support optimize

=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result	2010-08-30 14:07:40 +0000
+++ b/mysql-test/r/mysqld--help-win.result	2010-10-05 12:26:49 +0000
@@ -919,7 +919,6 @@ slave-transaction-retries 10
 slave-type-conversions 
 slow-launch-time 2
 slow-query-log FALSE
-socket MySQL
 sort-buffer-size 2097152
 sporadic-binlog-dump-fail FALSE
 sql-mode 

=== added file 'mysql-test/r/plugin_auth.result'
--- a/mysql-test/r/plugin_auth.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/plugin_auth.result	2010-10-04 13:09:37 +0000
@@ -0,0 +1,237 @@
+SELECT PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_DESCRIPTION
+FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server';
+PLUGIN_STATUS	ACTIVE
+PLUGIN_TYPE	AUTHENTICATION
+PLUGIN_DESCRIPTION	plugin API test plugin
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+SELECT plugin,authentication_string FROM mysql.user WHERE User='plug';
+plugin	authentication_string
+test_plugin_server	plug_dest
+## test plugin auth
+ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES)
+GRANT PROXY ON plug_dest TO plug;
+select USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+plug@localhost	plug_dest@%
+## test SET PASSWORD
+SET PASSWORD = PASSWORD('plug_dest');
+Warnings:
+Note	1699	SET PASSWORD has no significance for users authenticating via plugins
+## test bad credentials
+ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES)
+## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD
+## test correct default plugin
+select USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+plug@localhost	plug@%
+## test no_auto_create_user sql mode with plugin users
+SET @@sql_mode=no_auto_create_user;
+GRANT INSERT ON TEST.* TO grant_user IDENTIFIED WITH 'test_plugin_server';
+SET @@sql_mode=default;
+DROP USER grant_user;
+## test utf-8 user name
+CREATE USER `Ÿ` IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+GRANT PROXY ON plug_dest TO `Ÿ`;
+select USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+Ÿ@localhost	plug_dest@%
+DROP USER `Ÿ`;
+## test GRANT ... IDENTIFIED WITH/BY ...
+CREATE DATABASE test_grant_db;
+# create new user via GRANT WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user 
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+GRANT PROXY ON plug_dest TO new_grant_user;
+select USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+new_grant_user@localhost	plug_dest@%
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+REVOKE ALL PRIVILEGES ON test_grant_db.* FROM new_grant_user;
+# try re-create existing user via GRANT IDENTIFIED BY
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+IDENTIFIED BY 'unused_password';
+# make sure password doesn't take precendence
+ERROR 28000: Access denied for user 'new_grant_user'@'localhost' (using password: YES)
+#make sure plugin auth still available
+select USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+new_grant_user@localhost	plug_dest@%
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+DROP USER new_grant_user;
+# try re-create existing user via GRANT IDENTIFIED WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug already exists
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug_dest
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug_dest already exists
+REVOKE SELECT on test_grant_db.* FROM joro 
+INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'' at line 2
+REVOKE SELECT on test_grant_db.* FROM joro 
+INDENTIFIED BY 'plug_dest_passwd';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY 'plug_dest_passwd'' at line 2
+REVOKE SELECT on test_grant_db.* FROM joro 
+INDENTIFIED BY PASSWORD 'plug_dest_passwd';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY PASSWORD 'plug_dest_passwd'' at line 2
+DROP DATABASE test_grant_db;
+## GRANT PROXY tests
+CREATE USER grant_plug IDENTIFIED WITH 'test_plugin_server' 
+AS 'grant_plug_dest';
+CREATE USER grant_plug_dest IDENTIFIED BY 'grant_plug_dest_passwd';
+CREATE USER grant_plug_dest2 IDENTIFIED BY 'grant_plug_dest_passwd2';
+# ALL PRIVILEGES doesn't include PROXY
+GRANT ALL PRIVILEGES ON *.* TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug'@'localhost' (using password: YES)
+GRANT ALL PRIVILEGES,PROXY ON grant_plug_dest TO grant_plug;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PROXY ON grant_plug_dest TO grant_plug' at line 1
+this should fail : can't combine PROXY
+GRANT ALL SELECT,PROXY ON grant_plug_dest TO grant_plug;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT,PROXY ON grant_plug_dest TO grant_plug' at line 1
+# this should fail : no such grant
+REVOKE PROXY ON grant_plug_dest FROM grant_plug;
+ERROR 42000: There is no such grant defined for user 'grant_plug' on host '%'
+in grant_plug_dest_con
+## testing what an ordinary user can grant 
+this should fail : no rights to grant all
+GRANT PROXY ON ''@'' TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should fail : not the same user
+GRANT PROXY ON grant_plug TO grant_plug_dest;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should fail : same user, but on a different host
+GRANT PROXY ON grant_plug_dest TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug_dest2;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug_dest2;
+this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug WITH GRANT OPTION;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug;
+this should fail : can't create users
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug@localhost;
+ERROR 42000: You are not allowed to create a user with GRANT
+in default connection
+# test what root can grant
+should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO grant_plug;
+REVOKE PROXY ON ''@'' FROM grant_plug;
+should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test' 
+WITH GRANT OPTION;
+need USAGE : PROXY doesn't contain it.
+GRANT USAGE on *.* TO proxy_admin;
+in proxy_admin_con;
+should work : proxy_admin has proxy to ''@''
+GRANT PROXY ON future_user TO grant_plug;
+in default connection
+SHOW GRANTS FOR grant_plug;
+Grants for grant_plug@%
+GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION
+GRANT PROXY ON 'future_user'@'%' TO 'grant_plug'@'%'
+REVOKE PROXY ON future_user FROM grant_plug;
+SHOW GRANTS FOR grant_plug;
+Grants for grant_plug@%
+GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION
+## testing drop user
+CREATE USER test_drop@localhost;
+GRANT PROXY ON future_user TO test_drop@localhost;
+SHOW GRANTS FOR test_drop@localhost;
+Grants for test_drop@localhost
+GRANT USAGE ON *.* TO 'test_drop'@'localhost'
+GRANT PROXY ON 'future_user'@'%' TO 'test_drop'@'localhost'
+DROP USER test_drop@localhost;
+SELECT * FROM mysql.proxy_priv WHERE Host = 'test_drop' AND User = 'localhost';
+Host	User	Proxied_Host	Proxied_User	With_Grant
+DROP USER proxy_admin;
+DROP USER grant_plug,grant_plug_dest,grant_plug_dest2;
+## END GRANT PROXY tests
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## @@proxy_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.proxy_user;
+USER()	CURRENT_USER()	@@LOCAL.proxy_user
+root@localhost	root@localhost	NULL
+SELECT @@GLOBAL.proxy_user;
+ERROR HY000: Variable 'proxy_user' is a SESSION variable
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+NULL
+SET GLOBAL proxy_user = 'test';
+ERROR HY000: Variable 'proxy_user' is a read only variable
+SET LOCAL proxy_user = 'test';
+ERROR HY000: Variable 'proxy_user' is a read only variable
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+NULL
+# in connection plug_con
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+'plug'@'%'
+# in connection default
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## END @@proxy_user tests
+## @@external_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.external_user;
+USER()	CURRENT_USER()	@@LOCAL.external_user
+root@localhost	root@localhost	NULL
+SELECT @@GLOBAL.external_user;
+ERROR HY000: Variable 'external_user' is a SESSION variable
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+NULL
+SET GLOBAL external_user = 'test';
+ERROR HY000: Variable 'external_user' is a read only variable
+SET LOCAL external_user = 'test';
+ERROR HY000: Variable 'external_user' is a read only variable
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+NULL
+# in connection plug_con
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+'plug'@'%'
+# in connection default
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## END @@external_user tests
+#
+#  Bug #56798 : Wrong credentials assigned when using a proxy user.
+#
+GRANT ALL PRIVILEGES ON *.* TO power_user;
+GRANT USAGE ON anonymous_db.* TO ''@''
+  IDENTIFIED WITH 'test_plugin_server' AS 'power_user';
+GRANT PROXY ON power_user TO ''@'';
+CREATE DATABASE confidential_db;
+SELECT user(),current_user(),@@proxy_user;
+user()	current_user()	@@proxy_user
+test_login_user@localhost	power_user@%	''@''
+DROP USER power_user;
+DROP USER ''@'';
+DROP DATABASE confidential_db;
+# Test case #2 (crash with double grant proxy)
+CREATE USER ''@'' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
+CREATE USER standard_user;
+CREATE DATABASE shared;
+GRANT ALL PRIVILEGES ON shared.* TO standard_user;
+GRANT PROXY ON standard_user TO ''@'';
+#should not crash
+GRANT PROXY ON standard_user TO ''@'';
+DROP USER ''@'';
+DROP USER standard_user;
+DROP DATABASE shared;

=== modified file 'mysql-test/r/ps.result'
--- a/mysql-test/r/ps.result	2010-08-05 12:53:09 +0000
+++ b/mysql-test/r/ps.result	2010-08-16 15:16:07 +0000
@@ -1194,13 +1194,13 @@ SET @aux= "SELECT COUNT(*)
 prepare my_stmt from @aux;
 execute my_stmt;
 COUNT(*)
-40
+42
 execute my_stmt;
 COUNT(*)
-40
+42
 execute my_stmt;
 COUNT(*)
-40
+42
 deallocate prepare my_stmt;
 drop procedure if exists p1|
 drop table if exists t1|

=== modified file 'mysql-test/r/sp_notembedded.result'
--- a/mysql-test/r/sp_notembedded.result	2010-02-24 13:52:27 +0000
+++ b/mysql-test/r/sp_notembedded.result	2010-08-09 08:32:50 +0000
@@ -9,9 +9,11 @@ end|
 call bug4902()|
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 call bug4902()|
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop procedure bug4902|
 drop procedure if exists bug4902_2|
 create procedure bug4902_2()
@@ -206,9 +208,11 @@ create procedure 15298_2 () sql security
 call 15298_1();
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 call 15298_2();
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop user mysqltest_1@localhost;
 drop procedure 15298_1;
 drop procedure 15298_2;
@@ -245,6 +249,8 @@ max_updates, max_connections, max_user_c
 VALUES('%', 'mysqltest_1', password(''), 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'N',
 'N', 'N', 'N', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'Y', 'Y', 'N', '',
 '', '', '', '0', '0', '0', '0');
+Warnings:
+Warning	1364	Field 'authentication_string' doesn't have a default value
 FLUSH PRIVILEGES;
 CREATE PROCEDURE p1(i INT) BEGIN END;
 DROP PROCEDURE p1;

=== modified file 'mysql-test/r/system_mysql_db.result'
--- a/mysql-test/r/system_mysql_db.result	2009-11-06 14:20:27 +0000
+++ b/mysql-test/r/system_mysql_db.result	2010-08-09 08:32:50 +0000
@@ -14,6 +14,7 @@ ndb_binlog_index
 plugin
 proc
 procs_priv
+proxy_priv
 servers
 slow_log
 tables_priv
@@ -119,6 +120,8 @@ user	CREATE TABLE `user` (
   `max_updates` int(11) unsigned NOT NULL DEFAULT '0',
   `max_connections` int(11) unsigned NOT NULL DEFAULT '0',
   `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
+  `plugin` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
+  `authentication_string` text COLLATE utf8_bin NOT NULL,
   PRIMARY KEY (`Host`,`User`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
 show create table func;

=== modified file 'mysql-test/suite/federated/federated.result'
--- a/mysql-test/suite/federated/federated.result	2010-03-05 10:51:37 +0000
+++ b/mysql-test/suite/federated/federated.result	2010-08-09 08:32:50 +0000
@@ -60,7 +60,7 @@ CREATE TABLE federated.t1 (
 ENGINE="FEDERATED" DEFAULT CHARSET=latin1
 CONNECTION='mysql://user:pass@stripped:SLAVE_PORT/federated/t1';
 SELECT * FROM federated.t1;
-ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: YES)
+ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: NO)
 DROP TABLE federated.t1;
 CREATE TABLE federated.t1 (
 `id` int(20) NOT NULL,

=== modified file 'mysql-test/suite/funcs_1/r/innodb_trig_03e.result'
--- a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result	2010-08-16 06:46:21 +0000
+++ b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result	2010-09-20 14:17:32 +0000
@@ -573,6 +573,7 @@ root@localhost
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop trigger trg1_1;
 use priv_db;
 

=== modified file 'mysql-test/suite/funcs_1/r/is_columns_mysql.result'
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result	2010-07-16 15:04:39 +0000
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result	2010-08-09 08:32:50 +0000
@@ -134,6 +134,11 @@ def	mysql	procs_priv	Routine_name	4		NO	
 def	mysql	procs_priv	Routine_type	5	NULL	NO	enum	9	27	NULL	NULL	utf8	utf8_bin	enum('FUNCTION','PROCEDURE')	PRI		select,insert,update,references	
 def	mysql	procs_priv	Timestamp	8	CURRENT_TIMESTAMP	NO	timestamp	NULL	NULL	NULL	NULL	NULL	NULL	timestamp		on update CURRENT_TIMESTAMP	select,insert,update,references	
 def	mysql	procs_priv	User	3		NO	char	16	48	NULL	NULL	utf8	utf8_bin	char(16)	PRI		select,insert,update,references	
+def	mysql	proxy_priv	Host	1		NO	char	60	180	NULL	NULL	utf8	utf8_bin	char(60)	PRI		select,insert,update,references	
+def	mysql	proxy_priv	Proxied_Host	3		NO	char	16	48	NULL	NULL	utf8	utf8_bin	char(16)	PRI		select,insert,update,references	
+def	mysql	proxy_priv	Proxied_User	4		NO	char	60	180	NULL	NULL	utf8	utf8_bin	char(60)	PRI		select,insert,update,references	
+def	mysql	proxy_priv	User	2		NO	char	16	48	NULL	NULL	utf8	utf8_bin	char(16)	PRI		select,insert,update,references	
+def	mysql	proxy_priv	With_Grant	5	0	NO	tinyint	NULL	NULL	3	0	NULL	NULL	tinyint(1)			select,insert,update,references	
 def	mysql	servers	Db	3		NO	char	64	192	NULL	NULL	utf8	utf8_general_ci	char(64)			select,insert,update,references	
 def	mysql	servers	Host	2		NO	char	64	192	NULL	NULL	utf8	utf8_general_ci	char(64)			select,insert,update,references	
 def	mysql	servers	Owner	9		NO	char	64	192	NULL	NULL	utf8	utf8_general_ci	char(64)			select,insert,update,references	
@@ -178,6 +183,7 @@ def	mysql	time_zone_transition_type	Time
 def	mysql	time_zone_transition_type	Transition_type_id	2	NULL	NO	int	NULL	NULL	10	0	NULL	NULL	int(10) unsigned	PRI		select,insert,update,references	
 def	mysql	user	Alter_priv	17	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
 def	mysql	user	Alter_routine_priv	28	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
+def	mysql	user	authentication_string	42	NULL	NO	text	65535	65535	NULL	NULL	utf8	utf8_bin	text			select,insert,update,references	
 def	mysql	user	Create_priv	8	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
 def	mysql	user	Create_routine_priv	27	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
 def	mysql	user	Create_tablespace_priv	32	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
@@ -199,6 +205,7 @@ def	mysql	user	max_questions	37	0	NO	int
 def	mysql	user	max_updates	38	0	NO	int	NULL	NULL	10	0	NULL	NULL	int(11) unsigned			select,insert,update,references	
 def	mysql	user	max_user_connections	40	0	NO	int	NULL	NULL	10	0	NULL	NULL	int(11) unsigned			select,insert,update,references	
 def	mysql	user	Password	3		NO	char	41	41	NULL	NULL	latin1	latin1_bin	char(41)			select,insert,update,references	
+def	mysql	user	plugin	41		NO	char	60	180	NULL	NULL	utf8	utf8_bin	char(60)			select,insert,update,references	
 def	mysql	user	Process_priv	12	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
 def	mysql	user	References_priv	15	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
 def	mysql	user	Reload_priv	10	N	NO	enum	1	3	NULL	NULL	utf8	utf8_general_ci	enum('N','Y')			select,insert,update,references	
@@ -418,6 +425,11 @@ NULL	mysql	proc	modified	timestamp	NULL	
 3.0000	mysql	procs_priv	Grantor	char	77	231	utf8	utf8_bin	char(77)
 3.0000	mysql	procs_priv	Proc_priv	set	27	81	utf8	utf8_general_ci	set('Execute','Alter Routine','Grant')
 NULL	mysql	procs_priv	Timestamp	timestamp	NULL	NULL	NULL	NULL	timestamp
+3.0000	mysql	proxy_priv	Host	char	60	180	utf8	utf8_bin	char(60)
+3.0000	mysql	proxy_priv	User	char	16	48	utf8	utf8_bin	char(16)
+3.0000	mysql	proxy_priv	Proxied_Host	char	16	48	utf8	utf8_bin	char(16)
+3.0000	mysql	proxy_priv	Proxied_User	char	60	180	utf8	utf8_bin	char(60)
+NULL	mysql	proxy_priv	With_Grant	tinyint	NULL	NULL	NULL	NULL	tinyint(1)
 3.0000	mysql	servers	Server_name	char	64	192	utf8	utf8_general_ci	char(64)
 3.0000	mysql	servers	Host	char	64	192	utf8	utf8_general_ci	char(64)
 3.0000	mysql	servers	Db	char	64	192	utf8	utf8_general_ci	char(64)
@@ -500,3 +512,5 @@ NULL	mysql	user	max_questions	int	NULL	N
 NULL	mysql	user	max_updates	int	NULL	NULL	NULL	NULL	int(11) unsigned
 NULL	mysql	user	max_connections	int	NULL	NULL	NULL	NULL	int(11) unsigned
 NULL	mysql	user	max_user_connections	int	NULL	NULL	NULL	NULL	int(11) unsigned
+3.0000	mysql	user	plugin	char	60	180	utf8	utf8_bin	char(60)
+1.0000	mysql	user	authentication_string	text	65535	65535	utf8	utf8_bin	text

=== modified file 'mysql-test/suite/funcs_1/r/is_key_column_usage.result'
--- a/mysql-test/suite/funcs_1/r/is_key_column_usage.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_key_column_usage.result	2010-08-09 08:32:50 +0000
@@ -106,6 +106,10 @@ def	mysql	PRIMARY	def	mysql	procs_priv	D
 def	mysql	PRIMARY	def	mysql	procs_priv	User
 def	mysql	PRIMARY	def	mysql	procs_priv	Routine_name
 def	mysql	PRIMARY	def	mysql	procs_priv	Routine_type
+def	mysql	PRIMARY	def	mysql	proxy_priv	Host
+def	mysql	PRIMARY	def	mysql	proxy_priv	User
+def	mysql	PRIMARY	def	mysql	proxy_priv	Proxied_Host
+def	mysql	PRIMARY	def	mysql	proxy_priv	Proxied_User
 def	mysql	PRIMARY	def	mysql	servers	Server_name
 def	mysql	PRIMARY	def	mysql	tables_priv	Host
 def	mysql	PRIMARY	def	mysql	tables_priv	Db

=== modified file 'mysql-test/suite/funcs_1/r/is_statistics.result'
--- a/mysql-test/suite/funcs_1/r/is_statistics.result	2010-02-20 10:07:32 +0000
+++ b/mysql-test/suite/funcs_1/r/is_statistics.result	2010-08-09 08:32:50 +0000
@@ -118,6 +118,10 @@ def	mysql	procs_priv	mysql	PRIMARY
 def	mysql	procs_priv	mysql	PRIMARY
 def	mysql	procs_priv	mysql	PRIMARY
 def	mysql	procs_priv	mysql	Grantor
+def	mysql	proxy_priv	mysql	PRIMARY
+def	mysql	proxy_priv	mysql	PRIMARY
+def	mysql	proxy_priv	mysql	PRIMARY
+def	mysql	proxy_priv	mysql	PRIMARY
 def	mysql	servers	mysql	PRIMARY
 def	mysql	tables_priv	mysql	PRIMARY
 def	mysql	tables_priv	mysql	PRIMARY

=== modified file 'mysql-test/suite/funcs_1/r/is_statistics_mysql.result'
--- a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result	2010-02-20 10:07:32 +0000
+++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result	2010-08-09 08:32:50 +0000
@@ -40,6 +40,10 @@ def	mysql	procs_priv	0	mysql	PRIMARY	2	D
 def	mysql	procs_priv	0	mysql	PRIMARY	3	User	A	#CARD#	NULL	NULL		BTREE		
 def	mysql	procs_priv	0	mysql	PRIMARY	4	Routine_name	A	#CARD#	NULL	NULL		BTREE		
 def	mysql	procs_priv	0	mysql	PRIMARY	5	Routine_type	A	#CARD#	NULL	NULL		BTREE		
+def	mysql	proxy_priv	0	mysql	PRIMARY	1	Host	A	#CARD#	NULL	NULL		BTREE		
+def	mysql	proxy_priv	0	mysql	PRIMARY	2	User	A	#CARD#	NULL	NULL		BTREE		
+def	mysql	proxy_priv	0	mysql	PRIMARY	3	Proxied_Host	A	#CARD#	NULL	NULL		BTREE		
+def	mysql	proxy_priv	0	mysql	PRIMARY	4	Proxied_User	A	#CARD#	NULL	NULL		BTREE		
 def	mysql	servers	0	mysql	PRIMARY	1	Server_name	A	#CARD#	NULL	NULL		BTREE		
 def	mysql	tables_priv	1	mysql	Grantor	1	Grantor	A	#CARD#	NULL	NULL		BTREE		
 def	mysql	tables_priv	0	mysql	PRIMARY	1	Host	A	#CARD#	NULL	NULL		BTREE		

=== modified file 'mysql-test/suite/funcs_1/r/is_table_constraints.result'
--- a/mysql-test/suite/funcs_1/r/is_table_constraints.result	2010-02-20 10:07:32 +0000
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints.result	2010-08-09 08:32:50 +0000
@@ -73,6 +73,7 @@ def	mysql	PRIMARY	mysql	ndb_binlog_index
 def	mysql	PRIMARY	mysql	plugin
 def	mysql	PRIMARY	mysql	proc
 def	mysql	PRIMARY	mysql	procs_priv
+def	mysql	PRIMARY	mysql	proxy_priv
 def	mysql	PRIMARY	mysql	servers
 def	mysql	PRIMARY	mysql	tables_priv
 def	mysql	PRIMARY	mysql	time_zone

=== modified file 'mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result'
--- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result	2010-08-09 08:32:50 +0000
@@ -23,6 +23,7 @@ def	mysql	PRIMARY	mysql	ndb_binlog_index
 def	mysql	PRIMARY	mysql	plugin	PRIMARY KEY
 def	mysql	PRIMARY	mysql	proc	PRIMARY KEY
 def	mysql	PRIMARY	mysql	procs_priv	PRIMARY KEY
+def	mysql	PRIMARY	mysql	proxy_priv	PRIMARY KEY
 def	mysql	PRIMARY	mysql	servers	PRIMARY KEY
 def	mysql	PRIMARY	mysql	tables_priv	PRIMARY KEY
 def	mysql	PRIMARY	mysql	time_zone	PRIMARY KEY

=== modified file 'mysql-test/suite/funcs_1/r/is_tables_mysql.result'
--- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result	2010-08-09 08:32:50 +0000
@@ -336,6 +336,29 @@ user_comment	Procedure privileges
 Separator	-----------------------------------------------------
 TABLE_CATALOG	def
 TABLE_SCHEMA	mysql
+TABLE_NAME	proxy_priv
+TABLE_TYPE	BASE TABLE
+ENGINE	MyISAM
+VERSION	10
+ROW_FORMAT	Fixed
+TABLE_ROWS	#TBLR#
+AVG_ROW_LENGTH	#ARL#
+DATA_LENGTH	#DL#
+MAX_DATA_LENGTH	#MDL#
+INDEX_LENGTH	#IL#
+DATA_FREE	#DF#
+AUTO_INCREMENT	NULL
+CREATE_TIME	#CRT#
+UPDATE_TIME	#UT#
+CHECK_TIME	#CT#
+TABLE_COLLATION	utf8_bin
+CHECKSUM	NULL
+CREATE_OPTIONS	#CO#
+TABLE_COMMENT	#TC#
+user_comment	User proxy privileges
+Separator	-----------------------------------------------------
+TABLE_CATALOG	def
+TABLE_SCHEMA	mysql
 TABLE_NAME	servers
 TABLE_TYPE	BASE TABLE
 ENGINE	MyISAM

=== modified file 'mysql-test/suite/funcs_1/r/is_user_privileges.result'
--- a/mysql-test/suite/funcs_1/r/is_user_privileges.result	2009-11-02 11:10:04 +0000
+++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result	2010-08-09 08:32:50 +0000
@@ -69,46 +69,436 @@ GRANT UPDATE ON *.* TO 'testuser2'@'loca
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 #
 # Add GRANT OPTION db_datadict.* to testuser1;
 GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 # Establish connection testuser1 (user=testuser1)
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -123,46 +513,436 @@ GRANT SELECT ON *.* TO 'testuser1'@'loca
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	SELECT	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	SELECT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	Y
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
 #
 # Here <SELECT YES> is shown correctly for testuser1;
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	SELECT	YES
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	SELECT
+IS_GRANTABLE	YES
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	Y
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	Y
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	SELECT	YES
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	SELECT
+IS_GRANTABLE	YES
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	Y
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	Y
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION
@@ -172,9 +952,14 @@ GRANT SELECT ON `mysql`.`user` TO 'testu
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 ERROR 42000: SELECT command denied to user 'testuser2'@'localhost' for table 'user'
@@ -185,8 +970,10 @@ GRANT INSERT, UPDATE ON *.* TO 'testuser
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 ERROR 42000: SELECT command denied to user 'testuser3'@'localhost' for table 'user'
@@ -200,23 +987,158 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -228,8 +1150,10 @@ ERROR 42000: CREATE command denied to us
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -246,29 +1170,286 @@ GRANT SELECT ON mysql.user TO 'testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -280,14 +1461,138 @@ USE db_datadict;
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -302,23 +1607,158 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
-'testuser2'@'localhost'	def	INSERT	NO
-'testuser2'@'localhost'	def	UPDATE	NO
-'testuser3'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	INSERT
+IS_GRANTABLE	NO
+GRANTEE	'testuser2'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	UPDATE
+IS_GRANTABLE	NO
+GRANTEE	'testuser3'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	localhost
+User	testuser1
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser2
+Password	
+Select_priv	N
+Insert_priv	Y
+Update_priv	Y
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
+Host	localhost
+User	testuser3
+Password	
+Select_priv	N
+Insert_priv	N
+Update_priv	N
+Delete_priv	N
+Create_priv	N
+Drop_priv	N
+Reload_priv	N
+Shutdown_priv	N
+Process_priv	N
+File_priv	N
+Grant_priv	N
+References_priv	N
+Index_priv	N
+Alter_priv	N
+Show_db_priv	N
+Super_priv	N
+Create_tmp_table_priv	N
+Lock_tables_priv	N
+Execute_priv	N
+Repl_slave_priv	N
+Repl_client_priv	N
+Create_view_priv	N
+Show_view_priv	N
+Create_routine_priv	N
+Alter_routine_priv	N
+Create_user_priv	N
+Event_priv	N
+Trigger_priv	N
+Create_tablespace_priv	N
+ssl_type	
+ssl_cipher	
+x509_issuer	
+x509_subject	
+max_questions	0
+max_updates	0
+max_connections	0
+max_user_connections	0
+plugin	
+authentication_string	
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
 ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -341,31 +1781,36 @@ DROP DATABASE IF EXISTS db_datadict;
 ########################################################################################
 SELECT * FROM information_schema.user_privileges
 WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
 SHOW GRANTS FOR 'testuser1'@'localhost';
 ERROR 42000: There is no such grant defined for user 'testuser1' on host 'localhost'
 DROP   USER 'testuser1'@'localhost';
 CREATE USER 'testuser1'@'localhost';
 SELECT * FROM information_schema.user_privileges
 WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	USAGE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	USAGE
+IS_GRANTABLE	NO
 SHOW GRANTS FOR 'testuser1'@'localhost';
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
 GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost';
 SELECT * FROM information_schema.user_privileges
 WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
-'testuser1'@'localhost'	def	SELECT	NO
-'testuser1'@'localhost'	def	FILE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	SELECT
+IS_GRANTABLE	NO
+GRANTEE	'testuser1'@'localhost'
+TABLE_CATALOG	def
+PRIVILEGE_TYPE	FILE
+IS_GRANTABLE	NO
 SHOW GRANTS FOR 'testuser1'@'localhost';
 Grants for testuser1@localhost
 GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost'
 DROP USER   'testuser1'@'localhost';
 SELECT * FROM information_schema.user_privileges
 WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_GRANTABLE
 SHOW GRANTS FOR 'testuser1'@'localhost';
 ERROR 42000: There is no such grant defined for user 'testuser1' on host 'localhost'
 ########################################################################

=== modified file 'mysql-test/suite/funcs_1/r/memory_trig_03e.result'
--- a/mysql-test/suite/funcs_1/r/memory_trig_03e.result	2010-08-16 06:46:21 +0000
+++ b/mysql-test/suite/funcs_1/r/memory_trig_03e.result	2010-09-20 14:17:32 +0000
@@ -574,6 +574,7 @@ root@localhost
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop trigger trg1_1;
 use priv_db;
 

=== modified file 'mysql-test/suite/funcs_1/r/myisam_trig_03e.result'
--- a/mysql-test/suite/funcs_1/r/myisam_trig_03e.result	2010-08-16 06:46:21 +0000
+++ b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result	2010-09-20 14:17:32 +0000
@@ -574,6 +574,7 @@ root@localhost
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 drop trigger trg1_1;
 use priv_db;
 

=== modified file 'mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result'
--- a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result	2009-10-23 06:56:30 +0000
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result	2010-08-09 08:32:50 +0000
@@ -65,6 +65,7 @@ ERROR 42000: Access denied for user 'roo
 SHOW GRANTS;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 CREATE INDEX i_processlist ON processlist (user);
 ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
 DROP TABLE processlist;

=== modified file 'mysql-test/suite/funcs_1/r/processlist_priv_ps.result'
--- a/mysql-test/suite/funcs_1/r/processlist_priv_ps.result	2009-10-23 06:56:30 +0000
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_ps.result	2010-08-09 08:32:50 +0000
@@ -65,6 +65,7 @@ ERROR 42000: Access denied for user 'roo
 SHOW GRANTS;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 CREATE INDEX i_processlist ON processlist (user);
 ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
 DROP TABLE processlist;

=== modified file 'mysql-test/suite/funcs_1/t/is_user_privileges.test'
--- a/mysql-test/suite/funcs_1/t/is_user_privileges.test	2009-10-26 07:35:20 +0000
+++ b/mysql-test/suite/funcs_1/t/is_user_privileges.test	2010-08-09 08:32:50 +0000
@@ -104,20 +104,26 @@ ORDER BY grantee, table_catalog, privile
 let $my_select2= SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
 let $my_show= SHOW GRANTS;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo #
 --echo # Add GRANT OPTION db_datadict.* to testuser1;
 GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo # Establish connection testuser1 (user=testuser1)
 --replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
 connect (testuser1, localhost, testuser1, , db_datadict);
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 --echo
@@ -128,36 +134,46 @@ connection default;
 GRANT SELECT ON *.* TO 'testuser1'@'localhost';
 --echo #
 --echo # Here <SELECT NO> is shown correctly for testuser1;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
 --echo #
 --echo # Here <SELECT YES> is shown correctly for testuser1;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo # Switch to connection testuser1
 # check that this appears
 connection testuser1;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 --echo # Establish connection testuser2 (user=testuser2)
 --replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
 connect (testuser2, localhost, testuser2, , db_datadict);
+--vertical_results
 eval $my_select1;
 --error ER_TABLEACCESS_DENIED_ERROR
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 --echo # Establish connection testuser3 (user=testuser3)
 --replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
 connect (testuser3, localhost, testuser3, , test);
+--vertical_results
 eval $my_select1;
 --error ER_TABLEACCESS_DENIED_ERROR
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 --echo
@@ -165,23 +181,29 @@ eval $my_show;
 --echo # Switch to connection default
 connection default;
 REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo # Switch to connection testuser1
 # check for changes
 connection testuser1;
+--vertical_results
 eval $my_select1;
 --error ER_TABLEACCESS_DENIED_ERROR
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 # OK, testuser1 has no privs here
 --error ER_TABLEACCESS_DENIED_ERROR
 CREATE TABLE db_datadict.tb_55 ( c1 TEXT );
+--vertical_results
 eval $my_select1;
 --error ER_TABLEACCESS_DENIED_ERROR
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 # OK, testuser1 has no privs here
 --error ER_TABLEACCESS_DENIED_ERROR
@@ -193,13 +215,17 @@ CREATE TABLE db_datadict.tb_66 ( c1 TEXT
 connection default;
 GRANT ALL ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
 GRANT SELECT ON mysql.user TO 'testuser1'@'localhost';
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo # Switch to connection testuser1
 connection testuser1;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 
 # OK, testuser1 has no privs here
@@ -208,8 +234,10 @@ CREATE TABLE db_datadict.tb_56 ( c1 TEXT
 
 # using 'USE' lets the server read the privileges new, so now the CREATE works
 USE db_datadict;
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 --replace_result $other_engine_type <other_engine_type>
 eval
@@ -221,15 +249,19 @@ ENGINE = $other_engine_type;
 --echo # Switch to connection default
 connection default;
 REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
+--vertical_results
 eval $my_select1;
 eval $my_select2;
+--horizontal_results
 
 --echo # Switch to connection testuser1
 # check for changes
 connection testuser1;
+--vertical_results
 eval $my_select1;
 --error ER_TABLEACCESS_DENIED_ERROR
 eval $my_select2;
+--horizontal_results
 eval $my_show;
 # WORKS, as the existing old privileges are used!
 --replace_result $other_engine_type <other_engine_type>
@@ -273,19 +305,27 @@ DROP DATABASE IF EXISTS db_datadict;
 let $my_select = SELECT * FROM information_schema.user_privileges
 WHERE grantee = '''testuser1''@''localhost''';
 let $my_show = SHOW GRANTS FOR 'testuser1'@'localhost';
+--vertical_results
 eval $my_select;
+--horizontal_results
 --error ER_NONEXISTING_GRANT
 eval $my_show;
 --error 0,ER_CANNOT_USER
 DROP   USER 'testuser1'@'localhost';
 CREATE USER 'testuser1'@'localhost';
+--vertical_results
 eval $my_select;
+--horizontal_results
 eval $my_show;
 GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost';
+--vertical_results
 eval $my_select;
+--horizontal_results
 eval $my_show;
 DROP USER   'testuser1'@'localhost';
+--vertical_results
 eval $my_select;
+--horizontal_results
 --error ER_NONEXISTING_GRANT
 eval $my_show;
 

=== modified file 'mysql-test/suite/perfschema/r/column_privilege.result'
--- a/mysql-test/suite/perfschema/r/column_privilege.result	2010-01-12 01:47:27 +0000
+++ b/mysql-test/suite/perfschema/r/column_privilege.result	2010-08-09 08:32:50 +0000
@@ -1,6 +1,7 @@
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 grant usage on *.* to 'pfs_user_5'@localhost with GRANT OPTION;
 grant SELECT(thread_id, event_id) on performance_schema.EVENTS_WAITS_CURRENT
 to 'pfs_user_5'@localhost;

=== modified file 'mysql-test/suite/perfschema/r/pfs_upgrade.result'
--- a/mysql-test/suite/perfschema/r/pfs_upgrade.result	2010-08-12 14:08:52 +0000
+++ b/mysql-test/suite/perfschema/r/pfs_upgrade.result	2010-09-20 14:17:32 +0000
@@ -25,7 +25,7 @@ ERROR 1050 (42S01) at line 428: Table 'S
 ERROR 1050 (42S01) at line 445: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 461: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 478: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 1118: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1122: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 show tables like "user_table";
 Tables_in_performance_schema (user_table)
@@ -55,7 +55,7 @@ ERROR 1050 (42S01) at line 428: Table 'S
 ERROR 1050 (42S01) at line 445: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 461: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 478: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 1118: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1122: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 show tables like "user_view";
 Tables_in_performance_schema (user_view)
@@ -83,7 +83,7 @@ ERROR 1050 (42S01) at line 428: Table 'S
 ERROR 1050 (42S01) at line 445: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 461: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 478: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 1118: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1122: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 select name from mysql.proc where db='performance_schema';
 name
@@ -111,7 +111,7 @@ ERROR 1050 (42S01) at line 428: Table 'S
 ERROR 1050 (42S01) at line 445: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 461: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 478: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 1118: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1122: Unexpected content found in the performance_schema database.
 FATAL ERROR: Upgrade failed
 select name from mysql.proc where db='performance_schema';
 name
@@ -139,7 +139,7 @@ ERROR 1050 (42S01) at line 428: Table 'S
 ERROR 1050 (42S01) at line 445: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 461: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 478: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 1118: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1122: 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/privilege.result'
--- a/mysql-test/suite/perfschema/r/privilege.result	2010-01-12 01:47:27 +0000
+++ b/mysql-test/suite/perfschema/r/privilege.result	2010-08-09 08:32:50 +0000
@@ -1,6 +1,7 @@
 show grants;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 grant ALL on *.* to 'pfs_user_1'@localhost with GRANT OPTION;
 grant ALL on performance_schema.* to 'pfs_user_2'@localhost
 with GRANT OPTION;

=== modified file 'mysql-test/suite/rpl/r/rpl_do_grant.result'
--- a/mysql-test/suite/rpl/r/rpl_do_grant.result	2010-05-23 20:41:18 +0000
+++ b/mysql-test/suite/rpl/r/rpl_do_grant.result	2010-08-09 08:32:50 +0000
@@ -207,6 +207,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` T
 SHOW GRANTS FOR CURRENT_USER;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 ##############################################################
 ##############################################################
 ### Showing grants for both users: root and user49119 (master)
@@ -217,6 +218,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` T
 SHOW GRANTS FOR CURRENT_USER;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 ##############################################################
 ## This statement will make the revoke fail because root has no
 ## execute grant. However, it will still revoke the grant for
@@ -232,6 +234,7 @@ GRANT USAGE ON *.* TO 'user49119'@'local
 SHOW GRANTS FOR CURRENT_USER;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 ##############################################################
 #############################################################
 ### Showing grants for both users: root and user49119 (slave)
@@ -242,6 +245,7 @@ GRANT USAGE ON *.* TO 'user49119'@'local
 SHOW GRANTS FOR CURRENT_USER;
 Grants for root@localhost
 GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
 ##############################################################
 DROP TABLE t1;
 DROP PROCEDURE p1;

=== modified file 'mysql-test/suite/rpl/r/rpl_ignore_table.result'
--- a/mysql-test/suite/rpl/r/rpl_ignore_table.result	2008-11-13 19:19:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_ignore_table.result	2010-08-09 08:32:50 +0000
@@ -34,6 +34,7 @@ Warnings:
 Warning	1364	Field 'ssl_cipher' doesn't have a default value
 Warning	1364	Field 'x509_issuer' doesn't have a default value
 Warning	1364	Field 'x509_subject' doesn't have a default value
+Warning	1364	Field 'authentication_string' doesn't have a default value
 GRANT SELECT ON *.* TO mysqltest6@localhost;
 GRANT INSERT ON *.* TO mysqltest6@localhost;
 GRANT INSERT ON test.* TO mysqltest6@localhost;

=== modified file 'mysql-test/suite/rpl/r/rpl_stm_000001.result'
--- a/mysql-test/suite/rpl/r/rpl_stm_000001.result	2010-04-28 12:47:49 +0000
+++ b/mysql-test/suite/rpl/r/rpl_stm_000001.result	2010-08-09 08:32:50 +0000
@@ -66,6 +66,7 @@ Warnings:
 Warning	1364	Field 'ssl_cipher' doesn't have a default value
 Warning	1364	Field 'x509_issuer' doesn't have a default value
 Warning	1364	Field 'x509_subject' doesn't have a default value
+Warning	1364	Field 'authentication_string' doesn't have a default value
 select select_priv,user from mysql.user where user = _binary'blafasel2';
 select_priv	user
 N	blafasel2

=== added file 'mysql-test/suite/sys_vars/r/external_user_basic.result'
--- a/mysql-test/suite/sys_vars/r/external_user_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/external_user_basic.result	2010-08-09 08:32:50 +0000
@@ -0,0 +1,3 @@
+SELECT @@SESSION.EXTERNAL_USER FROM DUAL;
+@@SESSION.EXTERNAL_USER
+NULL

=== added file 'mysql-test/suite/sys_vars/r/proxy_user_basic.result'
--- a/mysql-test/suite/sys_vars/r/proxy_user_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/proxy_user_basic.result	2010-08-09 08:32:50 +0000
@@ -0,0 +1,3 @@
+SELECT @@SESSION.PROXY_USER FROM DUAL;
+@@SESSION.PROXY_USER
+NULL

=== added file 'mysql-test/suite/sys_vars/t/external_user_basic.test'
--- a/mysql-test/suite/sys_vars/t/external_user_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/external_user_basic.test	2010-08-09 08:32:50 +0000
@@ -0,0 +1 @@
+SELECT @@SESSION.EXTERNAL_USER FROM DUAL;

=== added file 'mysql-test/suite/sys_vars/t/proxy_user_basic.test'
--- a/mysql-test/suite/sys_vars/t/proxy_user_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/proxy_user_basic.test	2010-08-09 08:32:50 +0000
@@ -0,0 +1 @@
+SELECT @@SESSION.PROXY_USER FROM DUAL;

=== modified file 'mysql-test/t/change_user.test'
--- a/mysql-test/t/change_user.test	2009-12-22 09:35:56 +0000
+++ b/mysql-test/t/change_user.test	2010-08-09 08:32:50 +0000
@@ -1,4 +1,52 @@
 #
+# functional change user tests
+#
+
+grant select on test.* to test_nopw;
+grant select on test.* to test_oldpw identified by password "09301740536db389";
+grant select on test.* to test_newpw identified by "newpw";
+
+select user(), current_user(), database();
+#
+# massaging the data for tests to pass in the embedded server,
+# that has authentication completely disabled.
+#
+
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+   
+
+change_user test_nopw;
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_oldpw, oldpw;
+--replace_result <@> <test_oldpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_newpw, newpw;
+--replace_result <@> <test_newpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user root;
+--replace_result <@> <root@localhost> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+
+change_user test_nopw,,test;
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_oldpw,oldpw,test;
+--replace_result <@> <test_oldpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_newpw,newpw,test;
+--replace_result <@> <test_newpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user root,,test;
+--replace_result <@> <root@localhost> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+
+drop user test_nopw;
+drop user test_oldpw;
+drop user test_newpw;
+
+#
 # Bug#20023 mysql_change_user() resets the value of SQL_BIG_SELECTS
 #
 

=== modified file 'mysql-test/t/grant.test'
--- a/mysql-test/t/grant.test	2010-08-05 12:53:09 +0000
+++ b/mysql-test/t/grant.test	2010-08-16 15:16:07 +0000
@@ -29,7 +29,7 @@ flush privileges;
 grant select on mysqltest.* to mysqltest_1@localhost require cipher "EDH-RSA-DES-CBC3-SHA";
 show grants for mysqltest_1@localhost;
 grant delete on mysqltest.* to mysqltest_1@localhost;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
 show grants for mysqltest_1@localhost;
 revoke delete on mysqltest.* from mysqltest_1@localhost;
 show grants for mysqltest_1@localhost;
@@ -48,10 +48,10 @@ flush privileges;
 delete from mysql.user where user='mysqltest_1';
 flush privileges;
 grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
 show grants for mysqltest_1@localhost;
 grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
 show grants for mysqltest_1@localhost;
 # This is just to double check that one won't ignore results of selects
 flush privileges;

=== modified file 'mysql-test/t/grant2.test'
--- a/mysql-test/t/grant2.test	2009-10-30 05:06:10 +0000
+++ b/mysql-test/t/grant2.test	2010-08-09 08:32:50 +0000
@@ -31,7 +31,7 @@ create user mysqltest_2@localhost;
 connect (user_a,localhost,mysqltest_1,,);
 connection user_a;
 grant select on `my\_1`.* to mysqltest_2@localhost;
---error ER_PASSWORD_NOT_ALLOWED
+--error ER_DBACCESS_DENIED_ERROR
 grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
 disconnect user_a;
 connection default;
@@ -405,7 +405,7 @@ drop database mysqltest_1;
 # But anonymous users can't change their password
 connect (n5,localhost,test,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
 connection n5;
---error ER_DBACCESS_DENIED_ERROR
+--error ER_PASSWORD_NO_MATCH
 set password = password("changed");
 disconnect n5;
 connection default;

=== added file 'mysql-test/t/plugin_auth-master.opt'
--- a/mysql-test/t/plugin_auth-master.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/plugin_auth-master.opt	2010-08-09 08:32:50 +0000
@@ -0,0 +1,2 @@
+$PLUGIN_AUTH_OPT
+$PLUGIN_AUTH_LOAD

=== added file 'mysql-test/t/plugin_auth.test'
--- a/mysql-test/t/plugin_auth.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/plugin_auth.test	2010-09-20 13:51:42 +0000
@@ -0,0 +1,332 @@
+--source include/have_plugin_auth.inc
+--source include/not_embedded.inc
+
+query_vertical SELECT PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_DESCRIPTION
+  FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server';
+
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+
+SELECT plugin,authentication_string FROM mysql.user WHERE User='plug';
+
+--echo ## test plugin auth
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
+connect(plug_con,localhost,plug,plug_dest);
+--enable_query_log
+
+GRANT PROXY ON plug_dest TO plug;
+
+connect(plug_con,localhost,plug,plug_dest);
+
+connection plug_con;
+select USER(),CURRENT_USER();
+
+--echo ## test SET PASSWORD
+#--error ER_SET_PASSWORD_AUTH_PLUGIN
+SET PASSWORD = PASSWORD('plug_dest');
+
+connection default;
+disconnect plug_con;
+
+--echo ## test bad credentials
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR
+connect(plug_con,localhost,plug,bad_credentials);
+--enable_query_log
+
+--echo ## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD
+--disable_result_log
+--disable_query_log
+--error 2059
+connect(plug_con_wrongp,localhost,plug,plug_dest,,,,,wrong_plugin_name);
+--enable_query_log
+--enable_result_log
+
+--echo ## test correct default plugin
+connect(plug_con_rightp,localhost,plug,plug_dest,,,,,auth_test_plugin);
+connection plug_con_rightp;
+select USER(),CURRENT_USER();
+connection default;
+disconnect plug_con_rightp;
+
+--echo ## test no_auto_create_user sql mode with plugin users
+SET @@sql_mode=no_auto_create_user;
+GRANT INSERT ON TEST.* TO grant_user IDENTIFIED WITH 'test_plugin_server';
+SET @@sql_mode=default;
+DROP USER grant_user;
+
+--echo ## test utf-8 user name
+CREATE USER `Ÿ` IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+GRANT PROXY ON plug_dest TO `Ÿ`;
+
+connect(non_ascii,localhost,Ÿ,plug_dest);
+connection non_ascii;
+select USER(),CURRENT_USER();
+
+connection default;
+disconnect non_ascii;
+DROP USER `Ÿ`;
+
+--echo ## test GRANT ... IDENTIFIED WITH/BY ...
+
+CREATE DATABASE test_grant_db;
+
+--echo # create new user via GRANT WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user 
+  IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+GRANT PROXY ON plug_dest TO new_grant_user;  
+
+connect(plug_con_grant,localhost,new_grant_user,plug_dest);
+connection plug_con_grant;
+select USER(),CURRENT_USER();
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+connection default;
+disconnect plug_con_grant;
+REVOKE ALL PRIVILEGES ON test_grant_db.* FROM new_grant_user;
+
+--echo # try re-create existing user via GRANT IDENTIFIED BY
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+  IDENTIFIED BY 'unused_password';
+
+--echo # make sure password doesn't take precendence
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR
+connect(plug_con_grant_deny,localhost,new_grant_user,unused_password);
+--enable_query_log
+
+--echo #make sure plugin auth still available
+connect(plug_con_grant,localhost,new_grant_user,plug_dest);
+connection plug_con_grant;
+select USER(),CURRENT_USER();
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+connection default;
+disconnect plug_con_grant;
+
+DROP USER new_grant_user;
+
+--echo # try re-create existing user via GRANT IDENTIFIED WITH
+
+--error ER_GRANT_PLUGIN_USER_EXISTS
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug
+  IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_GRANT_PLUGIN_USER_EXISTS
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug_dest
+  IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro 
+  INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro 
+  INDENTIFIED BY 'plug_dest_passwd';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro 
+  INDENTIFIED BY PASSWORD 'plug_dest_passwd';
+
+DROP DATABASE test_grant_db;
+
+--echo ## GRANT PROXY tests
+
+CREATE USER grant_plug IDENTIFIED WITH 'test_plugin_server' 
+  AS 'grant_plug_dest';
+CREATE USER grant_plug_dest IDENTIFIED BY 'grant_plug_dest_passwd';
+CREATE USER grant_plug_dest2 IDENTIFIED BY 'grant_plug_dest_passwd2';
+
+--echo # ALL PRIVILEGES doesn't include PROXY
+GRANT ALL PRIVILEGES ON *.* TO grant_plug;
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
+connect(grant_plug_con,localhost,grant_plug,grant_plug_dest);
+--enable_query_log
+
+--error ER_PARSE_ERROR : this should fail : can't combine PROXY
+GRANT ALL PRIVILEGES,PROXY ON grant_plug_dest TO grant_plug;
+
+--echo this should fail : can't combine PROXY
+--error ER_PARSE_ERROR
+GRANT ALL SELECT,PROXY ON grant_plug_dest TO grant_plug;
+
+--echo # this should fail : no such grant
+--error ER_NONEXISTING_GRANT
+REVOKE PROXY ON grant_plug_dest FROM grant_plug;
+
+connect(grant_plug_dest_con,localhost,grant_plug_dest,grant_plug_dest_passwd);
+connection grant_plug_dest_con;
+--echo in grant_plug_dest_con
+
+--echo ## testing what an ordinary user can grant 
+--echo this should fail : no rights to grant all
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON ''@'' TO grant_plug;
+
+--echo this should fail : not the same user
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON grant_plug TO grant_plug_dest;
+
+--echo this should fail : same user, but on a different host
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON grant_plug_dest TO grant_plug;
+
+--echo this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug_dest2;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug_dest2;
+
+--echo this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug WITH GRANT OPTION;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug;
+
+--echo this should fail : can't create users
+--error ER_CANT_CREATE_USER_WITH_GRANT
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug@localhost;
+
+connection default;
+--echo in default connection
+disconnect grant_plug_dest_con;
+
+--echo # test what root can grant
+
+--echo should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO grant_plug;
+REVOKE PROXY ON ''@'' FROM grant_plug;
+
+--echo should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test' 
+  WITH GRANT OPTION;
+
+--echo need USAGE : PROXY doesn't contain it.
+GRANT USAGE on *.* TO proxy_admin;
+
+connect (proxy_admin_con,localhost,proxy_admin,test);
+connection proxy_admin_con;
+--echo in proxy_admin_con;
+
+--echo should work : proxy_admin has proxy to ''@''
+GRANT PROXY ON future_user TO grant_plug;
+
+connection default;
+--echo in default connection
+disconnect proxy_admin_con;
+
+SHOW GRANTS FOR grant_plug;
+REVOKE PROXY ON future_user FROM grant_plug;
+SHOW GRANTS FOR grant_plug;
+
+--echo ## testing drop user
+CREATE USER test_drop@localhost;
+GRANT PROXY ON future_user TO test_drop@localhost;
+SHOW GRANTS FOR test_drop@localhost;
+DROP USER test_drop@localhost;
+SELECT * FROM mysql.proxy_priv WHERE Host = 'test_drop' AND User = 'localhost';
+
+DROP USER proxy_admin;
+
+DROP USER grant_plug,grant_plug_dest,grant_plug_dest2;
+
+--echo ## END GRANT PROXY tests
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+
+--echo ## @@proxy_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+
+SELECT USER(),CURRENT_USER(),@@LOCAL.proxy_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT @@GLOBAL.proxy_user;
+SELECT @@LOCAL.proxy_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET GLOBAL proxy_user = 'test';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET LOCAL proxy_user = 'test';
+SELECT @@LOCAL.proxy_user;
+
+connect(plug_con,localhost,plug,plug_dest);
+connection plug_con;
+--echo # in connection plug_con
+SELECT @@LOCAL.proxy_user;
+connection default;
+--echo # in connection default
+disconnect plug_con;
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+--echo ## END @@proxy_user tests
+
+--echo ## @@external_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.external_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT @@GLOBAL.external_user;
+SELECT @@LOCAL.external_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET GLOBAL external_user = 'test';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET LOCAL external_user = 'test';
+SELECT @@LOCAL.external_user;
+
+connect(plug_con,localhost,plug,plug_dest);
+connection plug_con;
+--echo # in connection plug_con
+SELECT @@LOCAL.external_user;
+connection default;
+--echo # in connection default
+disconnect plug_con;
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+--echo ## END @@external_user tests
+
+--echo #
+--echo #  Bug #56798 : Wrong credentials assigned when using a proxy user.
+--echo #
+
+GRANT ALL PRIVILEGES ON *.* TO power_user;
+GRANT USAGE ON anonymous_db.* TO ''@''
+  IDENTIFIED WITH 'test_plugin_server' AS 'power_user';
+GRANT PROXY ON power_user TO ''@'';
+CREATE DATABASE confidential_db;
+
+connect(plug_con,localhost, test_login_user, power_user, confidential_db);
+SELECT user(),current_user(),@@proxy_user;
+
+connection default;
+disconnect plug_con;
+
+DROP USER power_user;
+DROP USER ''@'';
+DROP DATABASE confidential_db;
+
+--echo # Test case #2 (crash with double grant proxy)
+
+CREATE USER ''@'' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
+CREATE USER standard_user;
+CREATE DATABASE shared;
+GRANT ALL PRIVILEGES ON shared.* TO standard_user;
+GRANT PROXY ON standard_user TO ''@'';
+--echo #should not crash
+GRANT PROXY ON standard_user TO ''@'';
+
+DROP USER ''@'';
+DROP USER standard_user;
+DROP DATABASE shared;

=== modified file 'mysql-test/t/system_mysql_db_fix40123.test'
--- a/mysql-test/t/system_mysql_db_fix40123.test	2009-11-04 12:28:20 +0000
+++ b/mysql-test/t/system_mysql_db_fix40123.test	2010-08-09 08:32:50 +0000
@@ -72,7 +72,7 @@ CREATE TABLE time_zone_leap_second (   T
 -- disable_query_log
 
 # Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
 
 -- enable_query_log
 

=== modified file 'mysql-test/t/system_mysql_db_fix50030.test'
--- a/mysql-test/t/system_mysql_db_fix50030.test	2009-11-04 12:28:20 +0000
+++ b/mysql-test/t/system_mysql_db_fix50030.test	2010-08-09 08:32:50 +0000
@@ -78,7 +78,7 @@ INSERT INTO servers VALUES ('test','loca
 -- disable_query_log
 
 # Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
 
 -- enable_query_log
 

=== modified file 'mysql-test/t/system_mysql_db_fix50117.test'
--- a/mysql-test/t/system_mysql_db_fix50117.test	2009-11-04 12:28:20 +0000
+++ b/mysql-test/t/system_mysql_db_fix50117.test	2010-08-09 08:32:50 +0000
@@ -97,7 +97,7 @@ CREATE TABLE IF NOT EXISTS ndb_binlog_in
 -- disable_query_log
 
 # Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
 
 -- enable_query_log
 

=== modified file 'mysys/sha1.c'
--- a/mysys/sha1.c	2007-01-22 12:10:46 +0000
+++ b/mysys/sha1.c	2010-10-05 14:03:04 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002, 2004, 2006 MySQL AB
+/* Copyright (c) 2002, 2004, 2006 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
@@ -6,26 +6,57 @@
 
  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
+ 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
 
 /*
   Original Source from: http://www.faqs.org/rfcs/rfc3174.html
 
+  Copyright (C) The Internet Society (2001).  All Rights Reserved.
+
+  This document and translations of it may be copied and furnished to
+  others, and derivative works that comment on or otherwise explain it
+  or assist in its implementation may be prepared, copied, published
+  and distributed, in whole or in part, without restriction of any
+  kind, provided that the above copyright notice and this paragraph are
+  included on all such copies and derivative works.  However, this
+  document itself may not be modified in any way, such as by removing
+  the copyright notice or references to the Internet Society or other
+  Internet organizations, except as needed for the purpose of
+  developing Internet standards in which case the procedures for
+  copyrights defined in the Internet Standards process must be
+  followed, or as required to translate it into languages other than
+  English.
+
+  The limited permissions granted above are perpetual and will not be
+  revoked by the Internet Society or its successors or assigns.
+
+  This document and the information contained herein is provided on an
+  "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+  TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+  BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+  HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+  Acknowledgement 
+  Funding for the RFC Editor function is currently provided by the 
+  Internet Society. 
+
  DESCRIPTION
-   This file implements the Secure Hashing Algorithm 1 as
-   defined in FIPS PUB 180-1 published April 17, 1995.
+  This file implements the Secure Hashing Algorithm 1 as
+  defined in FIPS PUB 180-1 published April 17, 1995.
 
-   The SHA-1, produces a 160-bit message digest for a given data
-   stream.  It should take about 2**n steps to find a message with the
-   same digest as a given message and 2**(n/2) to find any two
-   messages with the same digest, when n is the digest size in bits.
-   Therefore, this algorithm can serve as a means of providing a
-   "fingerprint" for a message.
+  The SHA-1, produces a 160-bit message digest for a given data
+  stream.  It should take about 2**n steps to find a message with the
+  same digest as a given message and 2**(n/2) to find any two
+  messages with the same digest, when n is the digest size in bits.
+  Therefore, this algorithm can serve as a means of providing a
+  "fingerprint" for a message.
 
  PORTABILITY ISSUES
    SHA-1 is defined in terms of 32-bit "words".  This code uses

=== added directory 'plugin/auth'
=== added file 'plugin/auth/CMakeLists.txt'
--- a/plugin/auth/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/CMakeLists.txt	2010-08-16 14:34:09 +0000
@@ -0,0 +1,33 @@
+# Copyright (c) 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+
+MYSQL_ADD_PLUGIN(auth dialog.c 
+  MODULE_ONLY)
+MYSQL_ADD_PLUGIN(auth_test_plugin test_plugin.c 
+  MODULE_ONLY)
+
+CHECK_CXX_SOURCE_COMPILES(
+"#define _GNU_SOURCE
+#include <sys/socket.h>
+int main() {
+  struct ucred cred;
+  getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cred, 0);
+}" HAVE_PEERCRED)
+
+IF(HAVE_PEERCRED)
+  MYSQL_ADD_PLUGIN(auth_socket auth_socket.c
+    MODULE_ONLY)
+ENDIF()

=== added file 'plugin/auth/Makefile.am'
--- a/plugin/auth/Makefile.am	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/Makefile.am	2010-08-09 08:32:50 +0000
@@ -0,0 +1,16 @@
+pkgplugindir=$(pkglibdir)/plugin
+
+AM_LDFLAGS=-module -rpath $(pkgplugindir)
+AM_CPPFLAGS=-DMYSQL_DYNAMIC_PLUGIN -Wno-pointer-sign -I$(top_srcdir)/include
+
+pkgplugin_LTLIBRARIES=  auth.la auth_test_plugin.la
+auth_la_SOURCES= dialog.c
+auth_test_plugin_la_SOURCES= test_plugin.c
+
+if HAVE_PEERCRED
+pkgplugin_LTLIBRARIES+= auth_socket.la
+auth_socket_la_SOURCES= auth_socket.c
+endif
+
+EXTRA_DIST= plug.in
+

=== added file 'plugin/auth/auth_socket.c'
--- a/plugin/auth/auth_socket.c	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/auth_socket.c	2010-09-20 16:38:27 +0000
@@ -0,0 +1,94 @@
+/*  Copyright (c) 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+  @file
+
+  socket_peercred authentication plugin.
+
+  Authentication is successful if the connection is done via a unix socket and
+  the owner of the client process matches the user name that was used when
+  connecting to mysqld.
+*/
+#define _GNU_SOURCE /* for struct ucred */
+
+#include <mysql/plugin_auth.h>
+#include <sys/socket.h>
+#include <pwd.h>
+#include <string.h>
+
+static int socket_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+  unsigned char *pkt;
+  MYSQL_PLUGIN_VIO_INFO vio_info;
+  struct ucred cred;
+  socklen_t cred_len= sizeof(cred);
+  struct passwd pwd_buf, *pwd;
+  char buf[1024];
+
+  /* no user name yet ? read the client handshake packet with the user name */
+  if (info->user_name == 0)
+  {
+    if (vio->read_packet(vio, &pkt) < 0)
+      return CR_ERROR;
+  }
+
+  info->password_used= PASSWORD_USED_NO_MENTION;
+
+  vio->info(vio, &vio_info);
+  if (vio_info.protocol != MYSQL_VIO_SOCKET)
+    return CR_ERROR;
+
+  /* get the UID of the client process */
+  if (getsockopt(vio_info.socket, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len))
+    return CR_ERROR;
+
+  if (cred_len != sizeof(cred))
+    return CR_ERROR;
+
+  /* and find the username for this uid */
+  getpwuid_r(cred.uid, &pwd_buf, buf, sizeof(buf), &pwd);
+  if (pwd == NULL)
+    return CR_ERROR;
+
+  /* now it's simple as that */
+  return strcmp(pwd->pw_name, info->user_name) ? CR_ERROR : CR_OK;
+}
+
+static struct st_mysql_auth socket_auth_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  0,
+  socket_auth
+};
+
+mysql_declare_plugin(socket_auth)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &socket_auth_handler,
+  "socket_peercred",
+  "Sergei Golubchik",
+  "Unix Socket based authentication",
+  PLUGIN_LICENSE_GPL,
+  NULL,
+  NULL,
+  0x0100,
+  NULL,
+  NULL,
+  NULL
+}
+mysql_declare_plugin_end;
+

=== added file 'plugin/auth/dialog.c'
--- a/plugin/auth/dialog.c	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/dialog.c	2010-10-04 12:54:41 +0000
@@ -0,0 +1,329 @@
+/*  Copyright (c) 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+  @file
+
+  dialog client authentication plugin with examples
+
+  dialog is a general purpose client authentication plugin, it simply
+  asks the user the question, as provided by the server and reports
+  the answer back to the server. No encryption is involved,
+  the answers are sent in clear text.
+
+  Two examples are provided: two_questions server plugin, that asks
+  the password and an "Are you sure?" question with a reply "yes, of course".
+  It demonstrates the usage of "password" (input is hidden) and "ordinary"
+  (input can be echoed) questions, and how to mark the last question,
+  to avoid an extra roundtrip.
+
+  And three_attempts plugin that gives the user three attempts to enter
+  a correct password. It shows the situation when a number of questions
+  is not known in advance.
+*/
+#if defined (WIN32) && !defined (RTLD_DEFAULT)
+# define RTLD_DEFAULT GetModuleHandle(NULL)
+#endif
+
+#include <my_global.h>
+#include <mysql.h>
+#include <mysql/plugin_auth.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined (_GNU_SOURCE)
+# define _GNU_SOURCE /* for RTLD_DEFAULT */
+#endif
+
+/**
+  first byte of the question string is the question "type".
+  It can be a "ordinary" or a "password" question.
+  The last bit set marks a last question in the authentication exchange.
+*/
+#define ORDINARY_QUESTION       "\2"
+#define LAST_QUESTION           "\3"
+#define PASSWORD_QUESTION       "\4"
+#define LAST_PASSWORD           "\5"
+
+/********************* SERVER SIDE ****************************************/
+
+/**
+  dialog demo with two questions, one password and one, the last, ordinary.
+*/
+static int two_questions(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+  unsigned char *pkt;
+  int pkt_len;
+
+  /* send a password question */
+  if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
+    return CR_ERROR;
+
+  /* read the answer */
+  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+    return CR_ERROR;
+
+  info->password_used= PASSWORD_USED_YES;
+
+  /* fail if the password is wrong */
+  if (strcmp((const char *) pkt, info->auth_string))
+    return CR_ERROR;
+
+  /* send the last, ordinary, question */
+  if (vio->write_packet(vio, (const unsigned char *) LAST_QUESTION "Are you sure ?", 15))
+    return CR_ERROR;
+
+  /* read the answer */
+  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+    return CR_ERROR;
+
+  /* check the reply */
+  return strcmp((const char *) pkt, "yes, of course") ? CR_ERROR : CR_OK;
+}
+
+static struct st_mysql_auth two_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  "dialog", /* requires dialog client plugin */
+  two_questions
+};
+
+/* dialog demo where the number of questions is not known in advance */
+static int three_attempts(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+  unsigned char *pkt;
+  int pkt_len, i;
+
+  for (i= 0; i < 3; i++)
+  {
+    /* send the prompt */
+    if (vio->write_packet(vio, 
+		(const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
+      return CR_ERROR;
+
+    /* read the password */
+    if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+      return CR_ERROR;
+
+    info->password_used= PASSWORD_USED_YES;
+
+    /*
+      finish, if the password is correct.
+      note, that we did not mark the prompt packet as "last"
+    */
+    if (strcmp((const char *) pkt, info->auth_string) == 0)
+      return CR_OK;
+  }
+
+  return CR_ERROR;
+}
+
+static struct st_mysql_auth three_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  "dialog", /* requires dialog client plugin */
+  three_attempts 
+};
+
+mysql_declare_plugin(dialog)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &two_handler,
+  "two_questions",
+  "Sergei Golubchik",
+  "Dialog plugin demo 1",
+  PLUGIN_LICENSE_GPL,
+  NULL,
+  NULL,
+  0x0100,
+  NULL,
+  NULL,
+  NULL
+},
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &three_handler,
+  "three_attempts",
+  "Sergei Golubchik",
+  "Dialog plugin demo 2",
+  PLUGIN_LICENSE_GPL,
+  NULL,
+  NULL,
+  0x0100,
+  NULL,
+  NULL,
+  NULL
+}
+mysql_declare_plugin_end;
+
+/********************* CLIENT SIDE ***************************************/
+/*
+  This plugin performs a dialog with the user, asking questions and
+  reading answers. Depending on the client it may be desirable to do it
+  using GUI, or console, with or without curses, or read answers
+  from a smardcard, for example.
+
+  To support all this variety, the dialog plugin has a callback function
+  "authentication_dialog_ask". If the client has a function of this name
+  dialog plugin will use it for communication with the user. Otherwise
+  a default gets() based implementation will be used.
+*/
+
+/**
+  type of the mysql_authentication_dialog_ask function
+
+  @param mysql          mysql
+  @param type           type of the input
+                        1 - ordinary string input
+                        2 - password string
+  @param prompt         prompt
+  @param buf            a buffer to store the use input
+  @param buf_len        the length of the buffer
+
+  @retval               a pointer to the user input string.
+                        It may be equal to 'buf' or to 'mysql->password'.
+                        In all other cases it is assumed to be an allocated
+                        string, and the "dialog" plugin will free() it.
+*/
+typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql,
+                      int type, const char *prompt, char *buf, int buf_len);
+
+static mysql_authentication_dialog_ask_t ask;
+
+static char *builtin_ask(MYSQL *mysql __attribute__((unused)),
+                         int type __attribute__((unused)),
+                         const char *prompt,
+                         char *buf, int buf_len __attribute__((unused)))
+{
+  fputs(prompt, stdout);
+  fputc(' ', stdout);
+  if (gets(buf) == 0)
+    return 0;
+
+  return buf;
+}
+
+/**
+  The main function of the dialog plugin.
+
+  Read the prompt, ask the question, send the reply, repeat until
+  the server is satisfied.
+
+  @note
+   1. this plugin shows how a client authentication plugin
+      may read a MySQL protocol OK packet internally - which is important
+      where a number of packets is not known in advance.
+   2. the first byte of the prompt is special. it is not
+      shown to the user, but signals whether it is the last question
+      (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0),
+      and whether the input is a password (not echoed).
+   3. the prompt is expected to be sent zero-terminated
+*/
+static int perform_dialog(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  unsigned char *pkt, cmd= 0;
+  int pkt_len, res;
+  char reply_buf[1024], *reply;
+
+  do
+  {
+    /* read the prompt */
+    pkt_len= vio->read_packet(vio, &pkt);
+    if (pkt_len < 0)
+      return CR_ERROR;
+
+    if (pkt == 0)
+    {
+      /*
+        in mysql_change_user() the client sends the first packet, so
+        the first vio->read_packet() does nothing (pkt == 0).
+
+        We send the "password", assuming the client knows what its doing.
+        (in other words, the dialog plugin should be only set as a default
+        authentication plugin on the client if the first question
+        asks for a password - which will be sent in clear text, by the way)
+      */
+      reply= mysql->passwd;
+    }
+    else
+    {
+      cmd= *pkt++;
+
+      /* is it MySQL protocol packet ? */
+      if (cmd == 0 || cmd == 254)
+        return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
+
+      /*
+        asking for a password with an empty prompt means mysql->password
+        otherwise we ask the user and read the reply
+      */
+      if ((cmd >> 1) == 2 && *pkt == 0)
+        reply= mysql->passwd;
+      else
+        reply= ask(mysql, cmd >> 1, (const char *) pkt, 
+				   reply_buf, sizeof(reply_buf));
+      if (!reply)
+        return CR_ERROR;
+    }
+    /* send the reply to the server */
+    res= vio->write_packet(vio, (const unsigned char *) reply, 
+						   strlen(reply)+1);
+
+    if (reply != mysql->passwd && reply != reply_buf)
+      free(reply);
+
+    if (res)
+      return CR_ERROR;
+
+    /* repeat unless it was the last question */
+  } while ((cmd & 1) != 1);
+
+  /* the job of reading the ok/error packet is left to the server */
+  return CR_OK;
+}
+
+/**
+  initialization function of the dialog plugin
+
+  Pick up the client's authentication_dialog_ask() function, if exists,
+  or fall back to the default implementation.
+*/
+
+static int init_dialog(char *unused1   __attribute__((unused)), 
+                       size_t unused2  __attribute__((unused)), 
+                       int unused3     __attribute__((unused)), 
+                       va_list unused4 __attribute__((unused)))
+{
+  void *sym= dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask");
+  ask= sym ? (mysql_authentication_dialog_ask_t) sym : builtin_ask;
+  return 0;
+}
+
+mysql_declare_client_plugin(AUTHENTICATION)
+  "dialog",
+  "Sergei Golubchik",
+  "Dialog Client Authentication Plugin",
+  {0,1,0},
+  "GPL",
+  NULL,
+  init_dialog,
+  NULL,
+  NULL,
+  perform_dialog
+mysql_end_client_plugin;
+

=== added file 'plugin/auth/plug.in'
--- a/plugin/auth/plug.in	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/plug.in	2010-08-09 08:32:50 +0000
@@ -0,0 +1,12 @@
+MYSQL_PLUGIN(auth, [Collection of Authentication Plugins],
+                   [Collection of Authentication Plugins])
+MYSQL_PLUGIN_DYNAMIC(auth, [dialog.la auth_test_plugin.la])
+AC_COMPILE_IFELSE([
+  AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <sys/socket.h>
+]],[
+  struct ucred cred;
+  getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cred, 0);
+])],have_peercred=yes)
+AM_CONDITIONAL(HAVE_PEERCRED, test x$have_peercred = xyes)

=== added file 'plugin/auth/test_plugin.c'
--- a/plugin/auth/test_plugin.c	1970-01-01 00:00:00 +0000
+++ b/plugin/auth/test_plugin.c	2010-10-04 12:54:41 +0000
@@ -0,0 +1,205 @@
+/*  Copyright (c) 2010, 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+  @file
+
+  dialog client authentication plugin with examples
+
+  dialog is a general purpose client authentication plugin, it simply
+  asks the user the question, as provided by the server and reports
+  the answer back to the server. No encryption is involved,
+  the answers are sent in clear text.
+
+  Two examples are provided: two_questions server plugin, that asks
+  the password and an "Are you sure?" question with a reply "yes, of course".
+  It demonstrates the usage of "password" (input is hidden) and "ordinary"
+  (input can be echoed) questions, and how to mark the last question,
+  to avoid an extra roundtrip.
+
+  And three_attempts plugin that gives the user three attempts to enter
+  a correct password. It shows the situation when a number of questions
+  is not known in advance.
+*/
+
+#include <my_global.h>
+#include <mysql/plugin_auth.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+  first byte of the question string is the question "type".
+  It can be a "ordinary" or a "password" question.
+  The last bit set marks a last question in the authentication exchange.
+*/
+#define ORDINARY_QUESTION       "\2"
+#define LAST_QUESTION           "\3"
+#define LAST_PASSWORD           "\4"
+#define PASSWORD_QUESTION       "\5"
+
+/********************* SERVER SIDE ****************************************/
+
+/**
+  dialog test plugin mimicing the ordinary auth mechanism. Used to test the auth plugin API
+*/
+static int auth_test_plugin(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+  unsigned char *pkt;
+  int pkt_len;
+
+  /* send a password question */
+  if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION, 1))
+    return CR_ERROR;
+
+  /* read the answer */
+  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+    return CR_ERROR;
+
+  info->password_used= PASSWORD_USED_YES;
+
+  /* fail if the password is wrong */
+  if (strcmp((const char *) pkt, info->auth_string))
+    return CR_ERROR;
+
+  /* copy auth string as a destination name to check it */
+  strcpy (info->authenticated_as, info->auth_string);
+
+  /* copy something into the external user name */
+  strcpy (info->external_user, info->auth_string);
+
+  return CR_OK;
+}
+
+static struct st_mysql_auth auth_test_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  "auth_test_plugin", /* requires test_plugin client's plugin */
+  auth_test_plugin
+};
+
+mysql_declare_plugin(test_plugin)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &auth_test_handler,
+  "test_plugin_server",
+  "Georgi Kodinov",
+  "plugin API test plugin",
+  PLUGIN_LICENSE_GPL,
+  NULL,
+  NULL,
+  0x0100,
+  NULL,
+  NULL,
+  NULL
+}
+mysql_declare_plugin_end;
+
+/********************* CLIENT SIDE ***************************************/
+/*
+  client plugin used for testing the plugin API
+*/
+#include <mysql.h>
+
+/**
+  The main function of the test plugin.
+
+  Reads the prompt, check if the handshake is done and if the prompt is a
+  password request and returns the password. Otherwise return error.
+
+  @note
+   1. this plugin shows how a client authentication plugin
+      may read a MySQL protocol OK packet internally - which is important
+      where a number of packets is not known in advance.
+   2. the first byte of the prompt is special. it is not
+      shown to the user, but signals whether it is the last question
+      (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0),
+      and whether the input is a password (not echoed).
+   3. the prompt is expected to be sent zero-terminated
+*/
+static int test_plugin_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  unsigned char *pkt, cmd= 0;
+  int pkt_len, res;
+  char *reply;
+
+  do
+  {
+    /* read the prompt */
+    pkt_len= vio->read_packet(vio, &pkt);
+    if (pkt_len < 0)
+      return CR_ERROR;
+
+    if (pkt == 0)
+    {
+      /*
+        in mysql_change_user() the client sends the first packet, so
+        the first vio->read_packet() does nothing (pkt == 0).
+
+        We send the "password", assuming the client knows what its doing.
+        (in other words, the dialog plugin should be only set as a default
+        authentication plugin on the client if the first question
+        asks for a password - which will be sent in cleat text, by the way)
+      */
+      reply= mysql->passwd;
+    }
+    else
+    {
+      cmd= *pkt++;
+
+      /* is it MySQL protocol (0=OK or 254=need old password) packet ? */
+      if (cmd == 0 || cmd == 254)
+        return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
+
+      /*
+        asking for a password with an empty prompt means mysql->password
+        otherwise return an error
+      */
+      if ((cmd == LAST_PASSWORD[0] || cmd == PASSWORD_QUESTION[0]) && *pkt == 0)
+        reply= mysql->passwd;
+      else
+        return CR_ERROR;
+    }
+    if (!reply)
+      return CR_ERROR;
+    /* send the reply to the server */
+    res= vio->write_packet(vio, (const unsigned char *) reply, 
+                           strlen(reply) + 1);
+
+    if (res)
+      return CR_ERROR;
+
+    /* repeat unless it was the last question */
+  } while (cmd != LAST_QUESTION[0] && cmd != PASSWORD_QUESTION[0]);
+
+  /* the job of reading the ok/error packet is left to the server */
+  return CR_OK;
+}
+
+
+mysql_declare_client_plugin(AUTHENTICATION)
+  "auth_test_plugin",
+  "Georgi Kodinov",
+  "Dialog Client Authentication Plugin",
+  {0,1,0},
+  "GPL",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  test_plugin_client
+mysql_end_client_plugin;

=== modified file 'scripts/CMakeLists.txt'
--- a/scripts/CMakeLists.txt	2010-09-07 15:05:16 +0000
+++ b/scripts/CMakeLists.txt	2010-10-04 19:25:53 +0000
@@ -81,12 +81,9 @@ INSTALL(FILES 
 )
 
 # TCMalloc hacks
-IF($ENV{MALLOC_LIB})
-  SET(MALLOC_LIB $ENV{MALLOC_LIB} CACHE STRING "malloc library")
-ENDIF()
-
 IF(MALLOC_LIB)
-  INSTALL(FILES ${MALLOC_LIB} DESTINATION lib OPTIONAL)
+  MESSAGE("Using tcmalloc '${MALLOC_LIB}'")
+  INSTALL(FILES ${MALLOC_LIB} DESTINATION ${INSTALL_LIBDIR} OPTIONAL)
 ENDIF()
 
 IF(CMAKE_GENERATOR MATCHES "Makefiles")

=== modified file 'scripts/mysql_system_tables.sql'
--- a/scripts/mysql_system_tables.sql	2010-08-12 14:08:52 +0000
+++ b/scripts/mysql_system_tables.sql	2010-09-20 14:17:32 +0000
@@ -28,7 +28,7 @@ set @had_db_table= @@warning_count != 0;
 CREATE TABLE IF NOT EXISTS host (  Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges;  Merged with database privileges';
 
 
-CREATE TABLE IF NOT EXISTS user (   Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0  NOT NULL, max_updates int(11) unsigned DEFAULT 0  NOT NULL, max_connections int(11) unsigned DEFAULT 0  NOT NULL, max_user_connections int(11) unsigned DEFAULT 0  NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+CREATE TABLE IF NOT EXISTS user (   Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0  NOT NULL, max_updates int(11) unsigned DEFAULT 0  NOT NULL, max_connections int(11) unsigned DEFAULT 0  NOT NULL, max_user_connections int(11) unsigned DEFAULT 0  NOT NULL, plugin char(60) DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
 
 -- Remember for later if user table already existed
 set @had_user_table= @@warning_count != 0;
@@ -478,3 +478,7 @@ PREPARE stmt FROM @str;
 EXECUTE stmt;
 DROP PREPARE stmt;
 
+CREATE TABLE IF NOT EXISTS proxy_priv (Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Proxied_Host char(16) binary DEFAULT '' NOT NULL, Proxied_User char(60) binary DEFAULT '' NOT NULL, With_Grant BOOL DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User,Proxied_Host,Proxied_User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User proxy privileges';
+
+-- Remember for later if proxy_priv table already existed
+set @had_proxy_priv_table= @@warning_count != 0;

=== modified file 'scripts/mysql_system_tables_data.sql'
--- a/scripts/mysql_system_tables_data.sql	2009-11-25 10:53:23 +0000
+++ b/scripts/mysql_system_tables_data.sql	2010-08-09 08:32:50 +0000
@@ -21,11 +21,17 @@ DROP TABLE tmp_db;
 -- from local machine if "users" table didn't exist before
 CREATE TEMPORARY TABLE tmp_user LIKE user;
 set @current_hostname= @@hostname;
-INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
-REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
 INSERT INTO tmp_user (host,user) VALUES ('localhost','');
 INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
 INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
 DROP TABLE tmp_user;
+
+CREATE TEMPORARY TABLE tmp_proxy_priv LIKE proxy_priv;
+INSERT INTO tmp_proxy_priv VALUES ('localhost', 'root', '', '', TRUE);
+REPLACE INTO tmp_proxy_priv SELECT @current_hostname, 'root', '', '', TRUE FROM DUAL WHERE LOWER (@current_hostname) != 'localhost';
+INSERT INTO  proxy_priv SELECT * FROM tmp_proxy_priv WHERE @had_proxy_priv_table=0;
+DROP TABLE tmp_proxy_priv;

=== modified file 'scripts/mysql_system_tables_fix.sql'
--- a/scripts/mysql_system_tables_fix.sql	2010-07-05 10:22:13 +0000
+++ b/scripts/mysql_system_tables_fix.sql	2010-08-09 08:32:50 +0000
@@ -640,6 +640,11 @@ DROP PREPARE stmt;
 
 drop procedure mysql.die;
 
+ALTER TABLE user ADD plugin char(60) DEFAULT '' NOT NULL,  ADD authentication_string TEXT NOT NULL;
+ALTER TABLE user MODIFY plugin char(60) DEFAULT '' NOT NULL;
+
+CREATE TABLE IF NOT EXISTS proxy_priv (Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Proxied_User char(60) binary DEFAULT '' NOT NULL, Proxied_Host char(16) binary DEFAULT '' NOT NULL, With_Grant BOOL DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User,Proxied_Host,Proxied_User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+
 # Activate the new, possible modified privilege tables
 # This should not be needed, but gives us some extra testing that the above
 # changes was correct

=== modified file 'sql-common/Makefile.am'
--- a/sql-common/Makefile.am	2010-05-19 13:00:23 +0000
+++ b/sql-common/Makefile.am	2010-08-09 08:32:50 +0000
@@ -14,4 +14,4 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 ## Process this file with automake to create Makefile.in
-EXTRA_DIST = client.c pack.c my_time.c my_user.c
+EXTRA_DIST = client.c pack.c my_time.c my_user.c client_plugin.c

=== modified file 'sql-common/client.c'
--- a/sql-common/client.c	2010-07-15 11:16:06 +0000
+++ b/sql-common/client.c	2010-10-04 12:54:41 +0000
@@ -111,6 +111,10 @@ my_bool	net_flush(NET *net);
 
 #include "client_settings.h"
 #include <sql_common.h>
+#include <mysql/client_plugin.h>
+#define native_password_plugin_name "mysql_native_password"
+#define old_password_plugin_name    "mysql_old_password"
+
 
 uint		mysql_port=0;
 char		*mysql_unix_port= 0;
@@ -373,7 +377,7 @@ void net_clear_error(NET *net)
   @param ...       variable number of arguments
 */
 
-static void set_mysql_extended_error(MYSQL *mysql, int errcode,
+void set_mysql_extended_error(MYSQL *mysql, int errcode,
                                      const char *sqlstate,
                                      const char *format, ...)
 {
@@ -1145,9 +1149,20 @@ static const char *default_options[]=
   "connect-timeout", "local-infile", "disable-local-infile",
   "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
   "multi-results", "multi-statements", "multi-queries", "secure-auth",
-  "report-data-truncation",
+  "report-data-truncation", "plugin-dir", "default-auth",
   NullS
 };
+enum option_id {
+  OPT_port=1, OPT_socket, OPT_compress, OPT_password, OPT_pipe, OPT_timeout, OPT_user, 
+  OPT_init_command, OPT_host, OPT_database, OPT_debug, OPT_return_found_rows, 
+  OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath, 
+  OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout, 
+  OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile, 
+  OPT_replication_probe, OPT_enable_reads_from_master, OPT_repl_parse_query,
+  OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, 
+  OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, 
+  OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, 
+};
 
 static TYPELIB option_types={array_elements(default_options)-1,
 			     "options",default_options, NULL};
@@ -1178,6 +1193,15 @@ static int add_init_command(struct st_my
   return 0;
 }
 
+#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= my_strdup((STR), MYF(MY_WME));
+
 void mysql_read_default_options(struct st_mysql_options *options,
 				const char *filename,const char *group)
 {
@@ -1212,121 +1236,121 @@ void mysql_read_default_options(struct s
 	for (end= *option ; *(end= strcend(end,'_')) ; )
 	  *end= '-';
 	switch (find_type(*option+2,&option_types,2)) {
-	case 1:				/* port */
+	case OPT_port:
 	  if (opt_arg)
 	    options->port=atoi(opt_arg);
 	  break;
-	case 2:				/* socket */
+	case OPT_socket:
 	  if (opt_arg)
 	  {
 	    my_free(options->unix_socket);
 	    options->unix_socket=my_strdup(opt_arg,MYF(MY_WME));
 	  }
 	  break;
-	case 3:				/* compress */
+	case OPT_compress:
 	  options->compress=1;
 	  options->client_flag|= CLIENT_COMPRESS;
 	  break;
-	case 4:				/* password */
+        case OPT_password:
 	  if (opt_arg)
 	  {
 	    my_free(options->password);
 	    options->password=my_strdup(opt_arg,MYF(MY_WME));
 	  }
 	  break;
-        case 5:
+        case OPT_pipe:
           options->protocol = MYSQL_PROTOCOL_PIPE;
-	case 20:			/* connect_timeout */
-	case 6:				/* timeout */
+	case OPT_connect_timeout:
+	case OPT_timeout:
 	  if (opt_arg)
 	    options->connect_timeout=atoi(opt_arg);
 	  break;
-	case 7:				/* user */
+	case OPT_user:
 	  if (opt_arg)
 	  {
 	    my_free(options->user);
 	    options->user=my_strdup(opt_arg,MYF(MY_WME));
 	  }
 	  break;
-	case 8:				/* init-command */
+	case OPT_init_command:
 	  add_init_command(options,opt_arg);
 	  break;
-	case 9:				/* host */
+	case OPT_host:
 	  if (opt_arg)
 	  {
 	    my_free(options->host);
 	    options->host=my_strdup(opt_arg,MYF(MY_WME));
 	  }
 	  break;
-	case 10:			/* database */
+	case OPT_database:
 	  if (opt_arg)
 	  {
 	    my_free(options->db);
 	    options->db=my_strdup(opt_arg,MYF(MY_WME));
 	  }
 	  break;
-	case 11:			/* debug */
+	case OPT_debug:
 #ifdef MYSQL_CLIENT
 	  mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace");
 	  break;
 #endif
-	case 12:			/* return-found-rows */
+	case OPT_return_found_rows:
 	  options->client_flag|=CLIENT_FOUND_ROWS;
 	  break;
 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-	case 13:			/* ssl_key */
+	case OPT_ssl_key:
 	  my_free(options->ssl_key);
           options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
           break;
-	case 14:			/* ssl_cert */
+	case OPT_ssl_cert:
 	  my_free(options->ssl_cert);
           options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME));
           break;
-	case 15:			/* ssl_ca */
+	case OPT_ssl_ca:
 	  my_free(options->ssl_ca);
           options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME));
           break;
-	case 16:			/* ssl_capath */
+	case OPT_ssl_capath:
 	  my_free(options->ssl_capath);
           options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME));
           break;
-        case 23:			/* ssl_cipher */
+        case OPT_ssl_cipher:
           my_free(options->ssl_cipher);
           options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME));
           break;
 #else
-	case 13:				/* Ignore SSL options */
-	case 14:
-	case 15:
-	case 16:
-        case 23:
+	case OPT_ssl_key:
+	case OPT_ssl_cert:
+	case OPT_ssl_ca:
+	case OPT_ssl_capath:
+        case OPT_ssl_cipher:
 	  break;
 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
-	case 17:			/* charset-lib */
+	case OPT_character_sets_dir:
 	  my_free(options->charset_dir);
           options->charset_dir = my_strdup(opt_arg, MYF(MY_WME));
 	  break;
-	case 18:
+	case OPT_default_character_set:
 	  my_free(options->charset_name);
           options->charset_name = my_strdup(opt_arg, MYF(MY_WME));
 	  break;
-	case 19:				/* Interactive-timeout */
+	case OPT_interactive_timeout:
 	  options->client_flag|= CLIENT_INTERACTIVE;
 	  break;
-	case 21:
+	case OPT_local_infile:
 	  if (!opt_arg || atoi(opt_arg) != 0)
 	    options->client_flag|= CLIENT_LOCAL_FILES;
 	  else
 	    options->client_flag&= ~CLIENT_LOCAL_FILES;
 	  break;
-	case 22:
+	case OPT_disable_local_infile:
 	  options->client_flag&= ~CLIENT_LOCAL_FILES;
           break;
-	case 24: /* max-allowed-packet */
+	case OPT_max_allowed_packet:
           if (opt_arg)
 	    options->max_allowed_packet= atoi(opt_arg);
 	  break;
-        case 25: /* protocol */
+        case OPT_protocol:
           if ((options->protocol= find_type(opt_arg,
 					    &sql_protocol_typelib,0)) <= 0)
           {
@@ -1334,26 +1358,44 @@ void mysql_read_default_options(struct s
             exit(1);
           }
           break;
-        case 26: /* shared_memory_base_name */
+        case OPT_shared_memory_base_name:
 #ifdef HAVE_SMEM
           if (options->shared_memory_base_name != def_shared_memory_base_name)
             my_free(options->shared_memory_base_name);
           options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME));
 #endif
           break;
-	case 27: /* multi-results */
+	case OPT_multi_results:
 	  options->client_flag|= CLIENT_MULTI_RESULTS;
 	  break;
-	case 28: /* multi-statements */
-	case 29: /* multi-queries */
+	case OPT_multi_statements:
+	case OPT_multi_queries:
 	  options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
 	  break;
-        case 30: /* secure-auth */
+        case OPT_secure_auth:
           options->secure_auth= TRUE;
           break;
-        case 31: /* report-data-truncation */
+        case OPT_report_data_truncation:
           options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1;
           break;
+        case OPT_plugin_dir:
+          {
+            char buff[FN_REFLEN], buff2[FN_REFLEN];
+            if (strlen(opt_arg) >= FN_REFLEN)
+              opt_arg[FN_REFLEN]= '\0';
+            if (my_realpath(buff, opt_arg, 0))
+            {
+              DBUG_PRINT("warning",("failed to normalize the plugin path: %s",
+                                    opt_arg));
+              break;
+            }
+            convert_dirname(buff, buff2, NULL);
+            EXTENSION_SET_STRING(options, plugin_dir, buff2);
+          }
+          break;
+        case OPT_default_auth:
+          EXTENSION_SET_STRING(options, default_auth, opt_arg);
+          break;
 	default:
 	  DBUG_PRINT("warning",("unknown option: %s",option[0]));
 	}
@@ -1884,6 +1926,11 @@ static int ssl_verify_server_cert(Vio *v
 static my_bool cli_read_query_result(MYSQL *mysql);
 static MYSQL_RES *cli_use_result(MYSQL *mysql);
 
+int cli_read_change_user_result(MYSQL *mysql)
+{
+  return cli_safe_read(mysql);
+}
+
 static MYSQL_METHODS client_methods=
 {
   cli_read_query_result,                       /* read_query_result */
@@ -1891,7 +1938,8 @@ static MYSQL_METHODS client_methods=
   cli_read_rows,                               /* read_rows */
   cli_use_result,                              /* use_result */
   cli_fetch_lengths,                           /* fetch_lengths */
-  cli_flush_use_result                         /* flush_use_result */
+  cli_flush_use_result,                        /* flush_use_result */
+  cli_read_change_user_result                  /* read_change_user_result */
 #ifndef MYSQL_SERVER
   ,cli_list_fields,                            /* list_fields */
   cli_read_prepare_result,                     /* read_prepare_result */
@@ -1901,7 +1949,6 @@ static MYSQL_METHODS client_methods=
   NULL,                                        /* free_embedded_thd */
   cli_read_statistics,                         /* read_statistics */
   cli_read_query_result,                       /* next_result */
-  cli_read_change_user_result,                 /* read_change_user_result */
   cli_read_binary_rows                         /* read_rows_from_cursor */
 #endif
 };
@@ -2221,6 +2268,654 @@ int mysql_init_character_set(MYSQL *mysq
 }
 C_MODE_END
 
+/*********** client side authentication support **************************/
+
+typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
+static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int);
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+
+static auth_plugin_t native_password_client_plugin=
+{
+  MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+  MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+  native_password_plugin_name,
+  "R.J.Silk, Sergei Golubchik",
+  "Native MySQL authentication",
+  {1, 0, 0},
+  "GPL",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  native_password_auth_client
+};
+
+static auth_plugin_t old_password_client_plugin=
+{
+  MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+  MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+  old_password_plugin_name,
+  "R.J.Silk, Sergei Golubchik",
+  "Old MySQL-3.23 authentication",
+  {1, 0, 0},
+  "GPL",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  old_password_auth_client
+};
+
+struct st_mysql_client_plugin *mysql_client_builtins[]=
+{
+  (struct st_mysql_client_plugin *)&native_password_client_plugin,
+  (struct st_mysql_client_plugin *)&old_password_client_plugin,
+  0
+};
+
+
+
+/* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */
+typedef struct {
+  int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
+  int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, int pkt_len);
+  void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+  /* -= end of MYSQL_PLUGIN_VIO =- */
+  MYSQL *mysql;
+  auth_plugin_t *plugin;            /**< what plugin we're under */
+  const char *db;
+  struct {
+    uchar *pkt;                     /**< pointer into NET::buff */
+    uint pkt_len;
+  } cached_server_reply;
+  int packets_read, packets_written; /**< counters for send/received packets */
+  int mysql_change_user;            /**< if it's mysql_change_user() */
+  int last_read_packet_len;         /**< the length of the last *read* packet */
+} MCPVIO_EXT;
+
+/**
+  sends a COM_CHANGE_USER command with a caller provided payload
+
+  Packet format:
+   
+    Bytes       Content
+    -----       ----
+    n           user name - \0-terminated string
+    n           password
+                  3.23 scramble - \0-terminated string (9 bytes)
+                  otherwise - length (1 byte) coded
+    n           database name - \0-terminated string
+    2           character set number (if the server >= 4.1.x)
+    n           client auth plugin name - \0-terminated string,
+                  (if the server supports plugin auth)
+
+  @retval 0 ok
+  @retval 1 error
+*/
+static int send_change_user_packet(MCPVIO_EXT *mpvio,
+                                   const uchar *data, int data_len)
+{
+  MYSQL *mysql= mpvio->mysql;
+  char *buff, *end;
+  int res= 1;
+
+  buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN);
+
+  end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
+
+  if (!data_len)
+    *end++= 0;
+  else
+  {
+    if (mysql->client_flag & CLIENT_SECURE_CONNECTION)
+    {
+      DBUG_ASSERT(data_len <= 255);
+      if (data_len > 255)
+      {
+        set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
+        goto error;
+      }
+      *end++= data_len;
+    }
+    else
+    {
+      DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1);
+      DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0);
+    }
+    memcpy(end, data, data_len);
+    end+= data_len;
+  }
+  end= strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;
+
+  if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
+  {
+    int2store(end, (ushort) mysql->charset->number);
+    end+= 2;
+  }
+
+  if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+    end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+  res= simple_command(mysql, COM_CHANGE_USER,
+                      (uchar*)buff, (ulong)(end-buff), 1);
+
+error:
+  my_afree(buff);
+  return res;
+}
+
+/**
+  sends a client authentication packet (second packet in the 3-way handshake)
+
+  Packet format (when the server is 4.0 or earlier):
+   
+    Bytes       Content
+    -----       ----
+    2           client capabilities
+    3           max packet size
+    n           user name, \0-terminated
+    9           scramble_323, \0-terminated
+
+  Packet format (when the server is 4.1 or newer):
+   
+    Bytes       Content
+    -----       ----
+    4           client capabilities
+    4           max packet size
+    1           charset number
+    23          reserved (always 0)
+    n           user name, \0-terminated
+    n           plugin auth data (e.g. scramble), length (1 byte) coded
+    n           database name, \0-terminated
+                (if CLIENT_CONNECT_WITH_DB is set in the capabilities)
+    n           client auth plugin name - \0-terminated string,
+                (if CLIENT_PLUGIN_AUTH is set in the capabilities)
+
+  @retval 0 ok
+  @retval 1 error
+*/
+static int send_client_reply_packet(MCPVIO_EXT *mpvio,
+                                    const uchar *data, int data_len)
+{
+  MYSQL *mysql= mpvio->mysql;
+  NET *net= &mysql->net;
+  char *buff, *end;
+
+  /* see end= buff+32 below, fixed size of the packet is 32 bytes */
+  buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN);
+  
+  mysql->client_flag|= mysql->options.client_flag;
+  mysql->client_flag|= CLIENT_CAPABILITIES;
+
+  if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
+    mysql->client_flag|= CLIENT_MULTI_RESULTS;
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+  if (mysql->options.ssl_key || mysql->options.ssl_cert ||
+      mysql->options.ssl_ca || mysql->options.ssl_capath ||
+      mysql->options.ssl_cipher)
+    mysql->options.use_ssl= 1;
+  if (mysql->options.use_ssl)
+    mysql->client_flag|= CLIENT_SSL;
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
+  if (mpvio->db)
+    mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
+
+  /* Remove options that server doesn't support */
+  mysql->client_flag= mysql->client_flag &
+                       (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) 
+                       | mysql->server_capabilities);
+
+#ifndef HAVE_COMPRESS
+  mysql->client_flag&= ~CLIENT_COMPRESS;
+#endif
+
+  if (mysql->client_flag & CLIENT_PROTOCOL_41)
+  {
+    /* 4.1 server and 4.1 client has a 32 byte option flag */
+    int4store(buff,mysql->client_flag);
+    int4store(buff+4, net->max_packet_size);
+    buff[8]= (char) mysql->charset->number;
+    bzero(buff+9, 32-9);
+    end= buff+32;
+  }
+  else
+  {
+    int2store(buff, mysql->client_flag);
+    int3store(buff+2, net->max_packet_size);
+    end= buff+5;
+  }
+#ifdef HAVE_OPENSSL
+  if (mysql->client_flag & CLIENT_SSL)
+  {
+    /* Do the SSL layering. */
+    struct st_mysql_options *options= &mysql->options;
+    struct st_VioSSLFd *ssl_fd;
+
+    /*
+      Send mysql->client_flag, max_packet_size - unencrypted otherwise
+      the server does not know we want to do SSL
+    */
+    if (my_net_write(net, (uchar*)buff, (size_t) (end-buff)) || net_flush(net))
+    {
+      set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+                               ER(CR_SERVER_LOST_EXTENDED),
+                               "sending connection information to server",
+                               errno);
+      goto error;
+    }
+
+    /* Create the VioSSLConnectorFd - init SSL and load certs */
+    if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
+                                        options->ssl_cert,
+                                        options->ssl_ca,
+                                        options->ssl_capath,
+                                        options->ssl_cipher)))
+    {
+      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+      goto error;
+    }
+    mysql->connector_fd= (unsigned char *) ssl_fd;
+
+    /* Connect to the server */
+    DBUG_PRINT("info", ("IO layer change in progress..."));
+    if (sslconnect(ssl_fd, net->vio,
+                   (long) (mysql->options.connect_timeout)))
+    {    
+      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+      goto error;
+    }    
+    DBUG_PRINT("info", ("IO layer change done!"));
+
+    /* Verify server cert */
+    if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
+        ssl_verify_server_cert(net->vio, mysql->host))
+    {
+      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+      goto error;
+    }
+  }
+#endif /* HAVE_OPENSSL */
+
+  DBUG_PRINT("info",("Server version = '%s'  capabilites: %lu  status: %u  client_flag: %lu",
+		     mysql->server_version, mysql->server_capabilities,
+		     mysql->server_status, mysql->client_flag));
+
+  compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+  /* This needs to be changed as it's not useful with big packets */
+  if (mysql->user[0])
+    strmake(end, mysql->user, USERNAME_LENGTH);
+  else
+    read_user_name(end);
+
+  /* We have to handle different version of handshake here */
+  DBUG_PRINT("info",("user: %s",end));
+  end= strend(end) + 1;
+  if (data_len)
+  {
+    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+    {
+      *end++= data_len;
+      memcpy(end, data, data_len);
+      end+= data_len;
+    }
+    else
+    {
+      DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */
+      memcpy(end, data, data_len);
+      end+= data_len;
+    }
+  }
+  else
+    *end++= 0;
+
+  /* Add database if needed */
+  if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
+  {
+    end= strmake(end, mpvio->db, NAME_LEN) + 1;
+    mysql->db= my_strdup(mpvio->db, MYF(MY_WME));
+  }
+
+  if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+    end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+  /* Write authentication package */
+  if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
+  {
+    set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+                             ER(CR_SERVER_LOST_EXTENDED),
+                             "sending authentication information",
+                             errno);
+    goto error;
+  }
+  my_afree(buff);
+  return 0;
+  
+error:
+  my_afree(buff);
+  return 1;
+}
+
+/**
+  vio->read_packet() callback method for client authentication plugins
+
+  This function is called by a client authentication plugin, when it wants
+  to read data from the server.
+*/
+static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf)
+{
+  MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+  MYSQL *mysql= mpvio->mysql;
+  ulong  pkt_len;
+
+  /* there are cached data left, feed it to a plugin */
+  if (mpvio->cached_server_reply.pkt)
+  {
+    *buf= mpvio->cached_server_reply.pkt;
+    mpvio->cached_server_reply.pkt= 0;
+    mpvio->packets_read++;
+    return mpvio->cached_server_reply.pkt_len;
+  }
+
+  if (mpvio->packets_read == 0)
+  {
+    /*
+      the server handshake packet came from the wrong plugin,
+      or it's mysql_change_user(). Either way, there is no data
+      for a plugin to read. send a dummy packet to the server
+      to initiate a dialog.
+    */
+    if (client_mpvio_write_packet(mpv, 0, 0))
+      return (int)packet_error;
+  }
+
+  /* otherwise read the data */
+  pkt_len= (*mysql->methods->read_change_user_result)(mysql);
+  mpvio->last_read_packet_len= pkt_len;
+  *buf= mysql->net.read_pos;
+
+  /* was it a request to change plugins ? */
+  if (**buf == 254)
+    return (int)packet_error; /* if yes, this plugin shan't continue */
+
+  /*
+    the server sends \1\255 or \1\254 instead of just \255 or \254 -
+    for us to not confuse it with an error or "change plugin" packets.
+    We remove this escaping \1 here.
+
+    See also server_mpvio_write_packet() where the escaping is done.
+  */
+  if (pkt_len && **buf == 1)
+  {
+    (*buf)++;
+    pkt_len--;
+  }
+  mpvio->packets_read++;
+  return pkt_len;
+}
+
+/**
+  vio->write_packet() callback method for client authentication plugins
+
+  This function is called by a client authentication plugin, when it wants
+  to send data to the server.
+
+  It transparently wraps the data into a change user or authentication
+  handshake packet, if neccessary.
+*/
+static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
+                                     const uchar *pkt, int pkt_len)
+{
+  int res;
+  MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+
+  if (mpvio->packets_written == 0)
+  {
+    if (mpvio->mysql_change_user)
+      res= send_change_user_packet(mpvio, pkt, pkt_len);
+    else
+      res= send_client_reply_packet(mpvio, pkt, pkt_len);
+  }
+  else
+  {
+    NET *net= &mpvio->mysql->net;
+    if (mpvio->mysql->thd)
+      res= 1; /* no chit-chat in embedded */
+    else
+      res= my_net_write(net, pkt, pkt_len) || net_flush(net);
+    if (res)
+      set_mysql_extended_error(mpvio->mysql, CR_SERVER_LOST, unknown_sqlstate,
+                               ER(CR_SERVER_LOST_EXTENDED),
+                               "sending authentication information",
+                               errno);
+  }
+  mpvio->packets_written++;
+  return res;
+}
+
+/**
+  fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+  connection
+*/
+void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
+{
+  bzero(info, sizeof(*info));
+  switch (vio->type) {
+  case VIO_TYPE_TCPIP:
+    info->protocol= MYSQL_VIO_TCP;
+    info->socket= vio->sd;
+    return;
+  case VIO_TYPE_SOCKET:
+    info->protocol= MYSQL_VIO_SOCKET;
+    info->socket= vio->sd;
+    return;
+  case VIO_TYPE_SSL:
+    {
+      struct sockaddr addr;
+      socklen_t addrlen= sizeof(addr);
+      if (getsockname(vio->sd, &addr, &addrlen))
+        return;
+      info->protocol= addr.sa_family == AF_UNIX ?
+        MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
+      info->socket= vio->sd;
+      return;
+    }
+#ifdef _WIN32
+  case VIO_TYPE_NAMEDPIPE:
+    info->protocol= MYSQL_VIO_PIPE;
+    info->handle= vio->hPipe;
+    return;
+#ifdef HAVE_SMEM
+  case VIO_TYPE_SHARED_MEMORY:
+    info->protocol= MYSQL_VIO_MEMORY;
+    info->handle= vio->handle_file_map; /* or what ? */
+    return;
+#endif
+#endif
+  default: DBUG_ASSERT(0);
+  }
+}
+
+static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+                              MYSQL_PLUGIN_VIO_INFO *info)
+{
+  MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
+  mpvio_info(mpvio->mysql->net.vio, info);
+}
+
+/**
+  Client side of the plugin driver authentication.
+
+  @note this is used by both the mysql_real_connect and mysql_change_user
+
+  @param mysql       mysql
+  @param data        pointer to the plugin auth data (scramble) in the
+                     handshake packet
+  @param data_len    the length of the data
+  @param data_plugin a plugin that data were prepared for
+                     or 0 if it's mysql_change_user()
+  @param db          initial db to use, can be 0
+
+  @retval 0 ok
+  @retval 1 error
+*/
+int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+                    const char *data_plugin, const char *db)
+{
+  const char    *auth_plugin_name;
+  auth_plugin_t *auth_plugin;
+  MCPVIO_EXT    mpvio;
+  ulong		pkt_length;
+  int           res;
+
+  DBUG_ENTER ("run_plugin_auth");
+  /* determine the default/initial plugin to use */
+  if (mysql->options.extension && mysql->options.extension->default_auth &&
+      mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+  {
+    auth_plugin_name= mysql->options.extension->default_auth;
+    if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql,
+                       auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+      DBUG_RETURN (1); /* oops, not found */
+  }
+  else
+  {
+    auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ?
+      &native_password_client_plugin : &old_password_client_plugin;
+    auth_plugin_name= auth_plugin->name;
+  }
+
+  DBUG_PRINT ("info", ("using plugin %s", auth_plugin_name));
+
+  mysql->net.last_errno= 0; /* just in case */
+
+  if (data_plugin && strcmp(data_plugin, auth_plugin_name))
+  {
+    /* data was prepared for a different plugin, don't show it to this one */
+    data= 0;
+    data_len= 0;
+  }
+
+  mpvio.mysql_change_user= data_plugin == 0;
+  mpvio.cached_server_reply.pkt= (uchar*)data;
+  mpvio.cached_server_reply.pkt_len= data_len;
+  mpvio.read_packet= client_mpvio_read_packet;
+  mpvio.write_packet= client_mpvio_write_packet;
+  mpvio.info= client_mpvio_info;
+  mpvio.mysql= mysql;
+  mpvio.packets_read= mpvio.packets_written= 0;
+  mpvio.db= db;
+  mpvio.plugin= auth_plugin;
+
+  res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+  DBUG_PRINT ("info", ("authenticate_user returned %s", 
+                       res == CR_OK ? "CR_OK" : 
+                       res == CR_ERROR ? "CR_ERROR" :
+                       res == CR_OK_HANDSHAKE_COMPLETE ? 
+                         "CR_OK_HANDSHAKE_COMPLETE" : "error"));
+
+  compile_time_assert(CR_OK == -1);
+  compile_time_assert(CR_ERROR == 0);
+  if (res > CR_OK && mysql->net.read_pos[0] != 254)
+  {
+    /*
+      the plugin returned an error. write it down in mysql,
+      unless the error code is CR_ERROR and mysql->net.last_errno
+      is already set (the plugin has done it)
+    */
+    DBUG_PRINT ("info", ("res=%d", res));
+    if (res > CR_ERROR)
+      set_mysql_error(mysql, res, unknown_sqlstate);
+    else
+      if (!mysql->net.last_errno)
+        set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+    DBUG_RETURN (1);
+  }
+
+  /* read the OK packet (or use the cached value in mysql->net.read_pos */
+  if (res == CR_OK)
+    pkt_length= (*mysql->methods->read_change_user_result)(mysql);
+  else /* res == CR_OK_HANDSHAKE_COMPLETE */
+    pkt_length= mpvio.last_read_packet_len;
+
+  DBUG_PRINT ("info", ("OK packet length=%lu", pkt_length));
+  if (pkt_length == packet_error)
+  {
+    if (mysql->net.last_errno == CR_SERVER_LOST)
+      set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+                               ER(CR_SERVER_LOST_EXTENDED),
+                               "reading authorization packet",
+                               errno);
+    DBUG_RETURN (1);
+  }
+
+  if (mysql->net.read_pos[0] == 254)
+  {
+    /* The server asked to use a different authentication plugin */
+    if (pkt_length == 1)
+    { 
+      /* old "use short scramble" packet */
+      DBUG_PRINT ("info", ("old use short scramble packet from server"));
+      auth_plugin_name= old_password_plugin_name;
+      mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble;
+      mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1;
+    }
+    else
+    { 
+      /* new "use different plugin" packet */
+      uint len;
+      auth_plugin_name= (char*)mysql->net.read_pos + 1;
+      len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */
+      mpvio.cached_server_reply.pkt_len= pkt_length - len - 2;
+      mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2;
+      DBUG_PRINT ("info", ("change plugin packet from server for plugin %s",
+                           auth_plugin_name));
+    }
+
+    if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql,
+                         auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+      DBUG_RETURN (1);
+
+    mpvio.plugin= auth_plugin;
+    res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+
+    DBUG_PRINT ("info", ("second authenticate_user returned %s", 
+                         res == CR_OK ? "CR_OK" : 
+                         res == CR_ERROR ? "CR_ERROR" :
+                         res == CR_OK_HANDSHAKE_COMPLETE ? 
+                         "CR_OK_HANDSHAKE_COMPLETE" : "error"));
+    if (res > CR_OK)
+    {
+      if (res > CR_ERROR)
+        set_mysql_error(mysql, res, unknown_sqlstate);
+      else
+        if (!mysql->net.last_errno)
+          set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+      DBUG_RETURN (1);
+    }
+
+    if (res != CR_OK_HANDSHAKE_COMPLETE)
+    {
+      /* Read what server thinks about out new auth message report */
+      if (cli_safe_read(mysql) == packet_error)
+      {
+        if (mysql->net.last_errno == CR_SERVER_LOST)
+          set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+                                   ER(CR_SERVER_LOST_EXTENDED),
+                                   "reading final connect information",
+                                   errno);
+        DBUG_RETURN (1);
+      }
+    }
+  }
+  /*
+    net->read_pos[0] should always be 0 here if the server implements
+    the protocol correctly
+  */
+  DBUG_RETURN (mysql->net.read_pos[0] != 0);
+}
 
 MYSQL * STDCALL 
 CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
@@ -2228,7 +2923,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
 		       uint port, const char *unix_socket,ulong client_flag)
 {
   char		buff[NAME_LEN+USERNAME_LENGTH+100];
-  char		*end,*host_info= NULL;
+  int           scramble_data_len, pkt_scramble_len;
+  char          *end,*host_info= 0, *server_version_end, *pkt_end;
+  char          *scramble_data;
+  const char    *scramble_plugin;
   ulong		pkt_length;
   NET		*net= &mysql->net;
 #ifdef MYSQL_SERVER
@@ -2600,8 +3298,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
                                errno);
     goto error;
   }
+  pkt_end= (char*)net->read_pos + pkt_length;
   /* Check if version of protocol matches current one */
-
   mysql->protocol_version= net->read_pos[0];
   DBUG_DUMP("packet",(uchar*) net->read_pos,10);
   DBUG_PRINT("info",("mysql protocol version %d, server=%d",
@@ -2613,31 +3311,29 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
                              PROTOCOL_VERSION);
     goto error;
   }
-  end=strend((char*) net->read_pos+1);
+  server_version_end= end= strend((char*) net->read_pos+1);
   mysql->thread_id=uint4korr(end+1);
   end+=5;
   /* 
-    Scramble is split into two parts because old clients does not understand
+    Scramble is split into two parts because old clients do not understand
     long scrambles; here goes the first part.
   */
-  strmake(mysql->scramble, end, SCRAMBLE_LENGTH_323);
-  end+= SCRAMBLE_LENGTH_323+1;
+  scramble_data= end;
+  scramble_data_len= SCRAMBLE_LENGTH_323 + 1;
+  scramble_plugin= old_password_plugin_name;
+  end+= scramble_data_len;
 
-  if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
+  if (pkt_end >= end + 1)
     mysql->server_capabilities=uint2korr(end);
-  if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
+  if (pkt_end >= end + 18)
   {
     /* New protocol with 16 bytes to describe server characteristics */
     mysql->server_language=end[2];
     mysql->server_status=uint2korr(end+3);
+    mysql->server_capabilities|= uint2korr(end+5) << 16;
+    pkt_scramble_len= end[7];
   }
   end+= 18;
-  if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 - 
-                           (char *) net->read_pos))
-    strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
-            SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
-  else
-    mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
 
   if (mysql->options.secure_auth && passwd[0] &&
       !(mysql->server_capabilities & CLIENT_SECURE_CONNECTION))
@@ -2656,7 +3352,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
 		       &mysql->unix_socket,unix_socket ?
 		       (uint) strlen(unix_socket)+1 : (uint) 1,
 		       &mysql->server_version,
-		       (uint) (end - (char*) net->read_pos),
+		       (uint) (server_version_end - (char*) net->read_pos + 1),
 		       NullS) ||
       !(mysql->user=my_strdup(user,MYF(0))) ||
       !(mysql->passwd=my_strdup(passwd,MYF(0))))
@@ -2673,198 +3369,47 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
   strmov(mysql->server_version,(char*) net->read_pos+1);
   mysql->port=port;
 
-  /*
-    Part 2: format and send client info to the server for access check
-  */
-  
-  client_flag|=mysql->options.client_flag;
-  client_flag|=CLIENT_CAPABILITIES;
-  if (client_flag & CLIENT_MULTI_STATEMENTS)
-    client_flag|= CLIENT_MULTI_RESULTS;
-
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-  if (mysql->options.ssl_key || mysql->options.ssl_cert ||
-      mysql->options.ssl_ca || mysql->options.ssl_capath ||
-      mysql->options.ssl_cipher)
-    mysql->options.use_ssl= 1;
-  if (mysql->options.use_ssl)
-    client_flag|=CLIENT_SSL;
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
-  if (db)
-    client_flag|=CLIENT_CONNECT_WITH_DB;
-
-  /* Remove options that server doesn't support */
-  client_flag= ((client_flag &
-		 ~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)) |
-		(client_flag & mysql->server_capabilities));
-#ifndef HAVE_COMPRESS
-  client_flag&= ~CLIENT_COMPRESS;
-#endif
-
-  if (client_flag & CLIENT_PROTOCOL_41)
+  if (pkt_end >= end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1)
   {
-    /* 4.1 server and 4.1 client has a 32 byte option flag */
-    int4store(buff,client_flag);
-    int4store(buff+4, net->max_packet_size);
-    buff[8]= (char) mysql->charset->number;
-    bzero(buff+9, 32-9);
-    end= buff+32;
-  }
-  else
-  {
-    int2store(buff,client_flag);
-    int3store(buff+2,net->max_packet_size);
-    end= buff+5;
-  }
-  mysql->client_flag=client_flag;
-
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-  if (client_flag & CLIENT_SSL)
-  {
-    /* Do the SSL layering. */
-    struct st_mysql_options *options= &mysql->options;
-    struct st_VioSSLFd *ssl_fd;
-
     /*
-      Send client_flag, max_packet_size - unencrypted otherwise
-      the server does not know we want to do SSL
+     move the first scramble part - directly in the NET buffer -
+     to get a full continuous scramble. We've read all the header,
+     and can overwrite it now.
     */
-    if (my_net_write(net, (uchar*) buff, (uint) (end-buff)) || net_flush(net))
-    {
-      set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
-                               ER(CR_SERVER_LOST_EXTENDED),
-                               "sending connection information to server",
-                               errno);
-      goto error;
-    }
-
-    /* Create the VioSSLConnectorFd - init SSL and load certs */
-    if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
-                                        options->ssl_cert,
-                                        options->ssl_ca,
-                                        options->ssl_capath,
-                                        options->ssl_cipher)))
-    {
-      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
-      goto error;
-    }
-    mysql->connector_fd= (void*)ssl_fd;
-
-    /* Connect to the server */
-    DBUG_PRINT("info", ("IO layer change in progress..."));
-    if (sslconnect(ssl_fd, mysql->net.vio,
-                   (long) (mysql->options.connect_timeout)))
-    {
-      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
-      goto error;
-    }
-    DBUG_PRINT("info", ("IO layer change done!"));
-
-    /* Verify server cert */
-    if ((client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
-        ssl_verify_server_cert(mysql->net.vio, mysql->host))
-    {
-      set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
-      goto error;
-    }
-
-  }
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
-
-  DBUG_PRINT("info",("Server version = '%s'  capabilites: %lu  status: %u  client_flag: %lu",
-		     mysql->server_version,mysql->server_capabilities,
-		     mysql->server_status, client_flag));
-  /* This needs to be changed as it's not useful with big packets */
-  if (user && user[0])
-    strmake(end,user,USERNAME_LENGTH);          /* Max user name */
-  else
-    read_user_name((char*) end);
-
-  /* We have to handle different version of handshake here */
-#ifdef _CUSTOMCONFIG_
-#include "_cust_libmysql.h"
-#endif
-  DBUG_PRINT("info",("user: %s",end));
-  end= strend(end) + 1;
-  if (passwd[0])
-  {
-    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-    {
-      *end++= SCRAMBLE_LENGTH;
-      scramble(end, mysql->scramble, passwd);
-      end+= SCRAMBLE_LENGTH;
+    memmove(end - SCRAMBLE_LENGTH_323, scramble_data,
+            SCRAMBLE_LENGTH_323);
+    scramble_data= end - SCRAMBLE_LENGTH_323;
+    if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+    {
+      scramble_data_len= pkt_scramble_len;
+      scramble_plugin= scramble_data + scramble_data_len;
+      if (scramble_data + scramble_data_len > pkt_end)
+        scramble_data_len= pkt_end - scramble_data;
     }
     else
     {
-      scramble_323(end, mysql->scramble, passwd);
-      end+= SCRAMBLE_LENGTH_323 + 1;
+      scramble_data_len= pkt_end - scramble_data;
+      scramble_plugin= native_password_plugin_name;
     }
   }
   else
-    *end++= '\0';                               /* empty password */
+    mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
+
+  mysql->client_flag= client_flag;
 
-  /* Add database if needed */
-  if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
-  {
-    end= strmake(end, db, NAME_LEN) + 1;
-    mysql->db= my_strdup(db,MYF(MY_WME));
-    db= 0;
-  }
-  /* Write authentication package */
-  if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
-  {
-    set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
-                             ER(CR_SERVER_LOST_EXTENDED),
-                             "sending authentication information",
-                             errno);
-    goto error;
-  }
-  
   /*
-    Part 3: Authorization data's been sent. Now server can reply with
-    OK-packet, or re-request scrambled password.
+    Part 2: invoke the plugin to send the authentication data to the server
   */
 
-  if ((pkt_length=cli_safe_read(mysql)) == packet_error)
-  {
-    if (mysql->net.last_errno == CR_SERVER_LOST)
-      set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
-                               ER(CR_SERVER_LOST_EXTENDED),
-                               "reading authorization packet",
-                               errno);
+  if (run_plugin_auth(mysql, scramble_data, scramble_data_len,
+                      scramble_plugin, db))
     goto error;
-  }
 
-  if (pkt_length == 1 && net->read_pos[0] == 254 && 
-      mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-  {
-    /*
-      By sending this very specific reply server asks us to send scrambled
-      password in old format.
-    */
-    scramble_323(buff, mysql->scramble, passwd);
-    if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) ||
-        net_flush(net))
-    {
-      set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
-                               ER(CR_SERVER_LOST_EXTENDED),
-                               "sending password information",
-                               errno);
-      goto error;
-    }
-    /* Read what server thinks about out new auth message report */
-    if (cli_safe_read(mysql) == packet_error)
-    {
-      if (mysql->net.last_errno == CR_SERVER_LOST)
-        set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
-                                 ER(CR_SERVER_LOST_EXTENDED),
-                                 "reading final connect information",
-                                 errno);
-      goto error;
-    }
-  }
+  /*
+    Part 3: authenticated, finish the initialization of the connection
+  */
 
-  if (client_flag & CLIENT_COMPRESS)		/* We will use compression */
+  if (mysql->client_flag & CLIENT_COMPRESS)      /* We will use compression */
     net->compress=1;
 
 #ifdef CHECK_LICENSE 
@@ -2872,7 +3417,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons
     goto error;
 #endif
 
-  if (db && mysql_select_db(mysql, db))
+  if (db && !mysql->db && mysql_select_db(mysql, db))
   {
     if (mysql->net.last_errno == CR_SERVER_LOST)
         set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
@@ -2933,7 +3478,7 @@ error:
     /* Free alloced memory */
     end_server(mysql);
     mysql_close_free(mysql);
-    if (!(((ulong) client_flag) & CLIENT_REMEMBER_OPTIONS))
+    if (!(client_flag & CLIENT_REMEMBER_OPTIONS))
       mysql_close_free_options(mysql);
   }
   DBUG_RETURN(0);
@@ -3054,6 +3599,12 @@ static void mysql_close_free_options(MYS
   if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
     my_free(mysql->options.shared_memory_base_name);
 #endif /* HAVE_SMEM */
+  if (mysql->options.extension)
+  {
+    my_free(mysql->options.extension->plugin_dir);
+    my_free(mysql->options.extension->default_auth);
+    my_free(mysql->options.extension);
+  }
   bzero((char*) &mysql->options,sizeof(mysql->options));
   DBUG_VOID_RETURN;
 }
@@ -3528,6 +4079,12 @@ mysql_options(MYSQL *mysql,enum mysql_op
     else
       mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT;
     break;
+  case MYSQL_PLUGIN_DIR:
+    EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg);
+    break;
+  case MYSQL_DEFAULT_AUTH:
+    EXTENSION_SET_STRING(&mysql->options, default_auth, arg);
+    break;
   default:
     DBUG_RETURN(1);
   }
@@ -3631,4 +4188,104 @@ int STDCALL mysql_set_character_set(MYSQ
   return mysql->net.last_errno;
 }
 
+/**
+  client authentication plugin that does native MySQL authentication
+  using a 20-byte (4.1+) scramble
+*/
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  int pkt_len;
+  uchar *pkt;
+
+  DBUG_ENTER ("native_password_auth_client");
+
+
+  if (((MCPVIO_EXT *)vio)->mysql_change_user)
+  {
+    /*
+      in mysql_change_user() the client sends the first packet.
+      we use the old scramble.
+    */
+    pkt= (uchar*)mysql->scramble;
+    pkt_len= SCRAMBLE_LENGTH + 1;
+  }
+  else
+  {
+    /* read the scramble */
+    if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+      return CR_ERROR;
+
+    if (pkt_len != SCRAMBLE_LENGTH + 1)
+      DBUG_RETURN (CR_SERVER_HANDSHAKE_ERR);
+
+    /* save it in MYSQL */
+    memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);
+    mysql->scramble[SCRAMBLE_LENGTH] = 0;
+  }
+
+  if (mysql->passwd[0])
+  {
+    char scrambled[SCRAMBLE_LENGTH + 1];
+    DBUG_PRINT ("info", ("sending scramble"));
+    scramble(scrambled, (char*)pkt, mysql->passwd);
+    if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH))
+      DBUG_RETURN (CR_ERROR);
+  }
+  else
+  {
+    DBUG_PRINT ("info", ("no password"));
+    if (vio->write_packet(vio, 0, 0)) /* no password */
+      DBUG_RETURN (CR_ERROR);
+  }
+
+  DBUG_RETURN (CR_OK);
+}
+
+/**
+  client authentication plugin that does old MySQL authentication
+  using an 8-byte (4.0-) scramble
+*/
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  uchar *pkt;
+  int pkt_len;
 
+  DBUG_ENTER ("old_password_auth_client");
+
+  if (((MCPVIO_EXT *)vio)->mysql_change_user)
+  {
+    /*
+      in mysql_change_user() the client sends the first packet.
+      we use the old scramble.
+    */
+    pkt= (uchar*)mysql->scramble;
+    pkt_len= SCRAMBLE_LENGTH_323 + 1;
+  }
+  else
+  {
+    /* read the scramble */
+    if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+      return CR_ERROR;
+
+    if (pkt_len != SCRAMBLE_LENGTH_323 + 1 &&
+        pkt_len != SCRAMBLE_LENGTH + 1)
+        DBUG_RETURN (CR_SERVER_HANDSHAKE_ERR);
+
+    /* save it in MYSQL */
+    memcpy(mysql->scramble, pkt, pkt_len);
+    mysql->scramble[pkt_len] = 0;
+  }
+
+  if (mysql->passwd[0])
+  {
+    char scrambled[SCRAMBLE_LENGTH_323 + 1];
+    scramble_323(scrambled, (char*)pkt, mysql->passwd);
+    if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1))
+      DBUG_RETURN (CR_ERROR);
+  }
+  else
+    if (vio->write_packet(vio, 0, 0)) /* no password */
+      DBUG_RETURN (CR_ERROR);
+
+  DBUG_RETURN (CR_OK);
+}

=== added file 'sql-common/client_plugin.c'
--- a/sql-common/client_plugin.c	1970-01-01 00:00:00 +0000
+++ b/sql-common/client_plugin.c	2010-10-04 19:25:53 +0000
@@ -0,0 +1,477 @@
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+   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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/**
+  @file
+  
+  Support code for the client side (libmysql) plugins
+
+  Client plugins are somewhat different from server plugins, they are simpler.
+
+  They do not need to be installed or in any way explicitly loaded on the
+  client, they are loaded automatically on demand.
+  One client plugin per shared object, soname *must* match the plugin name.
+
+  There is no reference counting and no unloading either.
+*/
+
+#include <my_global.h>
+#include "mysql.h"
+#include <my_sys.h>
+#include <m_string.h>
+#ifdef THREAD
+#include <my_pthread.h>
+#else
+#include <my_no_pthread.h>
+#endif
+
+#include <sql_common.h>
+#include "errmsg.h"
+#include <mysql/client_plugin.h>
+
+struct st_client_plugin_int {
+  struct st_client_plugin_int *next;
+  void   *dlhandle;
+  struct st_mysql_client_plugin *plugin;
+};
+
+static my_bool initialized= 0;
+static MEM_ROOT mem_root;
+
+static const char *plugin_declarations_sym= "_mysql_client_plugin_declaration_";
+static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
+{
+  0, /* these two are taken by Connector/C */
+  0, /* these two are taken by Connector/C */
+  MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION
+};
+
+/*
+  Loaded plugins are stored in a linked list.
+  The list is append-only, the elements are added to the head (like in a stack).
+  The elements are added under a mutex, but the list can be read and traversed
+  without any mutex because once an element is added to the list, it stays
+  there. The main purpose of a mutex is to prevent two threads from
+  loading the same plugin twice in parallel.
+*/
+struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
+#ifdef THREAD
+static pthread_mutex_t LOCK_load_client_plugin;
+#endif
+
+static int is_not_initialized(MYSQL *mysql, const char *name)
+{
+  if (initialized)
+    return 0;
+
+  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+                           unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+                           name, "not initialized");
+  return 1;
+}
+
+/**
+  finds a plugin in the list
+
+  @param name   plugin name to search for
+  @param type   plugin type
+
+  @note this does NOT necessarily need a mutex, take care!
+  
+  @retval a pointer to a found plugin or 0
+*/
+static struct st_mysql_client_plugin *
+find_plugin(const char *name, int type)
+{
+  struct st_client_plugin_int *p;
+
+  DBUG_ASSERT(initialized);
+  DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
+  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+    return 0;
+
+  for (p= plugin_list[type]; p; p= p->next)
+  {
+    if (strcmp(p->plugin->name, name) == 0)
+      return p->plugin;
+  }
+  return NULL;
+}
+
+/**
+  verifies the plugin and adds it to the list
+
+  @param mysql          MYSQL structure (for error reporting)
+  @param plugin         plugin to install
+  @param dlhandle       a handle to the shared object (returned by dlopen)
+                        or 0 if the plugin was not dynamically loaded
+  @param argc           number of arguments in the 'va_list args'
+  @param args           arguments passed to the plugin initialization function
+
+  @retval a pointer to an installed plugin or 0
+*/
+static struct st_mysql_client_plugin *
+add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
+           int argc, va_list args)
+{
+  const char *errmsg;
+  struct st_client_plugin_int plugin_int, *p;
+  char errbuf[1024];
+
+  DBUG_ASSERT(initialized);
+
+  plugin_int.plugin= plugin;
+  plugin_int.dlhandle= dlhandle;
+
+  if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
+  {
+    errmsg= "Unknown client plugin type";
+    goto err1;
+  }
+
+  if (plugin->interface_version < plugin_version[plugin->type] ||
+      (plugin->interface_version >> 8) >
+       (plugin_version[plugin->type] >> 8))
+  {
+    errmsg= "Incompatible client plugin interface";
+    goto err1;
+  }
+
+  /* Call the plugin initialization function, if any */
+  if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
+  {
+    errmsg= errbuf;
+    goto err1;
+  }
+
+  p= (struct st_client_plugin_int *)
+    memdup_root(&mem_root, &plugin_int, sizeof(plugin_int));
+
+  if (!p)
+  {
+    errmsg= "Out of memory";
+    goto err2;
+  }
+
+  safe_mutex_assert_owner(&LOCK_load_client_plugin);
+
+  p->next= plugin_list[plugin->type];
+  plugin_list[plugin->type]= p;
+
+  return plugin;
+
+err2:
+  if (plugin->deinit)
+    plugin->deinit();
+err1:
+  if (dlhandle)
+    dlclose(dlhandle);
+  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+                           ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name,
+                           errmsg);
+  return NULL;
+}
+
+/**
+  Loads plugins which are specified in the environment variable
+  LIBMYSQL_PLUGINS.
+  
+  Multiple plugins must be separated by semicolon. This function doesn't
+  return or log an error.
+
+  The function is be called by mysql_client_plugin_init
+
+  @todo
+  Support extended syntax, passing parameters to plugins, for example
+  LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
+  or
+  LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
+*/
+static void load_env_plugins(MYSQL *mysql)
+{
+  char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
+
+  /* no plugins to load */
+  if(!s)
+    return;
+
+  free_env= plugs= my_strdup(s, MYF(MY_WME));
+
+  do {
+    if ((s= strchr(plugs, ';')))
+      *s= '\0';
+    mysql_load_plugin(mysql, plugs, -1, 0);
+    plugs= s + 1;
+  } while (s);
+
+  my_free(free_env);
+}
+
+/********** extern functions to be used by libmysql *********************/
+
+/**
+  Initializes the client plugin layer.
+
+  This function must be called before any other client plugin function.
+
+  @retval 0    successful
+  @retval != 0 error occured
+*/
+int mysql_client_plugin_init()
+{
+  MYSQL mysql;
+  struct st_mysql_client_plugin **builtin;
+
+  if (initialized)
+    return 0;
+
+  bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
+
+  pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
+  init_alloc_root(&mem_root, 128, 128);
+
+  bzero(&plugin_list, sizeof(plugin_list));
+
+  initialized= 1;
+
+  pthread_mutex_lock(&LOCK_load_client_plugin);
+
+  for (builtin= mysql_client_builtins; *builtin; builtin++)
+    add_plugin(&mysql, *builtin, 0, 0, 0);
+
+  pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+  load_env_plugins(&mysql);
+
+  return 0;
+}
+
+/**
+  Deinitializes the client plugin layer.
+
+  Unloades all client plugins and frees any associated resources.
+*/
+void mysql_client_plugin_deinit()
+{
+  int i;
+  struct st_client_plugin_int *p;
+
+  if (!initialized)
+    return;
+
+  for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
+    for (p= plugin_list[i]; p; p= p->next)
+    {
+      if (p->plugin->deinit)
+        p->plugin->deinit();
+      if (p->dlhandle)
+        dlclose(p->dlhandle);
+    }
+
+  bzero(&plugin_list, sizeof(plugin_list));
+  initialized= 0;
+  free_root(&mem_root, MYF(0));
+  pthread_mutex_destroy(&LOCK_load_client_plugin);
+}
+
+/************* public facing functions, for client consumption *********/
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(MYSQL *mysql,
+                             struct st_mysql_client_plugin *plugin)
+{
+  if (is_not_initialized(mysql, plugin->name))
+    return NULL;
+
+  pthread_mutex_lock(&LOCK_load_client_plugin);
+
+  /* make sure the plugin wasn't loaded meanwhile */
+  if (find_plugin(plugin->name, plugin->type))
+  {
+    set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+                             unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+                             plugin->name, "it is already loaded");
+    plugin= NULL;
+  }
+  else
+    plugin= add_plugin(mysql, plugin, 0, 0, 0);
+
+  pthread_mutex_unlock(&LOCK_load_client_plugin);
+  return plugin;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
+                    int argc, va_list args)
+{
+  const char *errmsg;
+  char dlpath[FN_REFLEN+1];
+  void *sym, *dlhandle;
+  struct st_mysql_client_plugin *plugin;
+#ifdef _WIN32
+  char win_errormsg[2048];
+#endif
+
+  DBUG_ENTER ("mysql_load_plugin_v");
+  DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc));
+  if (is_not_initialized(mysql, name))
+  {
+    DBUG_PRINT ("leave", ("mysql not initialized"));
+    DBUG_RETURN (NULL);
+  }
+
+  pthread_mutex_lock(&LOCK_load_client_plugin);
+
+  /* make sure the plugin wasn't loaded meanwhile */
+  if (type >= 0 && find_plugin(name, type))
+  {
+    errmsg= "it is already loaded";
+    goto err;
+  }
+
+  /* Compile dll path */
+  strxnmov(dlpath, sizeof(dlpath) - 1,
+           mysql->options.extension && mysql->options.extension->plugin_dir ?
+           mysql->options.extension->plugin_dir : PLUGINDIR, "/",
+           name, SO_EXT, NullS);
+   
+  DBUG_PRINT ("info", ("dlopeninig %s", dlpath));
+  /* Open new dll handle */
+  if (!(dlhandle= dlopen(dlpath, RTLD_NOW)))
+  {
+#if defined(__APPLE__)
+    /* Apple supports plugins with .so also, so try this as well */
+    strxnmov(dlpath, sizeof(dlpath) - 1,
+             mysql->options.extension && mysql->options.extension->plugin_dir ?
+             mysql->options.extension->plugin_dir : PLUGINDIR, "/",
+             name, ".so", NullS);
+    if ((dlhandle= dlopen(dlpath, RTLD_NOW)))
+      goto have_plugin;
+#endif
+
+    DBUG_PRINT ("info", ("failed to dlopen"));
+#ifdef _WIN32
+    if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+                  0, GetLastError(), 0, win_errormsg, 2048, NULL))
+      errmsg= win_errormsg;
+    else
+      errmsg= "";
+#else
+    errmsg= dlerror();
+#endif
+    goto err;
+  }
+
+#if defined(__APPLE__)
+have_plugin:  
+#endif
+  if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
+  {
+    errmsg= "not a plugin";
+    dlclose(dlhandle);
+    goto err;
+  }
+
+  plugin= (struct st_mysql_client_plugin*)sym;
+
+  if (type >=0 && type != plugin->type)
+  {
+    errmsg= "type mismatch";
+    goto err;
+  }
+
+  if (strcmp(name, plugin->name))
+  {
+    errmsg= "name mismatch";
+    goto err;
+  }
+
+  if (type < 0 && find_plugin(name, plugin->type))
+  {
+    errmsg= "it is already loaded";
+    goto err;
+  }
+
+  plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
+
+  pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+  DBUG_PRINT ("leave", ("plugin loaded ok"));
+  DBUG_RETURN (plugin);
+
+err:
+  pthread_mutex_unlock(&LOCK_load_client_plugin);
+  DBUG_PRINT ("leave", ("plugin load error : %s", errmsg));
+  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+                           ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
+  DBUG_RETURN (NULL);
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
+{
+  struct st_mysql_client_plugin *p;
+  va_list args;
+  va_start(args, argc);
+  p= mysql_load_plugin_v(mysql, name, type, argc, args);
+  va_end(args);
+  return p;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
+{
+  struct st_mysql_client_plugin *p;
+
+  DBUG_ENTER ("mysql_client_find_plugin");
+  DBUG_PRINT ("entry", ("name=%s, type=%d", name, type));
+  if (is_not_initialized(mysql, name))
+    DBUG_RETURN (NULL);
+
+  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+  {
+    set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+                             ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name,
+                             "invalid type");
+  }
+
+  if ((p= find_plugin(name, type)))
+  {
+    DBUG_PRINT ("leave", ("found %p", p));
+    DBUG_RETURN (p);
+  }
+
+  /* not found, load it */
+  p= mysql_load_plugin(mysql, name, type, 0);
+  DBUG_PRINT ("leave", ("loaded %p", p));
+  DBUG_RETURN (p);
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin,
+                                 const char *option,
+                                 const void *value)
+{
+  DBUG_ENTER("mysql_plugin_options");
+  /* does the plugin support options call? */
+  if (!plugin || !plugin->options)
+    DBUG_RETURN(1);
+  DBUG_RETURN(plugin->options(option, value));
+}

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2010-09-28 15:15:58 +0000
+++ b/sql/CMakeLists.txt	2010-10-04 12:42:16 +0000
@@ -50,6 +50,7 @@ SET (SQL_SOURCE
                log_event_old.cc rpl_record_old.cc
                message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c
                mysqld.cc net_serv.cc  keycaches.cc
+               ../sql-common/client_plugin.c
                opt_range.cc opt_range.h opt_sum.cc 
                ../sql-common/pack.c parse_file.cc password.c procedure.cc 
                protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc 

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2010-10-04 11:52:59 +0000
+++ b/sql/Makefile.am	2010-10-04 12:42:16 +0000
@@ -175,7 +175,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			rpl_handler.cc mdl.cc transaction.cc sql_audit.cc  \
 			sql_alter.cc sql_partition_admin.cc sha2.cc
 
-nodist_mysqld_SOURCES =	mini_client_errors.c pack.c client.c my_time.c my_user.c 
+nodist_mysqld_SOURCES =	mini_client_errors.c pack.c client.c my_time.c my_user.c client_plugin.c
 
 libndb_la_CPPFLAGS=	@ndbcluster_includes@
 libndb_la_SOURCES=	ha_ndbcluster.cc \
@@ -189,10 +189,10 @@ mysql_tzinfo_to_sql_SOURCES = tztime.cc
 mysql_tzinfo_to_sql_CXXFLAGS= -DTZINFO2SQL
 
 DEFS =			-DMYSQL_SERVER \
-			-DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
-			-DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \
-			-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
-			-DPLUGINDIR="\"$(pkgplugindir)\"" \
+			-DDEFAULT_MYSQL_HOME='"$(MYSQLBASEdir)"' \
+			-DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
+			-DSHAREDIR='"$(MYSQLSHAREdir)"' \
+			-DPLUGINDIR='"$(pkgplugindir)"' \
 			-DHAVE_EVENT_SCHEDULER \
 			@DEFS@
 
@@ -216,6 +216,8 @@ link_sources:
 	@LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c
 	rm -f client.c
 	@LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c
+	rm -f client_plugin.c
+	@LN_CP_F@ $(top_srcdir)/sql-common/client_plugin.c client_plugin.c
 	rm -f my_time.c
 	@LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c
 	rm -f my_user.c

=== modified file 'sql/client_settings.h'
--- a/sql/client_settings.h	2009-11-25 10:53:23 +0000
+++ b/sql/client_settings.h	2010-08-09 08:32:50 +0000
@@ -21,6 +21,7 @@
 #endif /* CLIENT_SETTINGS_INCLUDED */
 
 #include <thr_alarm.h>
+#include <sql_common.h>
 
 #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG |	  \
                              CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS | \
@@ -33,7 +34,8 @@
 #undef HAVE_SMEM
 #undef _CUSTOMCONFIG_
 
-#define mysql_server_init(a,b,c) 0
+#define mysql_server_init(a,b,c) mysql_client_plugin_init()
+#define mysql_server_end()       mysql_client_plugin_deinit()
 
 #ifdef HAVE_REPLICATION
 C_MODE_START

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-08-12 13:50:23 +0000
+++ b/sql/ha_ndbcluster.cc	2010-09-20 14:17:32 +0000
@@ -9503,7 +9503,7 @@ pthread_handler_t ndb_util_thread_func(v
   thd->client_capabilities = 0;
   my_net_init(&thd->net, 0);
   thd->main_security_ctx.master_access= ~0;
-  thd->main_security_ctx.priv_user = 0;
+  thd->main_security_ctx.priv_user[0] = 0;
   /* Do not use user-supplied timeout value for system threads. */
   thd->variables.lock_wait_timeout= LONG_TIMEOUT;
 

=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc	2010-08-12 13:50:23 +0000
+++ b/sql/ha_ndbcluster_binlog.cc	2010-09-20 14:17:32 +0000
@@ -3656,7 +3656,7 @@ pthread_handler_t ndb_binlog_thread_func
   thd->client_capabilities= 0;
   my_net_init(&thd->net, 0);
   thd->main_security_ctx.master_access= ~0;
-  thd->main_security_ctx.priv_user= 0;
+  thd->main_security_ctx.priv_user[0]= 0;
   /* Do not use user-supplied timeout value for system threads. */
   thd->variables.lock_wait_timeout= LONG_TIMEOUT;
 

=== modified file 'sql/lex.h'
--- a/sql/lex.h	2010-02-20 09:40:21 +0000
+++ b/sql/lex.h	2010-08-09 08:32:50 +0000
@@ -420,8 +420,9 @@ static SYMBOL symbols[] = {
   { "PROCEDURE",	SYM(PROCEDURE_SYM)},
   { "PROCESS"	,	SYM(PROCESS)},
   { "PROCESSLIST",	SYM(PROCESSLIST_SYM)},
-  { "PROFILE",    SYM(PROFILE_SYM)},
-  { "PROFILES",   SYM(PROFILES_SYM)},
+  { "PROFILE",          SYM(PROFILE_SYM)},
+  { "PROFILES",         SYM(PROFILES_SYM)},
+  { "PROXY",            SYM(PROXY_SYM)},
   { "PURGE",		SYM(PURGE)},
   { "QUARTER",          SYM(QUARTER_SYM)},
   { "QUERY",		SYM(QUERY_SYM)},

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-10-04 11:52:59 +0000
+++ b/sql/mysqld.cc	2010-10-04 12:42:16 +0000
@@ -58,6 +58,7 @@
 #include "sql_repl.h"
 #include "rpl_filter.h"
 #include "repl_failsafe.h"
+#include <sql_common.h>
 #include <my_stacktrace.h>
 #include "mysqld_suffix.h"
 #include "mysys_err.h"
@@ -1481,6 +1482,7 @@ void clean_up(bool print_message)
     sql_print_information(ER_DEFAULT(ER_SHUTDOWN_COMPLETE),my_progname);
   cleanup_errmsgs();
   MYSQL_CALLBACK(thread_scheduler, end, ());
+  mysql_client_plugin_deinit();
   finish_client_errs();
   (void) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); // finish server errs
   DBUG_PRINT("quit", ("Error messages freed"));
@@ -3333,6 +3335,7 @@ static int init_common_variables()
   if (init_errmessage())	/* Read error messages from file */
     return 1;
   init_client_errs();
+  mysql_client_plugin_init();
   lex_init();
   if (item_create_init())
     return 1;

=== modified file 'sql/password.c'
--- a/sql/password.c	2010-03-31 14:05:33 +0000
+++ b/sql/password.c	2010-08-09 08:32:50 +0000
@@ -223,13 +223,13 @@ void scramble_323(char *to, const char *
 */
 
 my_bool
-check_scramble_323(const char *scrambled, const char *message,
+check_scramble_323(const unsigned char *scrambled, const char *message,
                    ulong *hash_pass)
 {
   struct rand_struct rand_st;
   ulong hash_message[2];
-  char buff[16],*to,extra;                      /* Big enough for check */
-  const char *pos;
+  uchar buff[16],*to,extra;                     /* Big enough for check */
+  const uchar *pos;
 
   hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
   randominit(&rand_st,hash_pass[0] ^ hash_message[0],
@@ -244,7 +244,7 @@ check_scramble_323(const char *scrambled
   to=buff;
   while (*scrambled)
   {
-    if (*scrambled++ != (char) (*to++ ^ extra))
+    if (*scrambled++ != (uchar) (*to++ ^ extra))
       return 1;                                 /* Wrong password */
   }
   return 0;
@@ -510,7 +510,7 @@ scramble(char *to, const char *message, 
 */
 
 my_bool
-check_scramble(const char *scramble_arg, const char *message,
+check_scramble(const uchar *scramble_arg, const char *message,
                const uint8 *hash_stage2)
 {
   SHA1_CONTEXT sha1_context;
@@ -523,7 +523,7 @@ check_scramble(const char *scramble_arg,
   mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
   mysql_sha1_result(&sha1_context, buf);
   /* encrypt scramble */
-    my_crypt((char *) buf, buf, (const uchar *) scramble_arg, SCRAMBLE_LENGTH);
+    my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
   /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
   mysql_sha1_reset(&sha1_context);
   mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);

=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc	2010-07-29 12:32:11 +0000
+++ b/sql/protocol.cc	2010-08-09 08:32:50 +0000
@@ -349,24 +349,6 @@ static bool write_eof_packet(THD *thd, N
 }
 
 /**
-  Please client to send scrambled_password in old format.
-     
-  @param thd thread handle
-
-  @retval
-    0  ok
-  @retval
-   !0  error
-*/
-
-bool send_old_password_request(THD *thd)
-{
-  NET *net= &thd->net;
-  return my_net_write(net, eof_buff, 1) || net_flush(net);
-}
-
-
-/**
   @param thd Thread handler
   @param sql_errno The error code to send
   @param err A pointer to the error message

=== modified file 'sql/protocol.h'
--- a/sql/protocol.h	2010-03-31 14:05:33 +0000
+++ b/sql/protocol.h	2010-08-09 08:32:50 +0000
@@ -204,7 +204,6 @@ public:
 void send_warning(THD *thd, uint sql_errno, const char *err=0);
 bool net_send_error(THD *thd, uint sql_errno, const char *err,
                     const char* sqlstate);
-bool send_old_password_request(THD *thd);
 uchar *net_store_data(uchar *to,const uchar *from, size_t length);
 uchar *net_store_data(uchar *to,int32 from);
 uchar *net_store_data(uchar *to,longlong from);

=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc	2010-09-02 18:37:04 +0000
+++ b/sql/set_var.cc	2010-10-04 12:42:16 +0000
@@ -746,9 +746,9 @@ int set_var_password::check(THD *thd)
   }
   if (!user->user.str)
   {
-    DBUG_ASSERT(thd->security_ctx->priv_user);
-    user->user.str= (char *) thd->security_ctx->priv_user;
-    user->user.length= strlen(thd->security_ctx->priv_user);
+    DBUG_ASSERT(thd->security_ctx->user);
+    user->user.str= (char *) thd->security_ctx->user;
+    user->user.length= strlen(thd->security_ctx->user);
   }
   /* Returns 1 as the function sends error to client */
   return check_change_password(thd, user->host.str, user->user.str,

=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt	2010-08-30 15:33:55 +0000
+++ b/sql/share/errmsg-utf8.txt	2010-10-04 12:42:16 +0000
@@ -6350,3 +6350,32 @@ ER_FAILED_READ_FROM_PAR_FILE
 ER_VALUES_IS_NOT_INT_TYPE_ERROR
   eng "VALUES value for partition '%-.64s' must have type INT"
   swe "Värden i VALUES för partition '%-.64s' måste ha typen INT"
+
+ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000 
+        cze "P-Břístup pro uživatele '%-.48s'@'%-.64s'"
+        dan "Adgang nægtet bruger: '%-.48s'@'%-.64s'"
+        nla "Toegang geweigerd voor gebruiker: '%-.48s'@'%-.64s'"
+        eng "Access denied for user '%-.48s'@'%-.64s'"
+        est "Ligipääs keelatud kasutajale '%-.48s'@'%-.64s'"
+        fre "Accès refusé pour l'utilisateur: '%-.48s'@'@%-.64s'"
+        ger "Benutzer '%-.48s'@'%-.64s' hat keine Zugriffsberechtigung"
+        greek "Δεν επιτέρεται η πρόσβαση στο χρήστη: '%-.48s'@'%-.64s'"
+        hun "A(z) '%-.48s'@'%-.64s' felhasznalo szamara tiltott eleres."
+        ita "Accesso non consentito per l'utente: '%-.48s'@'%-.64s'"
+        kor "'%-.48s'@'%-.64s' 사용자는 접근or bruker: '%-.48s'@'%-.64s'"
+        norwegian-ny "Tilgang ikke tillate for brukar: '%-.48s'@'%-.64s'"
+        por "Acesso negado para o usuário '%-.48s'@'%-.64s'"
+        rum "Acces interzis pentru utilizatorul: '%-.48s'@'%-.64s'"
+        rus "Доступ закрыт для an "Pristup je zabranjen korisniku '%-.48s'@'%-.64s'"
+        slo "Zakázaný prístup pre užívateľa: '%-.48s'@'%-.64s'"
+        spa "Acceso negado para usuario: '%-.48s'@'%-.64s'"
+        swe "Användare '%-.48s'@'%-.64s' är ej berättigad att logga in"
+        ukr "Дост%-.48s'@'%-.64s'"
+
+ER_SET_PASSWORD_AUTH_PLUGIN
+        eng "SET PASSWORD has no significance for users authenticating via plugins"
+
+ER_GRANT_PLUGIN_USER_EXISTS
+        eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists"

=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h	2010-08-05 12:53:09 +0000
+++ b/sql/sp_head.h	2010-08-16 15:16:07 +0000
@@ -42,6 +42,7 @@
 #define TYPE_ENUM_FUNCTION  1
 #define TYPE_ENUM_PROCEDURE 2
 #define TYPE_ENUM_TRIGGER   3
+#define TYPE_ENUM_PROXY     4
 
 Item_result
 sp_map_result_type(enum enum_field_types type);

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2010-08-31 09:55:32 +0000
+++ b/sql/sql_acl.cc	2010-10-04 12:42:16 +0000
@@ -44,6 +44,11 @@
 #include "transaction.h"
 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
 #include "records.h"             // init_read_record, end_read_record
+#include <sql_common.h>
+#include <mysql/plugin_auth.h>
+#include "sql_connect.h"
+#include "hostname.h"
+#include "sql_db.h"
 
 bool mysql_user_table_is_in_short_password_format= false;
 
@@ -164,7 +169,327 @@ TABLE_FIELD_TYPE mysql_db_table_fields[M
 const TABLE_FIELD_DEF
   mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
 
+static LEX_STRING native_password_plugin_name= {
+  C_STRING_WITH_LEN("mysql_native_password")
+};
+  
+static LEX_STRING old_password_plugin_name= {
+  C_STRING_WITH_LEN("mysql_old_password")
+};
+  
+/// @todo make it configurable
+LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static plugin_ref old_password_plugin;
+#endif
+static plugin_ref native_password_plugin;
+
+/* Classes */
+
+struct acl_host_and_ip
+{
+  char *hostname;
+  long ip, ip_mask;                      // Used with masked ip:s
+};
+
+class ACL_ACCESS {
+public:
+  ulong sort;
+  ulong access;
+};
+
+/* ACL_HOST is used if no host is specified */
+
+class ACL_HOST :public ACL_ACCESS
+{
+public:
+  acl_host_and_ip host;
+  char *db;
+};
+
+class ACL_USER :public ACL_ACCESS
+{
+public:
+  acl_host_and_ip host;
+  uint hostname_length;
+  USER_RESOURCES user_resource;
+  char *user;
+  uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
+  uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1 
+  enum SSL_type ssl_type;
+  const char *ssl_cipher, *x509_issuer, *x509_subject;
+  LEX_STRING plugin;
+  LEX_STRING auth_string;
+
+  ACL_USER *copy(MEM_ROOT *root)
+  {
+    ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
+    if (!dst)
+      return 0;
+    *dst= *this;
+    dst->user= safe_strdup_root(root, user);
+    dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
+    dst->x509_issuer= safe_strdup_root(root, x509_issuer);
+    dst->x509_subject= safe_strdup_root(root, x509_subject);
+    if (plugin.str == native_password_plugin_name.str ||
+        plugin.str == old_password_plugin_name.str)
+      dst->plugin= plugin;
+    else
+      dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
+    dst->auth_string.str= safe_strdup_root(root, auth_string.str);
+    dst->host.hostname= safe_strdup_root(root, host.hostname);
+    return dst;
+  }
+};
+
+class ACL_DB :public ACL_ACCESS
+{
+public:
+  acl_host_and_ip host;
+  char *user,*db;
+};
+
+
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
+static void update_hostname(acl_host_and_ip *host, const char *hostname);
+static ulong get_sort(uint count,...);
+static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
+			     const char *ip);
+static bool show_proxy_grants (THD *thd, LEX_USER *user,
+                               char *buff, size_t buffsize);
+
+class ACL_PROXY_USER :public ACL_ACCESS
+{
+  acl_host_and_ip host;
+  const char *user;
+  acl_host_and_ip proxied_host;
+  const char *proxied_user;
+  bool with_grant;
+
+  typedef enum { 
+    MYSQL_PROXY_PRIV_HOST, 
+    MYSQL_PROXY_PRIV_USER, 
+    MYSQL_PROXY_PRIV_PROXIED_HOST,
+    MYSQL_PROXY_PRIV_PROXIED_USER, 
+    MYSQL_PROXY_PRIV_WITH_GRANT } old_acl_proxy_users;
+public:
+  ACL_PROXY_USER () {};
+
+  void init(const char *host_arg, const char *user_arg,
+       const char *proxied_host_arg, const char *proxied_user_arg,
+       bool with_grant_arg)
+  {
+    user= (user_arg && *user_arg) ? user_arg : NULL;
+    update_hostname (&host, 
+                     (host_arg && *host_arg) ? host_arg : NULL);
+    proxied_user= (proxied_user_arg && *proxied_user_arg) ? 
+      proxied_user_arg : NULL;
+    update_hostname (&proxied_host, 
+                     (proxied_host_arg && *proxied_host_arg) ?
+                     proxied_host_arg : NULL);
+    with_grant= with_grant_arg;
+    sort= get_sort(4, host.hostname, user,
+                   proxied_host.hostname, proxied_user);
+  }
+
+  void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
+       const char *proxied_host_arg, const char *proxied_user_arg,
+       bool with_grant_arg)
+  {
+    init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
+          (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
+          (proxied_host_arg && *proxied_host_arg) ? 
+            strdup_root (mem, proxied_host_arg) : NULL,
+          (proxied_user_arg && *proxied_user_arg) ? 
+            strdup_root (mem, proxied_user_arg) : NULL,
+          with_grant_arg);
+  }
+
+  void init(TABLE *table, MEM_ROOT *mem)
+  {
+    init (get_field(mem, table->field[MYSQL_PROXY_PRIV_HOST]),
+          get_field(mem, table->field[MYSQL_PROXY_PRIV_USER]),
+          get_field(mem, table->field[MYSQL_PROXY_PRIV_PROXIED_HOST]),
+          get_field(mem, table->field[MYSQL_PROXY_PRIV_PROXIED_USER]),
+          table->field[MYSQL_PROXY_PRIV_WITH_GRANT]->val_int() != 0);
+  }
+
+  bool get_with_grant() { return with_grant; }
+  const char *get_user() { return user; }
+  const char *get_host() { return host.hostname; }
+  const char *get_proxied_user() { return proxied_user; }
+  const char *get_proxied_host() { return proxied_host.hostname; }
+  void set_user(MEM_ROOT *mem, const char *user_arg) 
+  { 
+    user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
+  }
+  void set_host(MEM_ROOT *mem, const char *host_arg) 
+  { 
+    update_hostname(&host, 
+                    (host_arg && *host_arg) ? 
+                    strdup_root(mem, host_arg) : NULL);
+  }
+
+  bool check_validity(bool check_no_resolve)
+  {
+    if (check_no_resolve && 
+        (hostname_requires_resolving(host.hostname) ||
+         hostname_requires_resolving(proxied_host.hostname)))
+    {
+      sql_print_warning("'proxy_priv' entry '%s@%s %s@%s' "
+                        "ignored in --skip-name-resolve mode.",
+                        proxied_user ? proxied_user : "",
+                        proxied_host.hostname ? proxied_host.hostname : "",
+                        user ? user : "",
+                        host.hostname ? host.hostname : "");
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
+                const char *proxied_user_arg)
+  {
+    DBUG_ENTER("ACL_PROXY_USER::matches");
+    DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
+                        "compare_hostname(%s,%s,%s) &&"
+                        "wild_compare (%s,%s) &&"
+                        "wild_compare (%s,%s)",
+                        host.hostname ? host.hostname : "<NULL>",
+                        host_arg ? host_arg : "<NULL>",
+                        ip_arg ? ip_arg : "<NULL>",
+                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+                        host_arg ? host_arg : "<NULL>",
+                        ip_arg ? ip_arg : "<NULL>",
+                        user_arg ? user_arg : "<NULL>",
+                        user ? user : "<NULL>",
+                        proxied_user_arg ? proxied_user_arg : "<NULL>",
+                        proxied_user ? proxied_user : "<NULL>"));
+    DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
+                compare_hostname(&proxied_host, host_arg, ip_arg) &&
+                (!user ||
+                 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
+                (!proxied_user || 
+                 (proxied_user && !wild_compare(proxied_user_arg, 
+                                                proxied_user, TRUE))));
+  }
+
+
+  inline static bool auth_element_equals(const char *a, const char *b)
+  {
+    return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
+  }
+
+
+  bool pk_equals(ACL_PROXY_USER *grant)
+  {
+    DBUG_ENTER("pk_equals");
+    DBUG_PRINT("info", ("strcmp(%s,%s) &&"
+                        "strcmp(%s,%s) &&"
+                        "wild_compare (%s,%s) &&"
+                        "wild_compare (%s,%s)",
+                        user ? user : "<NULL>",
+                        grant->user ? grant->user : "<NULL>",
+                        proxied_user ? proxied_user : "<NULL>",
+                        grant->proxied_user ? grant->proxied_user : "<NULL>",
+                        host.hostname ? host.hostname : "<NULL>",
+                        grant->host.hostname ? grant->host.hostname : "<NULL>",
+                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+                        grant->proxied_host.hostname ? 
+                        grant->proxied_host.hostname : "<NULL>"));
+
+    DBUG_RETURN(auth_element_equals(user, grant->user) &&
+                auth_element_equals(proxied_user, grant->proxied_user) &&
+                auth_element_equals(host.hostname, grant->host.hostname) &&
+                auth_element_equals(proxied_host.hostname, 
+                                    grant->proxied_host.hostname));
+  }
+
+
+  bool granted_on(const char *host_arg, const char *user_arg)
+  {
+    return (((!user && (!user_arg || !user_arg[0])) ||
+             (user && user_arg && !strcmp(user, user_arg))) &&
+            ((!host.hostname && (!host_arg || !host_arg[0])) ||
+             (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
+  }
+
+
+  void print_grant(String *str)
+  {
+    str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
+    if (proxied_user)
+      str->append(proxied_user, strlen(proxied_user));
+    str->append(STRING_WITH_LEN("'@'"));
+    if (proxied_host.hostname)
+      str->append(proxied_host.hostname, strlen(proxied_host.hostname));
+    str->append(STRING_WITH_LEN("' TO '"));
+    if (user)
+      str->append(user, strlen(user));
+    str->append(STRING_WITH_LEN("'@'"));
+    if (host.hostname)
+      str->append(host.hostname, strlen(host.hostname));
+    str->append(STRING_WITH_LEN("'"));
+    if (with_grant)
+      str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+  }
+
+  void set_data(ACL_PROXY_USER *grant)
+  {
+    with_grant= grant->with_grant;
+  }
+
+  static int store_pk(TABLE *table, 
+                      const LEX_STRING *host, 
+                      const LEX_STRING *user,
+                      const LEX_STRING *proxied_host, 
+                      const LEX_STRING *proxied_user)
+  {
+    DBUG_ENTER("ACL_PROXY_USER::store_pk");
+    DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
+                        host->str ? host->str : "<NULL>",
+                        user->str ? user->str : "<NULL>",
+                        proxied_host->str ? proxied_host->str : "<NULL>",
+                        proxied_user->str ? proxied_user->str : "<NULL>"));
+    if (table->field[MYSQL_PROXY_PRIV_HOST]->store(host->str, 
+                                                   host->length,
+                                                   system_charset_info))
+      DBUG_RETURN(TRUE);
+    if (table->field[MYSQL_PROXY_PRIV_USER]->store(user->str, 
+                                                   user->length,
+                                                   system_charset_info))
+      DBUG_RETURN(TRUE);
+    if (table->field[MYSQL_PROXY_PRIV_PROXIED_HOST]->store(proxied_host->str,
+                                                           proxied_host->length,
+                                                           system_charset_info))
+      DBUG_RETURN(TRUE);
+    if (table->field[MYSQL_PROXY_PRIV_PROXIED_USER]->store(proxied_user->str,
+                                                           proxied_user->length,
+                                                           system_charset_info))
+      DBUG_RETURN(TRUE);
+
+    DBUG_RETURN(FALSE);
+  }
+
+  static int store_data_record(TABLE *table, 
+                               const LEX_STRING *host, 
+                               const LEX_STRING *user,
+                               const LEX_STRING *proxied_host, 
+                               const LEX_STRING *proxied_user,
+                               bool with_grant)
+  {
+    DBUG_ENTER ("ACL_PROXY_USER::store_pk");
+    if (store_pk (table,  host, user, proxied_host, proxied_user))
+      DBUG_RETURN(TRUE);
+    DBUG_PRINT ("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
+    if (table->field[MYSQL_PROXY_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, 
+                                                         TRUE))
+      DBUG_RETURN(TRUE);
+
+    DBUG_RETURN(FALSE);
+  }
+};
 
 #define FIRST_NON_YN_FIELD 26
 
@@ -184,10 +509,29 @@ static uchar* acl_entry_get_key(acl_entr
   return (uchar*) entry->key;
 }
 
-#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
-#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
+#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
+#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
+                        1 + USERNAME_LENGTH + 1)
+
+#if defined(HAVE_OPENSSL)
+/*
+  Without SSL the handshake consists of one packet. This packet
+  has both client capabilities and scrambled password.
+  With SSL the handshake might consist of two packets. If the first
+  packet (client capabilities) has CLIENT_SSL flag set, we have to
+  switch to SSL and read the second packet. The scrambled password
+  is in the second packet and client_capabilities field will be ignored.
+  Maybe it is better to accept flags other than CLIENT_SSL from the
+  second packet?
+*/
+#define SSL_HANDSHAKE_SIZE      2
+#define NORMAL_HANDSHAKE_SIZE   6
+#define MIN_HANDSHAKE_SIZE      2
+#else
+#define MIN_HANDSHAKE_SIZE      6
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
 
-static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
+static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
 static MEM_ROOT mem, memex;
 static bool initialized=0;
 static bool allow_all_hosts=1;
@@ -205,9 +549,6 @@ static ACL_USER *find_acl_user(const cha
 static bool update_user_table(THD *thd, TABLE *table,
                               const char *host, const char *user,
 			      const char *new_password, uint new_password_len);
-static void update_hostname(acl_host_and_ip *host, const char *hostname);
-static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
-			     const char *ip);
 static my_bool acl_load(THD *thd, TABLE_LIST *tables);
 static my_bool grant_load(THD *thd, TABLE_LIST *tables);
 static inline void get_grantor(THD *thd, char* grantor);
@@ -263,6 +604,19 @@ my_bool acl_init(bool dont_read_acl_tabl
                            (my_hash_get_key) acl_entry_get_key,
                            (my_hash_free_key) free,
                            &my_charset_utf8_bin);
+
+  /*
+    cache built-in native authentication plugins,
+    to avoid hash searches and a global mutex lock on every connect
+  */
+  native_password_plugin= my_plugin_lock_by_name(0,
+           &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+  old_password_plugin= my_plugin_lock_by_name(0,
+           &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+
+  if (!native_password_plugin || !old_password_plugin)
+    DBUG_RETURN(1);
+
   if (dont_read_acl_tables)
   {
     DBUG_RETURN(0); /* purecov: tested */
@@ -287,6 +641,37 @@ my_bool acl_init(bool dont_read_acl_tabl
   DBUG_RETURN(return_val);
 }
 
+/**
+  Choose from either native or old password plugins when assigning a password
+*/
+
+static bool
+set_user_plugin (ACL_USER *user, int password_len)
+{
+  switch (password_len) 
+  {
+  case 0: /* no password */
+  case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+    user->plugin= native_password_plugin_name;
+    return FALSE;
+  case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+    user->plugin= old_password_plugin_name;
+    return FALSE;
+  case 45: /* 4.1: to be removed */
+    sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
+                      "Ignoring user. "
+                      "You should change password for this user.",
+                      user->user ? user->user : "",
+                      user->host.hostname ? user->host.hostname : "");
+    return TRUE;
+  default:
+    sql_print_warning("Found invalid password for user: '%s@%s'; "
+                      "Ignoring user", user->user ? user->user : "",
+                      user->host.hostname ? user->host.hostname : "");
+    return TRUE;
+  }
+}
+
 
 /*
   Initialize structures responsible for user/db-level privilege checking
@@ -419,6 +804,7 @@ static my_bool acl_load(THD *thd, TABLE_
   while (!(read_record_info.read_record(&read_record_info)))
   {
     ACL_USER user;
+    bzero(&user, sizeof(user));
     update_hostname(&user.host, get_field(&mem, table->field[0]));
     user.user= get_field(&mem, table->field[1]);
     if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
@@ -430,27 +816,15 @@ static my_bool acl_load(THD *thd, TABLE_
       continue;
     }
 
-    const char *password= get_field(thd->mem_root, table->field[2]);
+    char *password= get_field(&mem, table->field[2]);
     uint password_len= password ? strlen(password) : 0;
+    user.auth_string.str= password ? password : const_cast<char*>("");
+    user.auth_string.length= password_len;
     set_user_salt(&user, password, password_len);
-    if (user.salt_len == 0 && password_len != 0)
-    {
-      switch (password_len) {
-      case 45: /* 4.1: to be removed */
-        sql_print_warning("Found 4.1 style password for user '%s@%s'. "
-                          "Ignoring user. "
-                          "You should change password for this user.",
-                          user.user ? user.user : "",
-                          user.host.hostname ? user.host.hostname : "");
-        break;
-      default:
-        sql_print_warning("Found invalid password for user: '%s@%s'; "
-                          "Ignoring user", user.user ? user.user : "",
-                           user.host.hostname ? user.host.hostname : "");
-        break;
-      }
-    }
-    else                                        // password is correct
+
+    if (set_user_plugin(&user, password_len))
+      continue;
+    
     {
       uint next_field;
       user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
@@ -527,13 +901,43 @@ static my_bool acl_load(THD *thd, TABLE_
           ptr= get_field(thd->mem_root, table->field[next_field++]);
           user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
         }
-        else
-          user.user_resource.user_conn= 0;
+
+        if (table->s->fields >= 41)
+        {
+          /* We may have plugin & auth_String fields */
+          char *tmpstr= get_field(&mem, table->field[next_field++]);
+          if (tmpstr)
+          {
+            if (user.auth_string.length)
+            {
+              sql_print_warning("'user' entry '%s@%s' has both a password "
+                                "and an authentication plugin specified. The "
+                                "password will be ignored.",
+                                user.user ? user.user : "",
+                                user.host.hostname ? user.host.hostname : "");
+            }
+            if (my_strcasecmp(system_charset_info, tmpstr,
+                              native_password_plugin_name.str) == 0)
+              user.plugin= native_password_plugin_name;
+            else
+              if (my_strcasecmp(system_charset_info, tmpstr,
+                                old_password_plugin_name.str) == 0)
+                user.plugin= old_password_plugin_name;
+              else
+              {
+                user.plugin.str= tmpstr;
+                user.plugin.length= strlen(tmpstr);
+              }
+            user.auth_string.str= get_field(&mem, table->field[next_field++]);
+            if (!user.auth_string.str)
+              user.auth_string.str= const_cast<char*>("");
+            user.auth_string.length= strlen(user.auth_string.str);
+          }
+        }
       }
       else
       {
         user.ssl_type=SSL_TYPE_NONE;
-        bzero((char *)&(user.user_resource),sizeof(user.user_resource));
 #ifndef TO_BE_REMOVED
         if (table->s->fields <= 13)
         {						// Without grant
@@ -617,6 +1021,27 @@ static my_bool acl_load(THD *thd, TABLE_
 	   sizeof(ACL_DB),(qsort_cmp) acl_compare);
   end_read_record(&read_record_info);
   freeze_size(&acl_dbs);
+
+  init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1, 
+                   0, FALSE);
+  table->use_all_columns();
+  (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 
+                               50, 100);
+  while (!(read_record_info.read_record(&read_record_info)))
+  {
+    ACL_PROXY_USER proxy;
+    proxy.init(table, &mem);
+    if (proxy.check_validity(check_no_resolve))
+      continue;
+    if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
+      return TRUE;
+  }
+  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
+           acl_proxy_users.elements,
+           sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
+  end_read_record(&read_record_info);
+  freeze_size(&acl_proxy_users);
+
   init_check_host();
 
   initialized=1;
@@ -635,7 +1060,10 @@ void acl_free(bool end)
   delete_dynamic(&acl_users);
   delete_dynamic(&acl_dbs);
   delete_dynamic(&acl_wild_hosts);
+  delete_dynamic(&acl_proxy_users);
   my_hash_free(&acl_check_hosts);
+  plugin_unlock(0, native_password_plugin);
+  plugin_unlock(0, old_password_plugin);
   if (!end)
     acl_cache->clear(1); /* purecov: inspected */
   else
@@ -667,8 +1095,8 @@ void acl_free(bool end)
 
 my_bool acl_reload(THD *thd)
 {
-  TABLE_LIST tables[3];
-  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
+  TABLE_LIST tables[4];
+  DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
   MEM_ROOT old_mem;
   bool old_initialized;
   my_bool return_val= TRUE;
@@ -684,9 +1112,14 @@ my_bool acl_reload(THD *thd)
                            C_STRING_WITH_LEN("user"), "user", TL_READ);
   tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
                            C_STRING_WITH_LEN("db"), "db", TL_READ);
-  tables[0].next_local= tables[0].next_global= tables+1;
-  tables[1].next_local= tables[1].next_global= tables+2;
-  tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
+  tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
+                           C_STRING_WITH_LEN("proxy_priv"), 
+                           "proxy_priv", TL_READ);
+  tables[0].next_local= tables[0].next_global= tables + 1;
+  tables[1].next_local= tables[1].next_global= tables + 2;
+  tables[2].next_local= tables[2].next_global= tables + 3;
+  tables[0].open_type= tables[1].open_type= tables[2].open_type= 
+  tables[3].open_type= OT_BASE_ONLY;
 
   if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
   {
@@ -703,10 +1136,11 @@ my_bool acl_reload(THD *thd)
   if ((old_initialized=initialized))
     mysql_mutex_lock(&acl_cache->lock);
 
-  old_acl_hosts=acl_hosts;
-  old_acl_users=acl_users;
-  old_acl_dbs=acl_dbs;
-  old_mem=mem;
+  old_acl_hosts= acl_hosts;
+  old_acl_users= acl_users;
+  old_acl_proxy_users= acl_proxy_users;
+  old_acl_dbs= acl_dbs;
+  old_mem= mem;
   delete_dynamic(&acl_wild_hosts);
   my_hash_free(&acl_check_hosts);
 
@@ -714,10 +1148,11 @@ my_bool acl_reload(THD *thd)
   {					// Error. Revert to old list
     DBUG_PRINT("error",("Reverting to old privileges"));
     acl_free();				/* purecov: inspected */
-    acl_hosts=old_acl_hosts;
-    acl_users=old_acl_users;
-    acl_dbs=old_acl_dbs;
-    mem=old_mem;
+    acl_hosts= old_acl_hosts;
+    acl_users= old_acl_users;
+    acl_proxy_users= old_acl_proxy_users;
+    acl_dbs= old_acl_dbs;
+    mem= old_mem;
     init_check_host();
   }
   else
@@ -725,6 +1160,7 @@ my_bool acl_reload(THD *thd)
     free_root(&old_mem,MYF(0));
     delete_dynamic(&old_acl_hosts);
     delete_dynamic(&old_acl_users);
+    delete_dynamic(&old_acl_proxy_users);
     delete_dynamic(&old_acl_dbs);
   }
   if (old_initialized)
@@ -830,246 +1266,10 @@ static int acl_compare(ACL_ACCESS *a,ACL
 
 
 /*
-  Seek ACL entry for a user, check password, SSL cypher, and if
-  everything is OK, update THD user data and USER_RESOURCES struct.
-
-  IMPLEMENTATION
-   This function does not check if the user has any sensible privileges:
-   only user's existence and  validity is checked.
-   Note, that entire operation is protected by acl_cache_lock.
+  Gets user credentials without authentication and resource limit checks.
 
   SYNOPSIS
     acl_getroot()
-    thd         thread handle. If all checks are OK,
-                thd->security_ctx->priv_user/master_access are updated.
-                thd->security_ctx->host/ip/user are used for checks.
-    mqh         user resources; on success mqh is reset, else
-                unchanged
-    passwd      scrambled & crypted password, received from client
-                (to check): thd->scramble or thd->scramble_323 is
-                used to decrypt passwd, so they must contain
-                original random string,
-    passwd_len  length of passwd, must be one of 0, 8,
-                SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
-    'thd' and 'mqh' are updated on success; other params are IN.
-  
-  RETURN VALUE
-    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
-       updated
-    1  user not found or authentication failure
-    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
-   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
-*/
-
-int acl_getroot(THD *thd, USER_RESOURCES  *mqh,
-                const char *passwd, uint passwd_len)
-{
-  ulong user_access= NO_ACCESS;
-  int res= 1;
-  ACL_USER *acl_user= 0;
-  Security_context *sctx= thd->security_ctx;
-  DBUG_ENTER("acl_getroot");
-
-  if (!initialized)
-  {
-    /* 
-      here if mysqld's been started with --skip-grant-tables option.
-    */
-    sctx->skip_grants();
-    bzero((char*) mqh, sizeof(*mqh));
-    DBUG_RETURN(0);
-  }
-
-  mysql_mutex_lock(&acl_cache->lock);
-
-  /*
-    Find acl entry in user database. Note, that find_acl_user is not the same,
-    because it doesn't take into account the case when user is not empty,
-    but acl_user->user is empty
-  */
-
-  for (uint i=0 ; i < acl_users.elements ; i++)
-  {
-    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
-    if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
-    {
-      if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
-      {
-        /* check password: it should be empty or valid */
-        if (passwd_len == acl_user_tmp->salt_len)
-        {
-          if (acl_user_tmp->salt_len == 0 ||
-              (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
-              check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
-              check_scramble_323(passwd, thd->scramble,
-                                 (ulong *) acl_user_tmp->salt)) == 0)
-          {
-            acl_user= acl_user_tmp;
-            res= 0;
-          }
-        }
-        else if (passwd_len == SCRAMBLE_LENGTH &&
-                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
-          res= -1;
-        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
-                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
-          res= 2;
-        /* linear search complete: */
-        break;
-      }
-    }
-  }
-  /*
-    This was moved to separate tree because of heavy HAVE_OPENSSL case.
-    If acl_user is not null, res is 0.
-  */
-
-  if (acl_user)
-  {
-    /* OK. User found and password checked continue validation */
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-    Vio *vio=thd->net.vio;
-    SSL *ssl= (SSL*) vio->ssl_arg;
-    X509 *cert;
-#endif
-
-    /*
-      At this point we know that user is allowed to connect
-      from given host by given username/password pair. Now
-      we check if SSL is required, if user is using SSL and
-      if X509 certificate attributes are OK
-    */
-    switch (acl_user->ssl_type) {
-    case SSL_TYPE_NOT_SPECIFIED:		// Impossible
-    case SSL_TYPE_NONE:				// SSL is not required
-      user_access= acl_user->access;
-      break;
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
-    case SSL_TYPE_ANY:				// Any kind of SSL is ok
-      if (vio_type(vio) == VIO_TYPE_SSL)
-	user_access= acl_user->access;
-      break;
-    case SSL_TYPE_X509: /* Client should have any valid certificate. */
-      /*
-	Connections with non-valid certificates are dropped already
-	in sslaccept() anyway, so we do not check validity here.
-
-	We need to check for absence of SSL because without SSL
-	we should reject connection.
-      */
-      if (vio_type(vio) == VIO_TYPE_SSL &&
-	  SSL_get_verify_result(ssl) == X509_V_OK &&
-	  (cert= SSL_get_peer_certificate(ssl)))
-      {
-	user_access= acl_user->access;
-        X509_free(cert);
-      }
-      break;
-    case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
-      /*
-	We do not check for absence of SSL because without SSL it does
-	not pass all checks here anyway.
-	If cipher name is specified, we compare it to actual cipher in
-	use.
-      */
-      if (vio_type(vio) != VIO_TYPE_SSL ||
-	  SSL_get_verify_result(ssl) != X509_V_OK)
-	break;
-      if (acl_user->ssl_cipher)
-      {
-	DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
-			   acl_user->ssl_cipher,SSL_get_cipher(ssl)));
-	if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
-	  user_access= acl_user->access;
-	else
-	{
-	  if (global_system_variables.log_warnings)
-	    sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
-			      acl_user->ssl_cipher,
-			      SSL_get_cipher(ssl));
-	  break;
-	}
-      }
-      /* Prepare certificate (if exists) */
-      DBUG_PRINT("info",("checkpoint 1"));
-      if (!(cert= SSL_get_peer_certificate(ssl)))
-      {
-	user_access=NO_ACCESS;
-	break;
-      }
-      DBUG_PRINT("info",("checkpoint 2"));
-      /* If X509 issuer is specified, we check it... */
-      if (acl_user->x509_issuer)
-      {
-        DBUG_PRINT("info",("checkpoint 3"));
-        char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
-        DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
-			   acl_user->x509_issuer, ptr));
-        if (strcmp(acl_user->x509_issuer, ptr))
-        {
-          if (global_system_variables.log_warnings)
-            sql_print_information("X509 issuer mismatch: should be '%s' "
-			      "but is '%s'", acl_user->x509_issuer, ptr);
-          free(ptr);
-          X509_free(cert);
-          user_access=NO_ACCESS;
-          break;
-        }
-        user_access= acl_user->access;
-        free(ptr);
-      }
-      DBUG_PRINT("info",("checkpoint 4"));
-      /* X509 subject is specified, we check it .. */
-      if (acl_user->x509_subject)
-      {
-        char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
-        DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
-                           acl_user->x509_subject, ptr));
-        if (strcmp(acl_user->x509_subject,ptr))
-        {
-          if (global_system_variables.log_warnings)
-            sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
-                            acl_user->x509_subject, ptr);
-          free(ptr);
-          X509_free(cert);
-          user_access=NO_ACCESS;
-          break;
-        }
-        user_access= acl_user->access;
-        free(ptr);
-      }
-      /* Deallocate the X509 certificate. */
-      X509_free(cert);
-      break;
-#else  /* HAVE_OPENSSL */
-    default:
-      /*
-        If we don't have SSL but SSL is required for this user the 
-        authentication should fail.
-      */
-      break;
-#endif /* HAVE_OPENSSL */
-    }
-    sctx->master_access= user_access;
-    sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
-    *mqh= acl_user->user_resource;
-
-    if (acl_user->host.hostname)
-      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
-    else
-      *sctx->priv_host= 0;
-  }
-  mysql_mutex_unlock(&acl_cache->lock);
-  DBUG_RETURN(res);
-}
-
-
-/*
-  This is like acl_getroot() above, but it doesn't check password,
-  and we don't care about the user resources.
-
-  SYNOPSIS
-    acl_getroot_no_password()
       sctx               Context which should be initialized
       user               user name
       host               host name
@@ -1081,13 +1281,13 @@ int acl_getroot(THD *thd, USER_RESOURCES
     TRUE   Error
 */
 
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
-                             char *ip, char *db)
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+                 char *ip, char *db)
 {
   int res= 1;
   uint i;
   ACL_USER *acl_user= 0;
-  DBUG_ENTER("acl_getroot_no_password");
+  DBUG_ENTER("acl_getroot");
 
   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                        (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
@@ -1110,8 +1310,7 @@ bool acl_getroot_no_password(Security_co
 
   sctx->master_access= 0;
   sctx->db_access= 0;
-  sctx->priv_user= (char *) "";
-  *sctx->priv_host= 0;
+  *sctx->priv_user= *sctx->priv_host= 0;
 
   /*
      Find acl entry in user database.
@@ -1153,7 +1352,11 @@ bool acl_getroot_no_password(Security_co
       }
     }
     sctx->master_access= acl_user->access;
-    sctx->priv_user= acl_user->user ? user : (char *) "";
+
+    if (acl_user->user)
+      strmake(sctx->priv_user, user, USERNAME_LENGTH);
+    else
+      *sctx->priv_user= 0;
 
     if (acl_user->host.hostname)
       strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
@@ -1179,7 +1382,9 @@ static void acl_update_user(const char *
 			    const char *x509_issuer,
 			    const char *x509_subject,
 			    USER_RESOURCES  *mqh,
-			    ulong privileges)
+			    ulong privileges,
+			    const LEX_STRING *plugin,
+			    const LEX_STRING *auth)
 {
   mysql_mutex_assert_owner(&acl_cache->lock);
 
@@ -1193,6 +1398,14 @@ static void acl_update_user(const char *
 	  (acl_user->host.hostname &&
 	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
       {
+        if (plugin->str[0])
+        {
+          acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+          acl_user->plugin.length= plugin->length;
+          acl_user->auth_string.str= auth->str ?
+            strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+          acl_user->auth_string.length= auth->length;
+        }
 	acl_user->access=privileges;
 	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
 	  acl_user->user_resource.questions=mqh->questions;
@@ -1229,7 +1442,9 @@ static void acl_insert_user(const char *
 			    const char *x509_issuer,
 			    const char *x509_subject,
 			    USER_RESOURCES *mqh,
-			    ulong privileges)
+			    ulong privileges,
+			    const LEX_STRING *plugin,
+			    const LEX_STRING *auth)
 {
   ACL_USER acl_user;
 
@@ -1237,6 +1452,22 @@ static void acl_insert_user(const char *
 
   acl_user.user=*user ? strdup_root(&mem,user) : 0;
   update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
+  if (plugin->str[0])
+  {
+    acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+    acl_user.plugin.length= plugin->length;
+    acl_user.auth_string.str= auth->str ?
+      strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+    acl_user.auth_string.length= auth->length;
+  }
+  else
+  {
+    acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
+      old_password_plugin_name : native_password_plugin_name;
+    acl_user.auth_string.str= strmake_root(&mem, password, password_len);
+    acl_user.auth_string.length= password_len;
+  }
+
   acl_user.access=privileges;
   acl_user.user_resource = *mqh;
   acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
@@ -1632,8 +1863,18 @@ bool change_password(THD *thd, const cha
     my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
     goto end;
   }
+
+  if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
+                    native_password_plugin_name.str) &&
+      my_strcasecmp(system_charset_info, acl_user->plugin.str,
+                    old_password_plugin_name.str))
+  {
+    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+                 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
+  }
   /* update loaded acl entry: */
   set_user_salt(acl_user, new_password, new_password_len);
+  set_user_plugin(acl_user, new_password_len);
 
   if (update_user_table(thd, table,
 			acl_user->host.hostname ? acl_user->host.hostname : "",
@@ -2013,7 +2254,7 @@ static int replace_user_table(THD *thd, 
 
       see also test_if_create_new_users()
     */
-    else if (!password_len && no_auto_create)
+    else if (!password_len && !combo.plugin.length && no_auto_create)
     {
       my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo.user.str, combo.host.str);
       goto end;
@@ -2024,6 +2265,15 @@ static int replace_user_table(THD *thd, 
                thd->security_ctx->user, thd->security_ctx->host_or_ip);
       goto end;
     }
+    else if (combo.plugin.str[0])
+    {
+      if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
+      {
+        my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
+        goto end;
+      }
+    }
+
     old_row_exists = 0;
     restore_record(table,s->default_values);
     table->field[0]->store(combo.host.str,combo.host.length,
@@ -2037,7 +2287,14 @@ static int replace_user_table(THD *thd, 
   {
     old_row_exists = 1;
     store_record(table,record[1]);			// Save copy for update
-    if (combo.password.str)			// If password given
+    /* what == 'N' means revoke */
+    if (combo.plugin.length && what != 'N')
+    {
+        my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0), combo.user.length, 
+                 combo.user.str);
+        goto end;
+    }
+    if (combo.password.str)                             // If password given
       table->field[2]->store(password, password_len, system_charset_info);
     else if (!rights && !revoke_grant &&
              lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
@@ -2118,10 +2375,28 @@ static int replace_user_table(THD *thd, 
         (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
       table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
     mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
-  }
-  if (old_row_exists)
-  {
-    /*
+
+    next_field+= 4;
+    if (combo.plugin.str[0])
+    {
+      if (table->s->fields >= 41 && combo.plugin.str[0])
+      {
+        table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
+                                        system_charset_info);
+        table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
+                                            system_charset_info);
+      }
+      else
+      {
+        my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
+        goto end;
+      }
+    }
+  }
+
+  if (old_row_exists)
+  {
+    /*
       We should NEVER delete from the user table, as a uses can still
       use mysqld even if he doesn't have any privileges in the user table!
     */
@@ -2162,7 +2437,9 @@ end:
 		      lex->x509_issuer,
 		      lex->x509_subject,
 		      &lex->mqh,
-		      rights);
+		      rights,
+		      &combo.plugin,
+		      &combo.auth);
     else
       acl_insert_user(combo.user.str, combo.host.str, password, password_len,
 		      lex->ssl_type,
@@ -2170,7 +2447,9 @@ end:
 		      lex->x509_issuer,
 		      lex->x509_subject,
 		      &lex->mqh,
-		      rights);
+		      rights,
+		      &combo.plugin,
+		      &combo.auth);
   }
   DBUG_RETURN(error);
 }
@@ -2285,6 +2564,160 @@ abort:
 }
 
 
+static void  
+acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
+{
+  mysql_mutex_assert_owner(&acl_cache->lock);
+
+  DBUG_ENTER("acl_update_proxy_user");
+  for (uint i= 0; i < acl_proxy_users.elements; i++)
+  {
+    ACL_PROXY_USER *acl_user= 
+      dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
+
+    if (acl_user->pk_equals(new_value))
+    {
+      if (is_revoke)
+      {
+        DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
+        delete_dynamic_element(&acl_proxy_users, i);
+      }
+      else
+      {
+        DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
+        acl_user->set_data(new_value);
+      }
+      break;
+    }
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+static void  
+acl_insert_proxy_user(ACL_PROXY_USER *new_value)
+{
+  DBUG_ENTER("acl_insert_proxy_user");
+  mysql_mutex_assert_owner(&acl_cache->lock);
+  (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
+  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
+           acl_proxy_users.elements,
+           sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
+  DBUG_VOID_RETURN;
+}
+
+
+static int 
+replace_proxy_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
+                         const LEX_USER *proxied_user, bool with_grant_arg, 
+                         bool revoke_grant)
+{
+  bool old_row_exists= 0;
+  int error;
+  uchar user_key[MAX_KEY_LENGTH];
+  ACL_PROXY_USER new_grant;
+
+  DBUG_ENTER("replace_proxy_priv_table");
+
+  if (!initialized)
+  {
+    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+    DBUG_RETURN(-1);
+  }
+
+  /* Check if there is such a user in user table in memory? */
+  if (!find_acl_user(user->host.str,user->user.str, FALSE))
+  {
+    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
+    DBUG_RETURN(-1);
+  }
+
+  table->use_all_columns();
+  ACL_PROXY_USER::store_pk (table, &user->host, &user->user, 
+                            &proxied_user->host, &proxied_user->user);
+
+  key_copy(user_key, table->record[0], table->key_info,
+           table->key_info->key_length);
+
+  table->file->ha_index_init(0, 1);
+  if (table->file->index_read_map(table->record[0], user_key,
+                                      HA_WHOLE_KEY,
+                                      HA_READ_KEY_EXACT))
+  {
+    DBUG_PRINT ("info", ("Row not found"));
+    if (revoke_grant)
+    { // no row, no revoke
+      my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
+      goto abort;
+    }
+    old_row_exists= 0;
+    restore_record(table, s->default_values);
+    ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
+                                      &proxied_user->host,
+                                      &proxied_user->user,
+                                      with_grant_arg);
+  }
+  else
+  {
+    DBUG_PRINT("info", ("Row found"));
+    old_row_exists= 1;
+    store_record(table, record[1]);
+  }
+
+  if (old_row_exists)
+  {
+    /* update old existing row */
+    if (!revoke_grant)
+    {
+      if ((error= table->file->ha_update_row(table->record[1],
+                                             table->record[0])) &&
+          error != HA_ERR_RECORD_IS_THE_SAME)
+	goto table_error;			/* purecov: inspected */
+    }
+    else
+    {
+      if ((error= table->file->ha_delete_row(table->record[1])))
+	goto table_error;			/* purecov: inspected */
+    }
+  }
+  else if ((error= table->file->ha_write_row(table->record[0])))
+  {
+    DBUG_PRINT("info", ("error inserting the row"));
+    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+      goto table_error; /* purecov: inspected */
+  }
+
+  acl_cache->clear(1);				// Clear privilege cache
+  if (old_row_exists)
+  {
+    new_grant.init(user->host.str, user->user.str,
+                   proxied_user->host.str, proxied_user->user.str,
+                   with_grant_arg);
+    acl_update_proxy_user(&new_grant, revoke_grant);
+  }
+  else
+  {
+    new_grant.init(&mem, user->host.str, user->user.str,
+                   proxied_user->host.str, proxied_user->user.str,
+                   with_grant_arg);
+    acl_insert_proxy_user(&new_grant);
+  }
+
+  table->file->ha_index_end();
+  DBUG_RETURN(0);
+
+  /* This could only happen if the grant tables got corrupted */
+table_error:
+  DBUG_PRINT("info", ("table error"));
+  table->file->print_error(error, MYF(0));	/* purecov: inspected */
+
+abort:
+  DBUG_PRINT("info", ("aborting replace_proxy_priv_table"));
+  table->file->ha_index_end();
+  DBUG_RETURN(-1);
+}
+
+
 class GRANT_COLUMN :public Sql_alloc
 {
 public:
@@ -3500,10 +3933,10 @@ bool mysql_routine_grant(THD *thd, TABLE
 
 
 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
-                 ulong rights, bool revoke_grant)
+                 ulong rights, bool revoke_grant, bool is_proxy)
 {
   List_iterator <LEX_USER> str_list (list);
-  LEX_USER *Str, *tmp_Str;
+  LEX_USER *Str, *tmp_Str, *proxied_user;
   char tmp_db[NAME_LEN+1];
   bool create_new_users=0;
   TABLE_LIST tables[2];
@@ -3523,11 +3956,26 @@ bool mysql_grant(THD *thd, const char *d
     db=tmp_db;
   }
 
-  /* open the mysql.user and mysql.db tables */
+  if (is_proxy)
+  {
+    DBUG_ASSERT(!db);
+    proxied_user= str_list++;
+  }
+
+  /* open the mysql.user and mysql.db or mysql.proxy_priv tables */
   tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                            C_STRING_WITH_LEN("user"), "user", TL_WRITE);
-  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
-                           C_STRING_WITH_LEN("db"), "db", TL_WRITE);
+  if (is_proxy)
+
+    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+                             C_STRING_WITH_LEN("proxy_priv"),
+                             "proxy_priv", 
+                             TL_WRITE);
+  else
+    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+                             C_STRING_WITH_LEN("db"), 
+                             "db", 
+                             TL_WRITE);
   tables[0].next_local= tables[0].next_global= tables+1;
 
   /*
@@ -3613,6 +4061,13 @@ bool mysql_grant(THD *thd, const char *d
 	result= -1;
       }
     }
+    else if (is_proxy)
+    {
+      if (replace_proxy_priv_table (thd, tables[1].table, Str, proxied_user,
+                                    rights & GRANT_ACL ? TRUE : FALSE, 
+                                    revoke_grant))
+        result= -1;
+    }
   }
   mysql_mutex_unlock(&acl_cache->lock);
 
@@ -5047,6 +5502,12 @@ bool mysql_show_grants(THD *thd,LEX_USER
     goto end;
   }
 
+  if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
+  {
+    error= -1;
+    goto end;
+  }
+
 end:
   mysql_mutex_unlock(&acl_cache->lock);
   mysql_rwlock_unlock(&LOCK_grant);
@@ -5204,7 +5665,7 @@ void get_mqh(const char *user, const cha
     < 0         Error.
 */
 
-#define GRANT_TABLES 5
+#define GRANT_TABLES 6
 int open_grant_tables(THD *thd, TABLE_LIST *tables)
 {
   DBUG_ENTER("open_grant_tables");
@@ -5228,10 +5689,14 @@ int open_grant_tables(THD *thd, TABLE_LI
   (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
                              C_STRING_WITH_LEN("procs_priv"),
                              "procs_priv", TL_WRITE);
-  tables->next_local= tables->next_global= tables+1;
-  (tables+1)->next_local= (tables+1)->next_global= tables+2;
-  (tables+2)->next_local= (tables+2)->next_global= tables+3;
-  (tables+3)->next_local= (tables+3)->next_global= tables+4;
+  (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
+                             C_STRING_WITH_LEN("proxy_priv"),
+                             "proxy_priv", TL_WRITE);
+  tables->next_local= tables->next_global= tables + 1;
+  (tables+1)->next_local= (tables+1)->next_global= tables + 2;
+  (tables+2)->next_local= (tables+2)->next_global= tables + 3;
+  (tables+3)->next_local= (tables+3)->next_global= tables + 4;
+  (tables+4)->next_local= (tables+4)->next_global= tables + 5;
 
 #ifdef HAVE_REPLICATION
   /*
@@ -5244,12 +5709,12 @@ int open_grant_tables(THD *thd, TABLE_LI
       The tables must be marked "updating" so that tables_ok() takes them into
       account in tests.
     */
-    tables[0].updating=tables[1].updating=tables[2].updating=
-      tables[3].updating=tables[4].updating=1;
+    tables[0].updating= tables[1].updating= tables[2].updating=
+      tables[3].updating= tables[4].updating= tables[5].updating= 1;
     if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
       DBUG_RETURN(1);
-    tables[0].updating=tables[1].updating=tables[2].updating=
-      tables[3].updating=tables[4].updating=0;;
+    tables[0].updating= tables[1].updating= tables[2].updating=
+      tables[3].updating= tables[4].updating= tables[5].updating= 0;
   }
 #endif
 
@@ -5378,7 +5843,7 @@ static int handle_grant_table(TABLE_LIST
   int error;
   TABLE *table= tables[table_no].table;
   Field *host_field= table->field[0];
-  Field *user_field= table->field[table_no ? 2 : 1];
+  Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
   char *host_str= user_from->host.str;
   char *user_str= user_from->user.str;
   const char *host;
@@ -5461,12 +5926,15 @@ static int handle_grant_table(TABLE_LIST
           user= "";
 
 #ifdef EXTRA_DEBUG
-        DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
-                           user, host,
-                           get_field(thd->mem_root, table->field[1]) /*db*/,
-                           get_field(thd->mem_root, table->field[3]) /*table*/,
-                           get_field(thd->mem_root,
-                                     table->field[4]) /*column*/));
+        if (table_no != 5)
+        {
+          DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
+                             user, host,
+                             get_field(thd->mem_root, table->field[1]) /*db*/,
+                             get_field(thd->mem_root, table->field[3]) /*table*/,
+                             get_field(thd->mem_root,
+                                       table->field[4]) /*column*/));
+        }
 #endif
         if (strcmp(user_str, user) ||
             my_strcasecmp(system_charset_info, host_str, host))
@@ -5528,6 +5996,7 @@ static int handle_grant_struct(uint stru
   const char *host;
   ACL_USER *acl_user= NULL;
   ACL_DB *acl_db= NULL;
+  ACL_PROXY_USER *acl_proxy_user= NULL;
   GRANT_NAME *grant_name= NULL;
   DBUG_ENTER("handle_grant_struct");
   DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
@@ -5552,6 +6021,9 @@ static int handle_grant_struct(uint stru
   case 3:
     elements= proc_priv_hash.records;
     break;
+  case 5:
+    elements= acl_proxy_users.elements;
+    break;
   default:
     return -1;
   }
@@ -5590,6 +6062,11 @@ static int handle_grant_struct(uint stru
       user= grant_name->user;
       host= grant_name->host.hostname;
       break;
+    case 5:
+      acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
+      user= acl_proxy_user->get_user();
+      host= acl_proxy_user->get_host();
+      break;
     default:
       assert(0);
     }
@@ -5625,6 +6102,11 @@ static int handle_grant_struct(uint stru
       case 3:
         my_hash_delete(&proc_priv_hash, (uchar*) grant_name);
 	break;
+
+      case 5:
+        delete_dynamic_element(&acl_proxy_users, idx);
+        break;
+
       }
       elements--;
       idx--;
@@ -5660,6 +6142,12 @@ static int handle_grant_struct(uint stru
         my_hash_update(&column_priv_hash, (uchar*) grant_name,
                        (uchar*) grant_name->hash_key, grant_name->key_length);
 	break;
+
+      case 5:
+        acl_proxy_user->set_user (&mem, user_to->user.str);
+        acl_proxy_user->set_host (&mem, user_to->host.str);
+        break;
+
       }
     }
     else
@@ -5794,6 +6282,20 @@ static int handle_grant_data(TABLE_LIST 
         result= 1; /* At least one record/element found. */
     }
   }
+
+  /* Handle proxy_priv table. */
+  if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
+  {
+    /* Handle of table failed, don't touch the in-memory array. */
+    result= -1;
+  }
+  else
+  {
+    /* Handle proxy_priv array. */
+    if ((handle_grant_struct(5, drop, user_from, user_to) && !result) ||
+        found)
+      result= 1; /* At least one record/element found. */
+  }
  end:
   DBUG_RETURN(result);
 }
@@ -6480,38 +6982,44 @@ bool sp_grant_privileges(THD *thd, const
   tables->db= (char*)sp_db;
   tables->table_name= tables->alias= (char*)sp_name;
 
-  combo->host.length= strlen(combo->host.str);
-  combo->user.length= strlen(combo->user.str);
-  combo->host.str= thd->strmake(combo->host.str,combo->host.length);
-  combo->user.str= thd->strmake(combo->user.str,combo->user.length);
+  thd->make_lex_string(&combo->user,
+                       combo->user.str, strlen(combo->user.str), 0);
+  thd->make_lex_string(&combo->host,
+                       combo->host.str, strlen(combo->host.str), 0);
 
+  combo->password= empty_lex_str;
+  combo->plugin= empty_lex_str;
+  combo->auth= empty_lex_str;
 
-  if(au && au->salt_len)
+  if(au)
   {
-    if (au->salt_len == SCRAMBLE_LENGTH)
+    if (au->salt_len)
     {
-      make_password_from_salt(passwd_buff, au->salt);
-      combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
-    }
-    else if (au->salt_len == SCRAMBLE_LENGTH_323)
-    {
-      make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
-      combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+      if (au->salt_len == SCRAMBLE_LENGTH)
+      {
+        make_password_from_salt(passwd_buff, au->salt);
+        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+      }
+      else if (au->salt_len == SCRAMBLE_LENGTH_323)
+      {
+        make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
+        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+      }
+      else
+      {
+        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
+                            ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+        return TRUE;
+      }
+      combo->password.str= passwd_buff;
     }
-    else
+
+    if (au->plugin.str != native_password_plugin_name.str &&
+        au->plugin.str != old_password_plugin_name.str)
     {
-      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                          ER_PASSWD_LENGTH,
-                          ER(ER_PASSWD_LENGTH),
-                          SCRAMBLED_PASSWORD_CHAR_LENGTH);
-      return TRUE;
+      combo->plugin= au->plugin;
+      combo->auth= au->auth_string;
     }
-    combo->password.str= passwd_buff;
-  }
-  else
-  {
-    combo->password.str= (char*)"";
-    combo->password.length= 0;
   }
 
   if (user_list.push_back(combo))
@@ -6544,6 +7052,129 @@ template class List<LEX_COLUMN>;
 template class List<LEX_USER>;
 #endif
 
+/**
+  Validate if a user can proxy as another user
+
+  @thd                     current thread
+  @param user              the logged in user (proxy user)
+  @param authenticated_as  the effective user a plugin is trying to 
+                           impersonate as (proxied user)
+  @return                  proxy user definition
+    @retval NULL           proxy user definition not found or not applicable
+    @retval non-null       the proxy user data
+*/
+
+static ACL_PROXY_USER *
+acl_find_proxy_user(const char *user, const char *host, const char *ip, 
+                    const char *authenticated_as, bool *proxy_used)
+{
+  uint i;
+  /* if the proxied and proxy user are the same return OK */
+  DBUG_ENTER("acl_find_proxy_user");
+  DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
+                      user, host, ip, authenticated_as));
+
+  if (!strcmp(authenticated_as, user))
+  {
+    DBUG_PRINT ("info", ("user is the same as authenticated_as"));
+    DBUG_RETURN (NULL);
+  }
+
+  *proxy_used= TRUE; 
+  for (i=0; i < acl_proxy_users.elements; i++)
+  {
+    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
+                                           ACL_PROXY_USER *);
+    if (proxy->matches(host, user, ip, authenticated_as))
+      DBUG_RETURN(proxy);
+  }
+
+  DBUG_RETURN(NULL);
+}
+
+
+bool
+acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
+                             bool with_grant)
+{
+  DBUG_ENTER("acl_check_proxy_grant_access");
+  DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host, 
+                      (int) with_grant));
+  if (!initialized)
+  {
+    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+    DBUG_RETURN(1);
+  }
+
+  /* replication slave thread can do anything */
+  if (thd->slave_thread)
+  {
+    DBUG_PRINT("info", ("replication slave"));
+    DBUG_RETURN(FALSE);
+  }
+
+  /* one can grant proxy to himself to others */
+  if (!strcmp(thd->security_ctx->user, user) &&
+      !my_strcasecmp(system_charset_info, host,
+                     thd->security_ctx->host))
+  {
+    DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", 
+                        thd->security_ctx->user, user,
+                        host, thd->security_ctx->host));
+    DBUG_RETURN(FALSE);
+  }
+
+  /* check for matching WITH PROXY rights */
+  for (uint i=0; i < acl_proxy_users.elements; i++)
+  {
+    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
+                                           ACL_PROXY_USER *);
+    if (proxy->matches(thd->security_ctx->host,
+                       thd->security_ctx->user,
+                       thd->security_ctx->ip,
+                       user) &&
+        proxy->get_with_grant())
+    {
+      DBUG_PRINT("info", ("found"));
+      DBUG_RETURN(FALSE);
+    }
+  }
+
+  my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+           thd->security_ctx->user,
+           thd->security_ctx->host_or_ip);
+  DBUG_RETURN(TRUE);
+}
+
+
+static bool
+show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
+{
+  Protocol *protocol= thd->protocol;
+  int error= 0;
+
+  for (uint i=0; i < acl_proxy_users.elements; i++)
+  {
+    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+                                           ACL_PROXY_USER *);
+    if (proxy->granted_on(user->host.str, user->user.str))
+    {
+      String global(buff, buffsize, system_charset_info);
+      global.length(0);
+      proxy->print_grant(&global);
+      protocol->prepare_for_resend();
+      protocol->store(global.ptr(), global.length(), global.charset());
+      if (protocol->write())
+      {
+        error= -1;
+        break;
+      }
+    }
+  }
+  return error;
+}
+
+
 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
 
 
@@ -7111,3 +7742,1629 @@ get_cached_table_access(GRANT_INTERNAL_I
 }
 
 
+/****************************************************************************
+   AUTHENTICATION CODE
+   including initial connect handshake, invoking appropriate plugins,
+   client-server plugin negotiation, COM_CHANGE_USER, and native
+   MySQL authentication plugins.
+****************************************************************************/
+
+/* few defines to have less ifdef's in the code below */
+#ifdef EMBEDDED_LIBRARY
+#undef HAVE_OPENSSL
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+#define initialized 0
+#define decrease_user_connections(X)        /* nothing */
+#define check_for_max_user_connections(X, Y)   0
+#endif
+#endif
+#ifndef HAVE_OPENSSL
+#define ssl_acceptor_fd 0
+#define sslaccept(A,B,C) 1
+#endif
+
+
+class Thd_charset_adapter
+{
+  THD *thd;
+public:
+  Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {} 
+  bool init_client_charset(uint cs_number)
+  {
+    thd_init_client_charset(thd, cs_number);
+    thd->update_charset();
+    return thd->is_error();
+  }
+
+  CHARSET_INFO *charset() { return thd->charset(); }
+};
+
+
+/**
+  The internal version of what plugins know as MYSQL_PLUGIN_VIO,
+  basically the context of the authentication session
+*/
+struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
+{
+  MYSQL_SERVER_AUTH_INFO auth_info;
+  const ACL_USER *acl_user;
+  plugin_ref plugin;        ///< what plugin we're under
+  LEX_STRING db;            ///< db name from the handshake packet
+  /** when restarting a plugin this caches the last client reply */
+  struct {
+    char *plugin, *pkt;     ///< pointers into NET::buff
+    uint pkt_len;
+  } cached_client_reply;
+  /** this caches the first plugin packet for restart request on the client */
+  struct {
+    char *pkt;
+    uint pkt_len;
+  } cached_server_packet;
+  int packets_read, packets_written; ///< counters for send/received packets
+  uint connect_errors;      ///< if there were connect errors for this host
+  /** when plugin returns a failure this tells us what really happened */
+  enum { SUCCESS, FAILURE, RESTART } status;
+
+  /* encapsulation members */
+  ulong client_capabilities;
+  char *scramble;
+  MEM_ROOT *mem_root;
+  struct  rand_struct *rand;
+  my_thread_id  thread_id;
+  uint      *server_status;
+  NET *net;
+  ulong max_client_packet_length;
+  char *ip;
+  char *host;
+  Thd_charset_adapter *charset_adapter;
+  LEX_STRING acl_user_plugin;
+};
+
+/**
+  a helper function to report an access denied error in all the proper places
+*/
+static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
+{
+  THD *thd= current_thd;
+  if (passwd_used == 2)
+  {
+    my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+             mpvio->auth_info.user_name,
+             mpvio->auth_info.host_or_ip);
+    general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
+                      mpvio->auth_info.user_name,
+                      mpvio->auth_info.host_or_ip);
+    /* 
+      Log access denied messages to the error log when log-warnings = 2
+      so that the overhead of the general query log is not required to track 
+      failed connections.
+    */
+    if (global_system_variables.log_warnings > 1)
+    {
+      sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
+                        mpvio->auth_info.user_name,
+                        mpvio->auth_info.host_or_ip);      
+    }
+  }
+  else
+  {
+    my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+             mpvio->auth_info.user_name,
+             mpvio->auth_info.host_or_ip,
+             passwd_used ? ER(ER_YES) : ER(ER_NO));
+    general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+                      mpvio->auth_info.user_name,
+                      mpvio->auth_info.host_or_ip,
+                      passwd_used ? ER(ER_YES) : ER(ER_NO));
+    /* 
+      Log access denied messages to the error log when log-warnings = 2
+      so that the overhead of the general query log is not required to track 
+      failed connections.
+    */
+    if (global_system_variables.log_warnings > 1)
+    {
+      sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
+                        mpvio->auth_info.user_name,
+                        mpvio->auth_info.host_or_ip,
+                        passwd_used ? ER(ER_YES) : ER(ER_NO));      
+    }
+  }
+}
+
+/**
+  sends a server handshake initialization packet, the very first packet
+  after the connection was established
+
+  Packet format:
+   
+    Bytes       Content
+    -----       ----
+    1           protocol version (always 10)
+    n           server version string, \0-terminated
+    4           thread id
+    8           first 8 bytes of the plugin provided data (scramble)
+    1           \0 byte, terminating the first part of a scramble
+    2           server capabilities (two lower bytes)
+    1           server character set
+    2           server status
+    2           server capabilities (two upper bytes)
+    1           length of the scramble
+    10          reserved, always 0
+    n           rest of the plugin provided data (at least 12 bytes)
+    1           \0 byte, terminating the second part of a scramble
+
+  @retval 0 ok
+  @retval 1 error
+*/
+static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
+                                         const char *data, uint data_len)
+{
+  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+  DBUG_ASSERT(data_len <= 255);
+
+  char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
+  char scramble_buf[SCRAMBLE_LENGTH];
+  char *end= buff;
+
+  DBUG_ENTER("send_server_handshake_packet");
+  *end++= protocol_version;
+
+  mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
+
+  if (opt_using_transactions)
+    mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
+
+  mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
+
+  if (ssl_acceptor_fd)
+  {
+    mpvio->client_capabilities|= CLIENT_SSL;
+    mpvio->client_capabilities|= CLIENT_SSL_VERIFY_SERVER_CERT;
+  }
+
+  if (data_len)
+  {
+    mpvio->cached_server_packet.pkt= (char*) memdup_root(mpvio->mem_root, 
+                                                         data, data_len);
+    mpvio->cached_server_packet.pkt_len= data_len;
+  }
+
+  if (data_len < SCRAMBLE_LENGTH)
+  {
+    if (data_len)
+    {
+      /*
+        the first packet *must* have at least 20 bytes of a scramble.
+        if a plugin provided less, we pad it to 20 with zeros
+      */
+      memcpy(scramble_buf, data, data_len);
+      bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
+      data= scramble_buf;
+    }
+    else
+    {
+      /*
+        if the default plugin does not provide the data for the scramble at
+        all, we generate a scramble internally anyway, just in case the
+        user account (that will be known only later) uses a
+        native_password_plugin (which needs a scramble). If we don't send a
+        scramble now - wasting 20 bytes in the packet -
+        native_password_plugin will have to send it in a separate packet,
+        adding one more round trip.
+      */
+      create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+      data= mpvio->scramble;
+    }
+    data_len= SCRAMBLE_LENGTH;
+  }
+
+  end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
+  int4store((uchar*) end, mpvio->thread_id);
+  end+= 4;
+
+  /*
+    Old clients does not understand long scrambles, but can ignore packet
+    tail: that's why first part of the scramble is placed here, and second
+    part at the end of packet.
+  */
+  end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
+  end+= SCRAMBLE_LENGTH_323;
+  *end++= 0;
+ 
+  int2store(end, mpvio->client_capabilities);
+  /* write server characteristics: up to 16 bytes allowed */
+  end[2]= (char) default_charset_info->number;
+  int2store(end + 3, mpvio->server_status[0]);
+  int2store(end + 5, mpvio->client_capabilities >> 16);
+  end[7]= data_len;
+  bzero(end + 8, 10);
+  end+= 18;
+  /* write scramble tail */
+  end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
+                      data_len - SCRAMBLE_LENGTH_323);
+  end+= data_len - SCRAMBLE_LENGTH_323;
+  end= strmake(end, plugin_name(mpvio->plugin)->str,
+                    plugin_name(mpvio->plugin)->length);
+
+  int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end - buff)) ||
+           net_flush(mpvio->net);
+  my_afree(buff);
+  DBUG_RETURN (res);
+}
+
+static bool secure_auth(MPVIO_EXT *mpvio)
+{
+  THD *thd;
+  if (!opt_secure_auth)
+    return 0;
+  /*
+    If the server is running in secure auth mode, short scrambles are 
+    forbidden. Extra juggling to report the same error as the old code.
+  */
+
+  thd= current_thd;
+  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
+  {
+    my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
+             mpvio->auth_info.user_name,
+             mpvio->auth_info.host_or_ip);
+    general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+                      mpvio->auth_info.user_name,
+                      mpvio->auth_info.host_or_ip);
+  }
+  else
+  {
+    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+  }
+  return 1;
+}
+
+/**
+  sends a "change plugin" packet, requesting a client to restart authentication
+  using a different authentication plugin
+
+  Packet format:
+   
+    Bytes       Content
+    -----       ----
+    1           byte with the value 254
+    n           client plugin to use, \0-terminated
+    n           plugin provided data
+
+  In a special case of switching from native_password_plugin to
+  old_password_plugin, the packet contains only one - the first - byte,
+  plugin name is omitted, plugin data aren't needed as the scramble was
+  already sent. This one-byte packet is identical to the "use the short
+  scramble" packet in the protocol before plugins were introduced.
+
+  @retval 0 ok
+  @retval 1 error
+*/
+static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
+                                       const uchar *data, uint data_len)
+{
+  DBUG_ASSERT(mpvio->packets_written == 1);
+  DBUG_ASSERT(mpvio->packets_read == 1);
+  NET *net= mpvio->net;
+  static uchar switch_plugin_request_buf[]= { 254 };
+
+  DBUG_ENTER("send_plugin_request_packet");
+  mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
+
+  const char *client_auth_plugin=
+    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+  DBUG_ASSERT(client_auth_plugin);
+
+  /*
+    we send an old "short 4.0 scramble request", if we need to request a
+    client to use 4.0 auth plugin (short scramble) and the scramble was
+    already sent to the client
+
+    below, cached_client_reply.plugin is the plugin name that client has used,
+    client_auth_plugin is derived from mysql.user table, for the given
+    user account, it's the plugin that the client need to use to login.
+  */
+  bool switch_from_long_to_short_scramble=
+    native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
+    client_auth_plugin == old_password_plugin_name.str;
+
+  if (switch_from_long_to_short_scramble)
+    DBUG_RETURN (secure_auth(mpvio) ||
+                 my_net_write(net, switch_plugin_request_buf, 1) ||
+                 net_flush(net));
+
+  /*
+    We never request a client to switch from a short to long scramble.
+    Plugin-aware clients can do that, but traditionally it meant to
+    ask an old 4.0 client to use the new 4.1 authentication protocol.
+  */
+  bool switch_from_short_to_long_scramble=
+    old_password_plugin_name.str == mpvio->cached_client_reply.plugin && 
+    client_auth_plugin == native_password_plugin_name.str;
+
+  if (switch_from_short_to_long_scramble)
+  {
+    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+    general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    DBUG_RETURN (1);
+  }
+
+  DBUG_PRINT("info", ("requesting client to use the %s plugin", 
+                      client_auth_plugin));
+  DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
+                                (uchar*) client_auth_plugin,
+                                strlen(client_auth_plugin) + 1,
+                                (uchar*) data, data_len));
+}
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+   Finds acl entry in user database for authentication purposes.
+   
+   Finds a user and copies it into mpvio. Reports an authentication
+   failure if a user is not found.
+
+   @note find_acl_user is not the same, because it doesn't take into
+   account the case when user is not empty, but acl_user->user is empty
+
+   @retval 0    found
+   @retval 1    not found
+*/
+static bool find_mpvio_user(MPVIO_EXT *mpvio)
+{
+  DBUG_ENTER("find_mpvio_user");
+  DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
+  DBUG_ASSERT(mpvio->acl_user == 0);
+  mysql_mutex_lock(&acl_cache->lock);
+  for (uint i=0; i < acl_users.elements; i++)
+  {
+    ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
+    if ((!acl_user_tmp->user || 
+         !strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
+        compare_hostname(&acl_user_tmp->host, mpvio->host, mpvio->ip))
+    {
+      mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
+      if (acl_user_tmp->plugin.str == native_password_plugin_name.str ||
+          acl_user_tmp->plugin.str == old_password_plugin_name.str)
+        mpvio->acl_user_plugin= acl_user_tmp->plugin;
+      else
+        make_lex_string_root(mpvio->mem_root, 
+                             &mpvio->acl_user_plugin, 
+                             acl_user_tmp->plugin.str, 
+                             acl_user_tmp->plugin.length, 0);
+      break;
+    }
+  }
+  mysql_mutex_unlock(&acl_cache->lock);
+
+  if (!mpvio->acl_user)
+  {
+    login_failed_error(mpvio, 0);
+    DBUG_RETURN (1);
+  }
+
+  /* user account requires non-default plugin and the client is too old */
+  if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
+      mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
+      !(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
+  {
+    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+                              native_password_plugin_name.str));
+    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+                              old_password_plugin_name.str));
+    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+    general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    DBUG_RETURN (1);
+  }
+
+  mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
+  mpvio->auth_info.auth_string_length= 
+    (unsigned long) mpvio->acl_user->auth_string.length;
+  strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
+          mpvio->acl_user->user : "", USERNAME_LENGTH);
+  DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
+                      "plugin=%s",
+                      mpvio->auth_info.user_name,
+                      mpvio->auth_info.auth_string,
+                      mpvio->auth_info.authenticated_as,
+                      mpvio->acl_user->plugin.str));
+  DBUG_RETURN(0);
+}
+#endif
+
+/* the packet format is described in send_change_user_packet() */
+static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
+{
+  NET *net= mpvio->net;
+
+  char *user= (char*) net->read_pos;
+  char *end= user + packet_length;
+  /* Safe because there is always a trailing \0 at the end of the packet */
+  char *passwd= strend(user) + 1;
+  uint user_len= passwd - user - 1;
+  char *db= passwd;
+  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;
+
+  DBUG_ENTER ("parse_com_change_user_packet");
+  if (passwd >= end)
+  {
+    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+    DBUG_RETURN (1);
+  }
+
+  /*
+    Old clients send null-terminated string as password; new clients send
+    the size (1 byte) + string (not null-terminated). Hence in case of empty
+    password both send '\0'.
+
+    This strlen() can't be easily deleted without changing protocol.
+
+    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
+    *passwd > 127 and become 2**32-127+ after casting to uint.
+  */
+  uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
+                    (uchar) (*passwd++) : strlen(passwd));
+
+  db+= passwd_len + 1;
+  /*
+    Database name is always NUL-terminated, so in case of empty database
+    the packet must contain at least the trailing '\0'.
+  */
+  if (db >= end)
+  {
+    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+    DBUG_RETURN (1);
+  }
+
+  uint db_len= strlen(db);
+
+  char *ptr= db + db_len + 1;
+
+  if (ptr + 1 < end)
+  {
+    if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
+      DBUG_RETURN(1);
+  }
+
+
+  /* Convert database and user names to utf8 */
+  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
+                           db, db_len, mpvio->charset_adapter->charset(),
+                           &dummy_errors);
+  db_buff[db_len]= 0;
+
+  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
+                                  system_charset_info, user, user_len,
+                                  mpvio->charset_adapter->charset(),
+                                  &dummy_errors);
+  user_buff[user_len]= 0;
+
+  /* we should not free mpvio->user here: it's saved by dispatch_command() */
+  if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
+    return 1;
+  mpvio->auth_info.user_name_length= user_len;
+
+  if (make_lex_string_root(mpvio->mem_root, 
+                           &mpvio->db, db_buff, db_len, 0) == 0)
+    DBUG_RETURN(1); /* The error is set by make_lex_string(). */
+
+  if (!initialized)
+  {
+    // if mysqld's been started with --skip-grant-tables option
+    strmake(mpvio->auth_info.authenticated_as, 
+            mpvio->auth_info.user_name, USERNAME_LENGTH);
+
+    mpvio->status= MPVIO_EXT::SUCCESS;
+    DBUG_RETURN(0);
+  }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+  if (find_mpvio_user(mpvio))
+    DBUG_RETURN(1);
+
+  char *client_plugin;
+  if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
+  {
+    client_plugin= ptr + 2;
+    if (client_plugin >= end)
+    {
+      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+      DBUG_RETURN(1);
+    }
+  }
+  else
+  {
+    if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
+      client_plugin= native_password_plugin_name.str;
+    else
+    {
+      client_plugin=  old_password_plugin_name.str;
+      /*
+        For a passwordless accounts we use native_password_plugin.
+        But when an old 4.0 client connects to it, we change it to
+        old_password_plugin, otherwise MySQL will think that server 
+        and client plugins don't match.
+      */
+      if (mpvio->acl_user->auth_string.length == 0)
+        mpvio->acl_user_plugin= old_password_plugin_name;
+    }
+  }
+
+  DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
+  /* 
+    Remember the data part of the packet, to present it to plugin in 
+    read_packet() 
+  */
+  mpvio->cached_client_reply.pkt= passwd;
+  mpvio->cached_client_reply.pkt_len= passwd_len;
+  mpvio->cached_client_reply.plugin= client_plugin;
+  mpvio->status= MPVIO_EXT::RESTART;
+#endif
+
+  DBUG_RETURN (0);
+}
+
+/* the packet format is described in send_client_reply_packet() */
+static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
+                                           uchar **buff, ulong pkt_len)
+{
+#ifndef EMBEDDED_LIBRARY
+  NET *net= mpvio->net;
+  char *end;
+
+  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+
+  if (pkt_len < MIN_HANDSHAKE_SIZE)
+    return packet_error;
+
+  if (mpvio->connect_errors)
+    reset_host_errors(mpvio->ip);
+
+  ulong client_capabilities= uint2korr(net->read_pos);
+  if (client_capabilities & CLIENT_PROTOCOL_41)
+  {
+    client_capabilities|= ((ulong) uint2korr(net->read_pos + 2)) << 16;
+    mpvio->max_client_packet_length= uint4korr(net->read_pos + 4);
+    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
+    if (mpvio->charset_adapter->init_client_charset((uint) net->read_pos[8]))
+      return packet_error;
+    end= (char*) net->read_pos + 32;
+  }
+  else
+  {
+    mpvio->max_client_packet_length= uint3korr(net->read_pos + 2);
+    end= (char*) net->read_pos + 5;
+  }
+
+  /* Disable those bits which are not supported by the client. */
+  mpvio->client_capabilities&= client_capabilities;
+
+
+#if defined(HAVE_OPENSSL)
+  DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
+  if (mpvio->client_capabilities & CLIENT_SSL)
+  {
+    char error_string[1024] __attribute__((unused));
+
+    /* Do the SSL layering. */
+    if (!ssl_acceptor_fd)
+      return packet_error;
+
+    DBUG_PRINT("info", ("IO layer change in progress..."));
+    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
+    {
+      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
+      return packet_error;
+    }
+
+    DBUG_PRINT("info", ("Reading user information over SSL layer"));
+    pkt_len= my_net_read(net);
+    if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
+    {
+      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
+			   pkt_len));
+      return packet_error;
+    }
+  }
+#endif
+
+  if (end >= (char*) net->read_pos + pkt_len + 2)
+    return packet_error;
+
+  if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
+      opt_using_transactions)
+    net->return_status= mpvio->server_status;
+
+  char *user= end;
+  char *passwd= strend(user) + 1;
+  uint user_len= passwd - user - 1, db_len;
+  char *db= passwd;
+  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;
+
+  /*
+    Old clients send null-terminated string as password; new clients send
+    the size (1 byte) + string (not null-terminated). Hence in case of empty
+    password both send '\0'.
+
+    This strlen() can't be easily deleted without changing protocol.
+
+    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
+    *passwd > 127 and become 2**32-127+ after casting to uint.
+  */
+  uint passwd_len= mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
+                   (uchar) (*passwd++) : strlen(passwd);
+  
+  if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
+  {
+    db= db + passwd_len + 1;
+    /* strlen() can't be easily deleted without changing protocol */
+    db_len= strlen(db);
+  }
+  else
+  {
+    db= 0;
+    db_len= 0;
+  }
+
+  if (passwd + passwd_len + db_len > (char *) net->read_pos + pkt_len)
+    return packet_error;
+
+  char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);
+
+  /* Since 4.1 all database names are stored in utf8 */
+  if (db)
+  {
+    db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
+                             db, db_len, mpvio->charset_adapter->charset(),
+                             &dummy_errors);
+    db= db_buff;
+    db_buff[db_len]= 0;
+  }
+
+  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
+                                system_charset_info, user, user_len,
+                                mpvio->charset_adapter->charset(),
+                                &dummy_errors);
+  user= user_buff;
+  user_buff[user_len]= 0;
+
+  /* If username starts and ends in "'", chop them off */
+  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
+  {
+    user[user_len - 1]= 0;
+    user++;
+    user_len-= 2;
+  }
+
+  if (make_lex_string_root(mpvio->mem_root, 
+                           &mpvio->db, db, db_len, 0) == 0)
+    return packet_error; /* The error is set by make_lex_string(). */
+  if (mpvio->auth_info.user_name)
+    my_free(mpvio->auth_info.user_name);
+  if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
+    return packet_error; /* The error is set by my_strdup(). */
+  mpvio->auth_info.user_name_length= user_len;
+
+  if (!initialized)
+  {
+    // if mysqld's been started with --skip-grant-tables option
+    mpvio->status= MPVIO_EXT::SUCCESS;
+    return packet_error;
+  }
+
+  if (find_mpvio_user(mpvio))
+    return packet_error;
+
+  if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
+  {
+    if ((client_plugin + strlen(client_plugin)) > 
+          (char *) net->read_pos + pkt_len)
+      return packet_error;
+  }
+  else
+  {
+    if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
+      client_plugin= native_password_plugin_name.str;
+    else
+    {
+      client_plugin= old_password_plugin_name.str;
+      /*
+        For a passwordless accounts we use native_password_plugin.
+        But when an old 4.0 client connects to it, we change it to
+        old_password_plugin, otherwise MySQL will think that server 
+        and client plugins don't match.
+      */
+      if (mpvio->acl_user->auth_string.length == 0)
+        mpvio->acl_user_plugin= old_password_plugin_name;
+    }
+  }
+  
+  /*
+    if the acl_user needs a different plugin to authenticate
+    (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
+    we need to restart the authentication in the server.
+    But perhaps the client has already used the correct plugin -
+    in that case the authentication on the client may not need to be
+    restarted and a server auth plugin will read the data that the client
+    has just send. Cache them to return in the next server_mpvio_read_packet().
+  */
+  if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
+                    plugin_name(mpvio->plugin)->str) != 0)
+  {
+    mpvio->cached_client_reply.pkt= passwd;
+    mpvio->cached_client_reply.pkt_len= passwd_len;
+    mpvio->cached_client_reply.plugin= client_plugin;
+    mpvio->status= MPVIO_EXT::RESTART;
+    return packet_error;
+  }
+
+  /*
+    ok, we don't need to restart the authentication on the server.
+    but if the client used the wrong plugin, we need to restart
+    the authentication on the client. Do it here, the server plugin
+    doesn't need to know.
+  */
+  const char *client_auth_plugin=
+    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+  if (client_auth_plugin &&
+      my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
+  {
+    mpvio->cached_client_reply.plugin= client_plugin;
+    if (send_plugin_request_packet(mpvio,
+                                   (uchar*) mpvio->cached_server_packet.pkt,
+                                   mpvio->cached_server_packet.pkt_len))
+      return packet_error;
+
+    passwd_len= my_net_read(mpvio->net);
+    passwd = (char*) mpvio->net->read_pos;
+  }
+
+  *buff= (uchar*) passwd;
+  return passwd_len;
+#else
+  return 0;
+#endif
+}
+
+
+/**
+  Make sure that when sending plugin supplued data to the client they
+  are not considered a special out-of-band command, like e.g. 
+  \255 (error) or \254 (change user request packet).
+  To avoid this we send plugin data packets starting with one of these
+  2 bytes "wrapped" in a command \1. 
+  For the above reason we have to wrap plugin data packets starting with
+  \1 as well.
+*/
+
+#define IS_OUT_OF_BAND_PACKET(packet,packet_len) \
+  ((packet_len) > 0 && \
+   (*(packet) == 1 || *(packet) == 255 || *(packet) == 254))
+
+static inline int 
+wrap_plguin_data_into_proper_command(NET *net, 
+                                     const uchar *packet, int packet_len)
+{
+  DBUG_ASSERT(IS_OUT_OF_BAND_PACKET(packet, packet_len));
+  return net_write_command(net, 1, (uchar *) "", 0, packet, packet_len);
+}
+
+
+/**
+  vio->write_packet() callback method for server authentication plugins
+
+  This function is called by a server authentication plugin, when it wants
+  to send data to the client.
+
+  It transparently wraps the data into a handshake packet,
+  and handles plugin negotiation with the client. If necessary,
+  it escapes the plugin data, if it starts with a mysql protocol packet byte.
+*/
+static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
+                                   const uchar *packet, int packet_len)
+{
+  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
+  int res;
+
+  DBUG_ENTER("server_mpvio_write_packet");
+  /* reset cached_client_reply */
+  mpvio->cached_client_reply.pkt= 0;
+  /* for the 1st packet we wrap plugin data into the handshake packet */
+  if (mpvio->packets_written == 0)
+    res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
+  else if (mpvio->status == MPVIO_EXT::RESTART)
+    res= send_plugin_request_packet(mpvio, packet, packet_len);
+  else if (IS_OUT_OF_BAND_PACKET(packet, packet_len))
+    res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
+  else
+  {
+    res= my_net_write(mpvio->net, packet, packet_len) ||
+         net_flush(mpvio->net);
+  }
+  mpvio->packets_written++;
+  DBUG_RETURN(res);
+}
+
+/**
+  vio->read_packet() callback method for server authentication plugins
+
+  This function is called by a server authentication plugin, when it wants
+  to read data from the client.
+
+  It transparently extracts the client plugin data, if embedded into
+  a client authentication handshake packet, and handles plugin negotiation
+  with the client, if necessary.
+*/
+static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
+{
+  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
+  ulong pkt_len;
+
+  DBUG_ENTER("server_mpvio_read_packet");
+  if (mpvio->packets_written == 0)
+  {
+    /*
+      plugin wants to read the data without sending anything first.
+      send an empty packet to force a server handshake packet to be sent
+    */
+    if (mpvio->write_packet(mpvio, 0, 0))
+      pkt_len= packet_error;
+    else
+      pkt_len= my_net_read(mpvio->net);
+  }
+  else if (mpvio->cached_client_reply.pkt)
+  {
+    DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
+    DBUG_ASSERT(mpvio->packets_read > 0);
+    /*
+      if the have the data cached from the last server_mpvio_read_packet
+      (which can be the case if it's a restarted authentication)
+      and a client has used the correct plugin, then we can return the
+      cached data straight away and avoid one round trip.
+    */
+    const char *client_auth_plugin=
+      ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+    if (client_auth_plugin == 0 ||
+        my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+                      client_auth_plugin) == 0)
+    {
+      mpvio->status= MPVIO_EXT::FAILURE;
+      *buf= (uchar*) mpvio->cached_client_reply.pkt;
+      mpvio->cached_client_reply.pkt= 0;
+      mpvio->packets_read++;
+      DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
+    }
+    /*
+      But if the client has used the wrong plugin, the cached data are
+      useless. Furthermore, we have to send a "change plugin" request
+      to the client.
+    */
+    if (mpvio->write_packet(mpvio, 0, 0))
+      pkt_len= packet_error;
+    else
+      pkt_len= my_net_read(mpvio->net);
+  }
+  else
+    pkt_len= my_net_read(mpvio->net);
+
+  if (pkt_len == packet_error)
+    goto err;
+
+  mpvio->packets_read++;
+
+  /*
+    the 1st packet has the plugin data wrapped into the client authentication
+    handshake packet
+  */
+  if (mpvio->packets_read == 1)
+  {
+    pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
+    if (pkt_len == packet_error)
+      goto err;
+  }
+  else
+    *buf= mpvio->net->read_pos;
+
+  DBUG_RETURN((int)pkt_len);
+
+err:
+  if (mpvio->status == MPVIO_EXT::FAILURE)
+  {
+    inc_host_errors(mpvio->ip);
+    my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+  }
+  DBUG_RETURN(-1);
+}
+
+/**
+  fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+  connection
+*/
+static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+                              MYSQL_PLUGIN_VIO_INFO *info)
+{
+  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
+  mpvio_info(mpvio->net->vio, info);
+}
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
+{
+#if defined(HAVE_OPENSSL)
+  Vio *vio= thd->net.vio;
+  SSL *ssl= (SSL *) vio->ssl_arg;
+  X509 *cert;
+#endif
+
+  /*
+    At this point we know that user is allowed to connect
+    from given host by given username/password pair. Now
+    we check if SSL is required, if user is using SSL and
+    if X509 certificate attributes are OK
+  */
+  switch (acl_user->ssl_type) {
+  case SSL_TYPE_NOT_SPECIFIED:                  // Impossible
+  case SSL_TYPE_NONE:                           // SSL is not required
+    return 0;
+#if defined(HAVE_OPENSSL)
+  case SSL_TYPE_ANY:                            // Any kind of SSL is ok
+    return vio_type(vio) != VIO_TYPE_SSL;
+  case SSL_TYPE_X509: /* Client should have any valid certificate. */
+    /*
+      Connections with non-valid certificates are dropped already
+      in sslaccept() anyway, so we do not check validity here.
+
+      We need to check for absence of SSL because without SSL
+      we should reject connection.
+    */
+    if (vio_type(vio) == VIO_TYPE_SSL &&
+        SSL_get_verify_result(ssl) == X509_V_OK &&
+        (cert= SSL_get_peer_certificate(ssl)))
+    {
+      X509_free(cert);
+      return 0;
+    }
+    return 1;
+  case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
+    /* If a cipher name is specified, we compare it to actual cipher in use. */
+    if (vio_type(vio) != VIO_TYPE_SSL ||
+        SSL_get_verify_result(ssl) != X509_V_OK)
+      return 1;
+    if (acl_user->ssl_cipher)
+    {
+      DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
+                         acl_user->ssl_cipher, SSL_get_cipher(ssl)));
+      if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
+      {
+        if (global_system_variables.log_warnings)
+          sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
+                            acl_user->ssl_cipher, SSL_get_cipher(ssl));
+        return 1;
+      }
+    }
+    /* Prepare certificate (if exists) */
+    if (!(cert= SSL_get_peer_certificate(ssl)))
+      return 1;
+    /* If X509 issuer is specified, we check it... */
+    if (acl_user->x509_issuer)
+    {
+      char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
+      DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
+                         acl_user->x509_issuer, ptr));
+      if (strcmp(acl_user->x509_issuer, ptr))
+      {
+        if (global_system_variables.log_warnings)
+          sql_print_information("X509 issuer mismatch: should be '%s' "
+                            "but is '%s'", acl_user->x509_issuer, ptr);
+        free(ptr);
+        X509_free(cert);
+        return 1;
+      }
+      free(ptr);
+    }
+    /* X509 subject is specified, we check it .. */
+    if (acl_user->x509_subject)
+    {
+      char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
+      DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
+                         acl_user->x509_subject, ptr));
+      if (strcmp(acl_user->x509_subject, ptr))
+      {
+        if (global_system_variables.log_warnings)
+          sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
+                          acl_user->x509_subject, ptr);
+        free(ptr);
+        X509_free(cert);
+        return 1;
+      }
+      free(ptr);
+    }
+    X509_free(cert);
+    return 0;
+#else  /* HAVE_OPENSSL */
+  default:
+    /*
+      If we don't have SSL but SSL is required for this user the 
+      authentication should fail.
+    */
+    return 1;
+#endif /* HAVE_OPENSSL */
+  }
+  return 1;
+}
+#endif
+
+
+static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
+                        MPVIO_EXT *mpvio)
+{
+  int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
+  bool unlock_plugin= false;
+  plugin_ref plugin;
+
+  if (auth_plugin_name->str == native_password_plugin_name.str)
+    plugin= native_password_plugin;
+  else
+#ifndef EMBEDDED_LIBRARY
+  if (auth_plugin_name->str == old_password_plugin_name.str)
+    plugin= old_password_plugin;
+  else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
+                                           MYSQL_AUTHENTICATION_PLUGIN)))
+    unlock_plugin= true;
+  else
+#endif
+    plugin= NULL;
+    
+  mpvio->plugin= plugin;
+  old_status= mpvio->status;
+  
+  if (plugin)
+  {
+    st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
+    res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+
+    if (unlock_plugin)
+      plugin_unlock(thd, plugin);
+  }
+  else
+  {
+    /* Server cannot load the required plugin. */
+    my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
+    res= CR_ERROR;
+  }
+
+  /*
+    If the status was MPVIO_EXT::RESTART before the authenticate_user() call
+    it can never be MPVIO_EXT::RESTART after the call, because any call
+    to write_packet() or read_packet() will reset the status.
+
+    But (!) if a plugin never called a read_packet() or write_packet(), the
+    status will stay unchanged. We'll fix it, by resetting the status here.
+  */
+  if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
+    mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
+
+  return res;
+}
+
+
+static void
+server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors,
+                        Thd_charset_adapter *charset_adapter)
+{
+  memset(mpvio, 0, sizeof(MPVIO_EXT));
+  mpvio->read_packet= server_mpvio_read_packet;
+  mpvio->write_packet= server_mpvio_write_packet;
+  mpvio->info= server_mpvio_info;
+  mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
+  mpvio->auth_info.host_or_ip_length= 
+    (unsigned int) strlen(thd->security_ctx->host_or_ip);
+  mpvio->auth_info.user_name= thd->security_ctx->user;
+  mpvio->auth_info.user_name_length= thd->security_ctx->user ? 
+    (unsigned int) strlen(thd->security_ctx->user) : 0;
+  mpvio->connect_errors= connect_errors;
+  mpvio->status= MPVIO_EXT::FAILURE;
+
+  mpvio->client_capabilities= thd->client_capabilities;
+  mpvio->mem_root= thd->mem_root;
+  mpvio->scramble= thd->scramble;
+  mpvio->rand= &thd->rand;
+  mpvio->thread_id= thd->thread_id;
+  mpvio->server_status= &thd->server_status;
+  mpvio->net= &thd->net;
+  mpvio->ip= thd->security_ctx->ip;
+  mpvio->host= thd->security_ctx->host;
+  mpvio->charset_adapter= charset_adapter;
+}
+
+
+static void
+server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
+{
+  thd->client_capabilities= mpvio->client_capabilities;
+  thd->max_client_packet_length= mpvio->max_client_packet_length;
+  if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
+    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
+  thd->security_ctx->user= mpvio->auth_info.user_name;
+  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
+}
+
+/**
+  Perform the handshake, authorize the client and update thd sctx variables.
+
+  @param thd                     thread handle
+  @param connect_errors          number of previous failed connect attemps
+                                 from this host
+  @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
+                                 (without the first, command, byte) or 0
+                                 if it's not a COM_CHANGE_USER (that is, if
+                                 it's a new connection)
+
+  @retval 0  success, thd is updated.
+  @retval 1  error
+*/
+bool 
+acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
+{
+  int res= CR_OK;
+  MPVIO_EXT mpvio;
+  Thd_charset_adapter charset_adapter(thd);
+
+  const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
+  enum  enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
+                                                             : COM_CONNECT;
+
+  DBUG_ENTER("acl_authenticate");
+  compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+  server_mpvio_initialize(thd, &mpvio, connect_errors, &charset_adapter);
+
+  DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
+
+  /*
+    Clear thd->db as it points to something, that will be freed when
+    connection is closed. We don't want to accidentally free a wrong
+    pointer if connect failed.
+  */
+  thd->reset_db(NULL, 0);
+
+  if (command == COM_CHANGE_USER)
+  {
+    mpvio.packets_written++; // pretend that a server handshake packet was sent
+    mpvio.packets_read++;    // take COM_CHANGE_USER packet into account
+
+    /* Clear variables that are allocated */
+    thd->user_connect= 0;
+
+    if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
+    {
+      server_mpvio_update_thd(thd, &mpvio);
+      DBUG_RETURN(1);
+    }
+
+    DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
+                mpvio.status == MPVIO_EXT::SUCCESS);
+  }
+  else
+  {
+    /* mark the thd as having no scramble yet */
+    mpvio.scramble[SCRAMBLE_LENGTH]= 1;
+    
+    /*
+     perform the first authentication attempt, with the default plugin.
+     This sends the server handshake packet, reads the client reply
+     with a user name, and performs the authentication if everyone has used
+     the correct plugin.
+    */
+
+    res= do_auth_once(thd, auth_plugin_name, &mpvio);  
+  }
+
+  /*
+   retry the authentication, if - after receiving the user name -
+   we found that we need to switch to a non-default plugin
+  */
+  if (mpvio.status == MPVIO_EXT::RESTART)
+  {
+    DBUG_ASSERT(mpvio.acl_user);
+    DBUG_ASSERT(command == COM_CHANGE_USER ||
+                my_strcasecmp(system_charset_info, auth_plugin_name->str,
+                              mpvio.acl_user->plugin.str));
+    auth_plugin_name= &mpvio.acl_user->plugin;
+    res= do_auth_once(thd, auth_plugin_name, &mpvio);
+  }
+
+  server_mpvio_update_thd(thd, &mpvio);
+
+  Security_context *sctx= thd->security_ctx;
+  const ACL_USER *acl_user= mpvio.acl_user;
+
+  thd->password= mpvio.auth_info.password_used;  // remember for error messages 
+
+  /*
+    Log the command here so that the user can check the log
+    for the tried logins and also to detect break-in attempts.
+
+    if sctx->user is unset it's protocol failure, bad packet.
+  */
+  if (mpvio.auth_info.user_name)
+  {
+    if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
+    {
+      general_log_print(thd, command, "%s@%s as %s on %s",
+                        mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
+                        mpvio.auth_info.authenticated_as ? 
+                          mpvio.auth_info.authenticated_as : "anonymous",
+                        mpvio.db.str ? mpvio.db.str : (char*) "");
+    }
+    else
+      general_log_print(thd, command, (char*) "%s@%s on %s",
+                        mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
+                        mpvio.db.str ? mpvio.db.str : (char*) "");
+  }
+
+  if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
+  {
+    DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
+
+    if (!thd->is_error())
+      login_failed_error(&mpvio, mpvio.auth_info.password_used);
+    DBUG_RETURN (1);
+  }
+
+  sctx->proxy_user[0]= 0;
+
+  if (initialized) // if not --skip-grant-tables
+  {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    bool is_proxy_user= FALSE;
+    const char *auth_user = acl_user->user ? acl_user->user : "";
+    ACL_PROXY_USER *proxy_user;
+    /* check if the user is allowed to proxy as another user */
+    proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
+                                    mpvio.auth_info.authenticated_as,
+                                          &is_proxy_user);
+    if (is_proxy_user)
+    {
+      ACL_USER *acl_proxy_user;
+
+      /* we need to find the proxy user, but there was none */
+      if (!proxy_user)
+      {
+        if (!thd->is_error())
+          login_failed_error(&mpvio, mpvio.auth_info.password_used);
+        DBUG_RETURN(1);
+      }
+
+      my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
+                  "'%s'@'%s'", auth_user,
+                  acl_user->host.hostname ? acl_user->host.hostname : "");
+
+      /* we're proxying : find the proxy user definition */
+      mysql_mutex_lock(&acl_cache->lock);
+      acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? 
+                                    proxy_user->get_proxied_host() : "",
+                                    mpvio.auth_info.authenticated_as, TRUE);
+      if (!acl_proxy_user)
+      {
+        if (!thd->is_error())
+          login_failed_error(&mpvio, mpvio.auth_info.password_used);
+        mysql_mutex_unlock(&acl_cache->lock);
+        DBUG_RETURN(1);
+      }
+      acl_user= acl_proxy_user->copy(thd->mem_root);
+      mysql_mutex_unlock(&acl_cache->lock);
+    }
+#endif
+
+    sctx->master_access= acl_user->access;
+    if (acl_user->user)
+      strmake(sctx->priv_user, acl_user->user, USERNAME_LENGTH - 1);
+    else
+      *sctx->priv_user= 0;
+
+    if (acl_user->host.hostname)
+      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
+    else
+      *sctx->priv_host= 0;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    /*
+      OK. Let's check the SSL. Historically it was checked after the password,
+      as an additional layer, not instead of the password
+      (in which case it would've been a plugin too).
+    */
+    if (acl_check_ssl(thd, acl_user))
+    {
+      if (!thd->is_error())
+        login_failed_error(&mpvio, thd->password);
+      DBUG_RETURN(1);
+    }
+
+    /* Don't allow the user to connect if he has done too many queries */
+    if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
+         acl_user->user_resource.conn_per_hour ||
+         acl_user->user_resource.user_conn || 
+         global_system_variables.max_user_connections) &&
+        get_or_create_user_conn(thd,
+          (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
+          (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
+          &acl_user->user_resource))
+      DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
+
+#endif
+  }
+  else
+    sctx->skip_grants();
+
+  if (thd->user_connect &&
+      (thd->user_connect->user_resources.conn_per_hour ||
+       thd->user_connect->user_resources.user_conn ||
+       global_system_variables.max_user_connections) &&
+      check_for_max_user_connections(thd, thd->user_connect))
+  {
+    DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
+  }
+
+  DBUG_PRINT("info",
+             ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
+              "Login user: '%s' Priv_user: '%s'  Using password: %s "
+              "Access: %lu  db: '%s'",
+              thd->client_capabilities, thd->max_client_packet_length,
+              sctx->host_or_ip, sctx->user, sctx->priv_user,
+              thd->password ? "yes": "no",
+              sctx->master_access, mpvio.db.str));
+
+  if (command == COM_CONNECT &&
+      !(thd->main_security_ctx.master_access & SUPER_ACL))
+  {
+    mysql_mutex_lock(&LOCK_connection_count);
+    bool count_ok= (connection_count <= max_connections);
+    mysql_mutex_unlock(&LOCK_connection_count);
+    if (!count_ok)
+    {                                         // too many connections
+      my_error(ER_CON_COUNT_ERROR, MYF(0));
+      DBUG_RETURN(1);
+    }
+  }
+
+  /*
+    This is the default access rights for the current database.  It's
+    set to 0 here because we don't have an active database yet (and we
+    may not have an active database to set.
+  */
+  sctx->db_access=0;
+
+  /* Change a database if necessary */
+  if (mpvio.db.length)
+  {
+    if (mysql_change_db(thd, &mpvio.db, FALSE))
+    {
+      /* mysql_change_db() has pushed the error message. */
+      if (thd->user_connect)
+      {
+        decrease_user_connections(thd->user_connect);
+        thd->user_connect= 0;
+      }
+      DBUG_RETURN(1);
+    }
+  }
+
+  if (mpvio.auth_info.external_user[0])
+    sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
+
+  if (res == CR_OK_HANDSHAKE_COMPLETE)
+    thd->stmt_da->disable_status();
+  else
+    my_ok(thd);
+
+#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)
+  /*
+    Allow the network layer to skip big packets. Although a malicious
+    authenticated session might use this to trick the server to read
+    big packets indefinitely, this is a previously established behavior
+    that needs to be preserved as to not break backwards compatibility.
+  */
+  thd->net.skip_big_packet= TRUE;
+#endif
+
+  /* Ready to handle queries */
+  DBUG_RETURN(0);
+}
+
+/**
+  MySQL Server Password Authentication Plugin
+
+  In the MySQL authentication protocol:
+  1. the server sends the random scramble to the client
+  2. client sends the encrypted password back to the server
+  3. the server checks the password.
+*/
+static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, 
+                                              MYSQL_SERVER_AUTH_INFO *info)
+{
+  uchar *pkt;
+  int pkt_len;
+  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
+
+  DBUG_ENTER("native_password_authenticate");
+
+  /* generate the scramble, or reuse the old one */
+  if (mpvio->scramble[SCRAMBLE_LENGTH])
+    create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+
+  /* send it to the client */
+  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
+    return CR_ERROR;
+
+  /* reply and authenticate */
+
+  /*
+    <digression>
+      This is more complex than it looks.
+
+      The plugin (we) may be called right after the client was connected -
+      and will need to send a scramble, read reply, authenticate.
+
+      Or the plugin may be called after another plugin has sent a scramble,
+      and read the reply. If the client has used the correct client-plugin,
+      we won't need to read anything here from the client, the client
+      has already sent a reply with everything we need for authentication.
+
+      Or the plugin may be called after another plugin has sent a scramble,
+      and read the reply, but the client has used the wrong client-plugin.
+      We'll need to sent a "switch to another plugin" packet to the
+      client and read the reply. "Use the short scramble" packet is a special
+      case of "switch to another plugin" packet.
+
+      Or, perhaps, the plugin may be called after another plugin has
+      done the handshake but did not send a useful scramble. We'll need
+      to send a scramble (and perhaps a "switch to another plugin" packet)
+      and read the reply.
+
+      Besides, a client may be an old one, that doesn't understand plugins.
+      Or doesn't even understand 4.0 scramble.
+
+      And we want to keep the same protocol on the wire  unless non-native
+      plugins are involved.
+
+      Anyway, it still looks simple from a plugin point of view:
+      "send the scramble, read the reply and authenticate"
+      All the magic is transparently handled by the server.
+    </digression>
+  */
+
+  /* read the reply with the encrypted password */
+  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+    DBUG_RETURN(CR_ERROR);
+  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+  DBUG_RETURN(CR_OK);
+#endif
+
+  if (pkt_len == 0) /* no password */
+    DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
+
+  info->password_used= PASSWORD_USED_YES;
+  if (pkt_len == SCRAMBLE_LENGTH)
+  {
+    if (!mpvio->acl_user->salt_len)
+      DBUG_RETURN(CR_ERROR);
+
+    DBUG_RETURN(check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
+                CR_ERROR : CR_OK);
+  }
+
+  inc_host_errors(mpvio->ip);
+  my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+  DBUG_RETURN(CR_ERROR);
+}
+
+static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, 
+                                     MYSQL_SERVER_AUTH_INFO *info)
+{
+  uchar *pkt;
+  int pkt_len;
+  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
+
+  /* generate the scramble, or reuse the old one */
+  if (mpvio->scramble[SCRAMBLE_LENGTH])
+    create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+
+  /* send it to the client */
+  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
+    return CR_ERROR;
+
+  /* read the reply and authenticate */
+  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+    return CR_ERROR;
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+  return CR_OK;
+#endif
+
+  /*
+    legacy: if switch_from_long_to_short_scramble,
+    the password is sent \0-terminated, the pkt_len is always 9 bytes.
+    We need to figure out the correct scramble length here.
+  */
+  if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
+    pkt_len= strnlen((char*)pkt, pkt_len);
+
+  if (pkt_len == 0) /* no password */
+    return info->auth_string[0] ? CR_ERROR : CR_OK;
+
+  if (secure_auth(mpvio))
+    return CR_ERROR;
+
+  info->password_used= PASSWORD_USED_YES;
+
+  if (pkt_len == SCRAMBLE_LENGTH_323)
+  {
+    if (!mpvio->acl_user->salt_len)
+      return CR_ERROR;
+
+    return check_scramble_323(pkt, mpvio->scramble,
+                             (ulong *) mpvio->acl_user->salt) ? 
+                             CR_ERROR : CR_OK;
+  }
+
+  inc_host_errors(mpvio->ip);
+  my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+  return CR_ERROR;
+}
+
+static struct st_mysql_auth native_password_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  native_password_plugin_name.str,
+  native_password_authenticate
+};
+
+static struct st_mysql_auth old_password_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  old_password_plugin_name.str,
+  old_password_authenticate
+};
+
+mysql_declare_plugin(mysql_password)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
+  &native_password_handler,                     /* type descriptor  */
+  native_password_plugin_name.str,              /* Name             */
+  "R.J.Silk, Sergei Golubchik",                 /* Author           */
+  "Native MySQL authentication",                /* Description      */
+  PLUGIN_LICENSE_GPL,                           /* License          */
+  NULL,                                         /* Init function    */
+  NULL,                                         /* Deinit function  */
+  0x0100,                                       /* Version (1.0)    */
+  NULL,                                         /* status variables */
+  NULL,                                         /* system variables */
+  NULL                                          /* config options   */
+},
+{
+  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
+  &old_password_handler,                        /* type descriptor  */
+  old_password_plugin_name.str,                 /* Name             */
+  "R.J.Silk, Sergei Golubchik",                 /* Author           */
+  "Old MySQL-4.0 authentication",               /* Description      */
+  PLUGIN_LICENSE_GPL,                           /* License          */
+  NULL,                                         /* Init function    */
+  NULL,                                         /* Deinit function  */
+  0x0100,                                       /* Version (1.0)    */
+  NULL,                                         /* status variables */
+  NULL,                                         /* system variables */
+  NULL                                          /* config options   */
+}
+mysql_declare_plugin_end;
+

=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h	2010-04-07 11:58:40 +0000
+++ b/sql/sql_acl.h	2010-08-09 08:32:50 +0000
@@ -173,53 +173,6 @@ enum mysql_db_table_field
 extern const TABLE_FIELD_DEF mysql_db_table_def;
 extern bool mysql_user_table_is_in_short_password_format;
 
-/* Classes */
-
-struct acl_host_and_ip
-{
-  char *hostname;
-  long ip,ip_mask;                      // Used with masked ip:s
-};
-
-
-class ACL_ACCESS {
-public:
-  ulong sort;
-  ulong access;
-};
-
-
-/* ACL_HOST is used if no host is specified */
-
-class ACL_HOST :public ACL_ACCESS
-{
-public:
-  acl_host_and_ip host;
-  char *db;
-};
-
-
-class ACL_USER :public ACL_ACCESS
-{
-public:
-  acl_host_and_ip host;
-  uint hostname_length;
-  USER_RESOURCES user_resource;
-  char *user;
-  uint8 salt[SCRAMBLE_LENGTH+1];       // scrambled password in binary form
-  uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1 
-  enum SSL_type ssl_type;
-  const char *ssl_cipher, *x509_issuer, *x509_subject;
-};
-
-
-class ACL_DB :public ACL_ACCESS
-{
-public:
-  acl_host_and_ip host;
-  char *user,*db;
-};
-
 /* prototypes */
 
 bool hostname_requires_resolving(const char *hostname);
@@ -228,17 +181,16 @@ my_bool acl_reload(THD *thd);
 void acl_free(bool end=0);
 ulong acl_get(const char *host, const char *ip,
 	      const char *user, const char *db, my_bool db_is_pattern);
-int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
-                uint passwd_len);
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
-                             char *ip, char *db);
+bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len);
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+                 char *ip, char *db);
 bool acl_check_host(const char *host, const char *ip);
 int check_change_password(THD *thd, const char *host, const char *user,
                            char *password, uint password_len);
 bool change_password(THD *thd, const char *host, const char *user,
 		     char *password);
 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
-                 ulong rights, bool revoke);
+                 ulong rights, bool revoke, bool is_proxy);
 int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
                        List <LEX_COLUMN> &column_list, ulong rights,
                        bool revoke);
@@ -420,4 +372,6 @@ get_cached_table_access(GRANT_INTERNAL_I
                         const char *schema_name,
                         const char *table_name);
 
+bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
+                                   bool with_grant);
 #endif /* SQL_ACL_INCLUDED */

=== modified file 'sql/sql_audit.h'
--- a/sql/sql_audit.h	2010-08-20 09:58:28 +0000
+++ b/sql/sql_audit.h	2010-09-20 14:17:32 +0000
@@ -41,7 +41,7 @@ static inline uint make_user_name(THD *t
 {
   Security_context *sctx= thd->security_ctx;
   return strxnmov(buf, MAX_USER_HOST_SIZE,
-                  sctx->priv_user ? sctx->priv_user : "", "[",
+                  sctx->priv_user[0] ? sctx->priv_user : "", "[",
                   sctx->user ? sctx->user : "", "] @ ",
                   sctx->host ? sctx->host : "", " [",
                   sctx->ip ? sctx->ip : "", "]", NullS) - buf;

=== modified file 'sql/sql_builtin.cc.in'
--- a/sql/sql_builtin.cc.in	2009-12-25 15:49:21 +0000
+++ b/sql/sql_builtin.cc.in	2010-08-09 08:32:50 +0000
@@ -23,7 +23,7 @@ extern "C"
 extern
 #endif
 builtin_plugin 
-  @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin;
+  @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin, builtin_mysql_password_plugin;
 
 struct st_mysql_plugin *mysql_optional_plugins[]=
 {
@@ -32,5 +32,5 @@ struct st_mysql_plugin *mysql_optional_p
 
 struct st_mysql_plugin *mysql_mandatory_plugins[]=
 {
-  builtin_binlog_plugin, @mysql_mandatory_plugins@ 0
+  builtin_binlog_plugin, builtin_mysql_password_plugin, @mysql_mandatory_plugins@ 0
 };

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-09-06 13:40:55 +0000
+++ b/sql/sql_class.cc	2010-10-04 12:42:16 +0000
@@ -537,7 +537,8 @@ THD::THD()
   catalog= (char*)"std"; // the only catalog we have for now
   main_security_ctx.init();
   security_ctx= &main_security_ctx;
-  no_errors=password= 0;
+  no_errors= 0;
+  password= 0;
   query_start_used= 0;
   count_cuted_fields= CHECK_FIELD_IGNORE;
   killed= NOT_KILLED;
@@ -1339,6 +1340,20 @@ void THD::cleanup_after_query()
 }
 
 
+LEX_STRING *
+make_lex_string_root(MEM_ROOT *mem_root,
+                     LEX_STRING *lex_str, const char* str, uint length,
+                     bool allocate_lex_string)
+{
+  if (allocate_lex_string)
+    if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
+      return 0;
+  if (!(lex_str->str= strmake_root(mem_root, str, length)))
+    return 0;
+  lex_str->length= length;
+  return lex_str;
+}
+
 /**
   Create a LEX_STRING in this connection.
 
@@ -1353,13 +1368,8 @@ LEX_STRING *THD::make_lex_string(LEX_STR
                                  const char* str, uint length,
                                  bool allocate_lex_string)
 {
-  if (allocate_lex_string)
-    if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING))))
-      return 0;
-  if (!(lex_str->str= strmake_root(mem_root, str, length)))
-    return 0;
-  lex_str->length= length;
-  return lex_str;
+  return make_lex_string_root (mem_root, lex_str, str,
+                               length, allocate_lex_string);
 }
 
 
@@ -2925,9 +2935,9 @@ void THD::set_status_var_init()
 
 void Security_context::init()
 {
-  host= user= priv_user= ip= 0;
+  host= user= ip= external_user= 0;
   host_or_ip= "connecting host";
-  priv_host[0]= '\0';
+  priv_user[0]= priv_host[0]= '\0';
   master_access= 0;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   db_access= NO_ACCESS;
@@ -2949,6 +2959,12 @@ void Security_context::destroy()
     user= NULL;
   }
 
+  if (external_user)
+  {
+    my_free(external_user);
+    user= NULL;
+  }
+
   my_free(ip);
   ip= NULL;
 }
@@ -2959,8 +2975,7 @@ void Security_context::skip_grants()
   /* privileges for the user are unknown everything is allowed */
   host_or_ip= (char *)"";
   master_access= ~NO_ACCESS;
-  priv_user= (char *)"";
-  *priv_host= '\0';
+  *priv_user= *priv_host= '\0';
 }
 
 
@@ -3002,7 +3017,7 @@ bool Security_context::set_user(char *us
   of a statement under credentials of a different user, e.g.
   definer of a procedure, we authenticate this user in a local
   instance of Security_context by means of this method (and
-  ultimately by means of acl_getroot_no_password), and make the
+  ultimately by means of acl_getroot), and make the
   local instance active in the thread by re-setting
   thd->security_ctx pointer.
 
@@ -3036,19 +3051,12 @@ change_security_context(THD *thd,
   DBUG_ASSERT(definer_user->str && definer_host->str);
 
   *backup= NULL;
-  /*
-    The current security context may have NULL members
-    if we have just started the thread and not authenticated
-    any user. This use case is currently in events worker thread.
-  */
-  needs_change= (thd->security_ctx->priv_user == NULL ||
-                 strcmp(definer_user->str, thd->security_ctx->priv_user) ||
-                 thd->security_ctx->priv_host == NULL ||
+  needs_change= (strcmp(definer_user->str, thd->security_ctx->priv_user) ||
                  my_strcasecmp(system_charset_info, definer_host->str,
                                thd->security_ctx->priv_host));
   if (needs_change)
   {
-    if (acl_getroot_no_password(this, definer_user->str, definer_host->str,
+    if (acl_getroot(this, definer_user->str, definer_host->str,
                                 definer_host->str, db->str))
     {
       my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str,
@@ -3467,6 +3475,10 @@ void THD::get_definer(LEX_USER *definer)
     definer->host= invoker_host;
     definer->password.str= NULL;
     definer->password.length= 0;
+    definer->plugin.str= (char *) "";
+    definer->plugin.length= 0;
+    definer->auth.str=  (char *) "";
+    definer->auth.length= 0;
   }
   else
 #endif

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-08-30 14:07:40 +0000
+++ b/sql/sql_class.h	2010-09-20 14:17:32 +0000
@@ -862,9 +862,13 @@ public:
     priv_user - The user privilege we are using. May be "" for anonymous user.
     ip - client IP
   */
-  char   *host, *user, *priv_user, *ip;
+  char   *host, *user, *ip;
+  char   priv_user[USERNAME_LENGTH];
+  char   proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
   /* The host privilege we are using */
   char   priv_host[MAX_HOSTNAME];
+  /* The external user (if available) */
+  char   *external_user;
   /* points to host if host is available, otherwise points to ip */
   const char *host_or_ip;
   ulong master_access;                 /* Global privileges from mysql.user */
@@ -1963,7 +1967,8 @@ public:
   char	     scramble[SCRAMBLE_LENGTH+1];
 
   bool       slave_thread, one_shot_set;
-  bool	     no_errors, password;
+  bool	     no_errors;
+  uchar      password;
   /**
     Set to TRUE if execution of the current compound statement
     can not continue. In particular, disables activation of
@@ -2774,6 +2779,11 @@ my_eof(THD *thd)
 #define reenable_binlog(A)   (A)->variables.option_bits= tmp_disable_binlog__save_options;}
 
 
+LEX_STRING *
+make_lex_string_root(MEM_ROOT *mem_root,
+                     LEX_STRING *lex_str, const char* str, uint length,
+                     bool allocate_lex_string);
+
 /*
   Used to hold information about file and file structure in exchange
   via non-DB file (...INTO OUTFILE..., ...LOAD DATA...)

=== modified file 'sql/sql_connect.cc'
--- a/sql/sql_connect.cc	2010-08-16 12:50:27 +0000
+++ b/sql/sql_connect.cc	2010-09-20 14:17:32 +0000
@@ -62,9 +62,9 @@
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 static HASH hash_user_connections;
 
-static int get_or_create_user_conn(THD *thd, const char *user,
-				   const char *host,
-				   USER_RESOURCES *mqh)
+int get_or_create_user_conn(THD *thd, const char *user,
+                            const char *host,
+                            const USER_RESOURCES *mqh)
 {
   int return_val= 0;
   size_t temp_len, user_len;
@@ -130,7 +130,6 @@ end:
     1	error
 */
 
-static
 int check_for_max_user_connections(THD *thd, USER_CONN *uc)
 {
   int error=0;
@@ -291,256 +290,6 @@ end:
 
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 
-
-/**
-  Check if user exist and password supplied is correct.
-
-  @param  thd         thread handle, thd->security_ctx->{host,user,ip} are used
-  @param  command     originator of the check: now check_user is called
-                      during connect and change user procedures; used for
-                      logging.
-  @param  passwd      scrambled password received from client
-  @param  passwd_len  length of scrambled password
-  @param  db          database name to connect to, may be NULL
-  @param  check_count TRUE if establishing a new connection. In this case
-                      check that we have not exceeded the global
-                      max_connections limist
-
-  @note Host, user and passwd may point to communication buffer.
-  Current implementation does not depend on that, but future changes
-  should be done with this in mind; 'thd' is INOUT, all other params
-  are 'IN'.
-
-  @retval  0  OK; thd->security_ctx->user/master_access/priv_user/db_access and
-              thd->db are updated; OK is sent to the client.
-  @retval  1  error, e.g. access denied or handshake error, not sent to
-              the client. A message is pushed into the error stack.
-*/
-
-int
-check_user(THD *thd, enum enum_server_command command,
-	       const char *passwd, uint passwd_len, const char *db,
-	       bool check_count)
-{
-  DBUG_ENTER("check_user");
-  LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 };
-
-  /*
-    Clear thd->db as it points to something, that will be freed when
-    connection is closed. We don't want to accidentally free a wrong
-    pointer if connect failed. Also in case of 'CHANGE USER' failure,
-    current database will be switched to 'no database selected'.
-  */
-  thd->reset_db(NULL, 0);
-
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
-  thd->main_security_ctx.master_access= GLOBAL_ACLS;       // Full rights
-  /* Change database if necessary */
-  if (db && db[0])
-  {
-    if (mysql_change_db(thd, &db_str, FALSE))
-      DBUG_RETURN(1);
-  }
-  my_ok(thd);
-  DBUG_RETURN(0);
-#else
-
-  my_bool opt_secure_auth_local;
-  mysql_mutex_lock(&LOCK_global_system_variables);
-  opt_secure_auth_local= opt_secure_auth;
-  mysql_mutex_unlock(&LOCK_global_system_variables);
-  
-  /*
-    If the server is running in secure auth mode, short scrambles are 
-    forbidden.
-  */
-  if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
-  {
-    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
-    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
-    DBUG_RETURN(1);
-  }
-  if (passwd_len != 0 &&
-      passwd_len != SCRAMBLE_LENGTH &&
-      passwd_len != SCRAMBLE_LENGTH_323)
-  {
-    my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-    DBUG_RETURN(1);
-  }
-
-  USER_RESOURCES ur;
-  int res= acl_getroot(thd, &ur, passwd, passwd_len);
-#ifndef EMBEDDED_LIBRARY
-  if (res == -1)
-  {
-    /*
-      This happens when client (new) sends password scrambled with
-      scramble(), but database holds old value (scrambled with
-      scramble_323()). Here we please client to send scrambled_password
-      in old format.
-    */
-    NET *net= &thd->net;
-    if (opt_secure_auth_local)
-    {
-      my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
-               thd->main_security_ctx.user,
-               thd->main_security_ctx.host_or_ip);
-      general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
-                        thd->main_security_ctx.user,
-                        thd->main_security_ctx.host_or_ip);
-      DBUG_RETURN(1);
-    }
-    /* We have to read very specific packet size */
-    if (send_old_password_request(thd) ||
-        my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
-    {
-      inc_host_errors(thd->main_security_ctx.ip);
-
-      my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-      DBUG_RETURN(1);
-    }
-    /* Final attempt to check the user based on reply */
-    /* So as passwd is short, errcode is always >= 0 */
-    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
-  }
-#endif /*EMBEDDED_LIBRARY*/
-  /* here res is always >= 0 */
-  if (res == 0)
-  {
-    if (!(thd->main_security_ctx.master_access &
-          NO_ACCESS)) // authentication is OK
-    {
-      DBUG_PRINT("info",
-                 ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
-                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
-                  "Access: %lu  db: '%s'",
-                  thd->client_capabilities,
-                  thd->max_client_packet_length,
-                  thd->main_security_ctx.host_or_ip,
-                  thd->main_security_ctx.user,
-                  thd->main_security_ctx.priv_user,
-                  passwd_len ? "yes": "no",
-                  thd->main_security_ctx.master_access,
-                  (thd->db ? thd->db : "*none*")));
-
-      if (check_count)
-      {
-        mysql_mutex_lock(&LOCK_connection_count);
-        bool count_ok= connection_count <= max_connections ||
-                       (thd->main_security_ctx.master_access & SUPER_ACL);
-        mysql_mutex_unlock(&LOCK_connection_count);
-
-        if (!count_ok)
-        {                                         // too many connections
-          my_error(ER_CON_COUNT_ERROR, MYF(0));
-          DBUG_RETURN(1);
-        }
-      }
-
-      /*
-        Log the command before authentication checks, so that the user can
-        check the log for the tried login tried and also to detect
-        break-in attempts.
-      */
-      general_log_print(thd, command,
-                        (thd->main_security_ctx.priv_user ==
-                         thd->main_security_ctx.user ?
-                         (char*) "%s@%s on %s" :
-                         (char*) "%s@%s as anonymous on %s"),
-                        thd->main_security_ctx.user,
-                        thd->main_security_ctx.host_or_ip,
-                        db ? db : (char*) "");
-
-      /*
-        This is the default access rights for the current database.  It's
-        set to 0 here because we don't have an active database yet (and we
-        may not have an active database to set.
-      */
-      thd->main_security_ctx.db_access=0;
-
-      /* Don't allow user to connect if he has done too many queries */
-      if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
-	   global_system_variables.max_user_connections) &&
-	  get_or_create_user_conn(thd,
-            (opt_old_style_user_limits ? thd->main_security_ctx.user :
-             thd->main_security_ctx.priv_user),
-            (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
-             thd->main_security_ctx.priv_host),
-            &ur))
-      {
-        /* The error is set by get_or_create_user_conn(). */
-	DBUG_RETURN(1);
-      }
-      if (thd->user_connect &&
-	  (thd->user_connect->user_resources.conn_per_hour ||
-	   thd->user_connect->user_resources.user_conn ||
-	   global_system_variables.max_user_connections) &&
-	  check_for_max_user_connections(thd, thd->user_connect))
-      {
-        /* The error is set in check_for_max_user_connections(). */
-        DBUG_RETURN(1);
-      }
-
-      /* Change database if necessary */
-      if (db && db[0])
-      {
-        if (mysql_change_db(thd, &db_str, FALSE))
-        {
-          /* mysql_change_db() has pushed the error message. */
-          if (thd->user_connect)
-          {
-            decrease_user_connections(thd->user_connect);
-            thd->user_connect= 0;
-          }
-          DBUG_RETURN(1);
-        }
-      }
-      my_ok(thd);
-      thd->password= test(passwd_len);          // remember for error messages 
-#ifndef EMBEDDED_LIBRARY
-      /*
-        Allow the network layer to skip big packets. Although a malicious
-        authenticated session might use this to trick the server to read
-        big packets indefinitely, this is a previously established behavior
-        that needs to be preserved as to not break backwards compatibility.
-      */
-      thd->net.skip_big_packet= TRUE;
-#endif
-      /* Ready to handle queries */
-      DBUG_RETURN(0);
-    }
-  }
-  else if (res == 2) // client gave short hash, server has long hash
-  {
-    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
-    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
-    DBUG_RETURN(1);
-  }
-  my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
-           thd->main_security_ctx.user,
-           thd->main_security_ctx.host_or_ip,
-           passwd_len ? ER(ER_YES) : ER(ER_NO));
-  general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
-                    thd->main_security_ctx.user,
-                    thd->main_security_ctx.host_or_ip,
-                    passwd_len ? ER(ER_YES) : ER(ER_NO));
-  /*
-    Log access denied messages to the error log when log-warnings = 2
-    so that the overhead of the general query log is not required to track
-    failed connections.
-  */
-  if (global_system_variables.log_warnings > 1)
-  {
-    sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
-                      thd->main_security_ctx.user,
-                      thd->main_security_ctx.host_or_ip,
-                      passwd_len ? ER(ER_YES) : ER(ER_NO));
-  }
-  DBUG_RETURN(1);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-}
-
-
 /*
   Check for maximum allowable user connections, if the mysqld server is
   started with corresponding variable that is greater then 0.
@@ -673,17 +422,14 @@ bool init_new_connection_handler_thread(
     thd  thread handle
 
   RETURN
-     0  success, OK is sent to user, thd is updated.
-    -1  error, which is sent to user
-   > 0  error code (not sent to user)
+     0  success, thd is updated.
+     1  error
 */
 
 static int check_connection(THD *thd)
 {
   uint connect_errors= 0;
   NET *net= &thd->net;
-  ulong pkt_len= 0;
-  char *end;
 
   DBUG_PRINT("info",
              ("New connection received on %s", vio_description(net->vio)));
@@ -748,203 +494,10 @@ static int check_connection(THD *thd)
   }
   vio_keepalive(net->vio, TRUE);
   
-  ulong server_capabilites;
-  {
-    /* buff[] needs to big enough to hold the server_version variable */
-    char buff[SERVER_VERSION_LENGTH + 1 + SCRAMBLE_LENGTH + 1 + 64];
-    server_capabilites= CLIENT_BASIC_FLAGS;
-
-    if (opt_using_transactions)
-      server_capabilites|= CLIENT_TRANSACTIONS;
-#ifdef HAVE_COMPRESS
-    server_capabilites|= CLIENT_COMPRESS;
-#endif /* HAVE_COMPRESS */
-#if defined(HAVE_OPENSSL)
-    if (ssl_acceptor_fd)
-    {
-      server_capabilites |= CLIENT_SSL;       /* Wow, SSL is available! */
-      server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT;
-    }
-#endif /* HAVE_OPENSSL */
-
-    end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
-    int4store((uchar*) end, thd->thread_id);
-    end+= 4;
-    /*
-      So as check_connection is the only entry point to authorization
-      procedure, scramble is set here. This gives us new scramble for
-      each handshake.
-    */
-    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
-    /*
-      Old clients does not understand long scrambles, but can ignore packet
-      tail: that's why first part of the scramble is placed here, and second
-      part at the end of packet.
-    */
-    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
-   
-    int2store(end, server_capabilites);
-    /* write server characteristics: up to 16 bytes allowed */
-    end[2]=(char) default_charset_info->number;
-    int2store(end+3, thd->server_status);
-    bzero(end+5, 13);
-    end+= 18;
-    /* write scramble tail */
-    end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, 
-                 SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
-
-    /* At this point we write connection message and read reply */
-    if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0,
-                          (uchar*) buff, (size_t) (end-buff)) ||
-	(pkt_len= my_net_read(net)) == packet_error ||
-	pkt_len < MIN_HANDSHAKE_SIZE)
-    {
-      inc_host_errors(thd->main_security_ctx.ip);
-
-      my_error(ER_HANDSHAKE_ERROR, MYF(0),
-               thd->main_security_ctx.host_or_ip);
-      return 1;
-    }
-  }
-#ifdef _CUSTOMCONFIG_
-#include "_cust_sql_parse.h"
-#endif
-  if (connect_errors)
-    reset_host_errors(thd->main_security_ctx.ip);
   if (thd->packet.alloc(thd->variables.net_buffer_length))
     return 1; /* The error is set by alloc(). */
 
-  thd->client_capabilities= uint2korr(net->read_pos);
-  if (thd->client_capabilities & CLIENT_PROTOCOL_41)
-  {
-    thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
-    thd->max_client_packet_length= uint4korr(net->read_pos+4);
-    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
-    thd_init_client_charset(thd, (uint) net->read_pos[8]);
-    thd->update_charset();
-    end= (char*) net->read_pos+32;
-  }
-  else
-  {
-    thd->max_client_packet_length= uint3korr(net->read_pos+2);
-    end= (char*) net->read_pos+5;
-  }
-  /*
-    Disable those bits which are not supported by the server.
-    This is a precautionary measure, if the client lies. See Bug#27944.
-  */
-  thd->client_capabilities&= server_capabilites;
-
-  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
-    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
-#if defined(HAVE_OPENSSL)
-  DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
-  if (thd->client_capabilities & CLIENT_SSL)
-  {
-    /* Do the SSL layering. */
-    if (!ssl_acceptor_fd)
-    {
-      inc_host_errors(thd->main_security_ctx.ip);
-      my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-      return 1;
-    }
-    DBUG_PRINT("info", ("IO layer change in progress..."));
-    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
-    {
-      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
-      inc_host_errors(thd->main_security_ctx.ip);
-
-      my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-      return 1;
-    }
-    DBUG_PRINT("info", ("Reading user information over SSL layer"));
-    if ((pkt_len= my_net_read(net)) == packet_error ||
-	pkt_len < NORMAL_HANDSHAKE_SIZE)
-    {
-      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
-			   pkt_len));
-      inc_host_errors(thd->main_security_ctx.ip);
-
-      my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-      return 1;
-    }
-  }
-#endif /* HAVE_OPENSSL */
-
-  if (end >= (char*) net->read_pos+ pkt_len +2)
-  {
-    inc_host_errors(thd->main_security_ctx.ip);
-
-    my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-    return 1;
-  }
-
-  if (thd->client_capabilities & CLIENT_INTERACTIVE)
-    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
-  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
-      opt_using_transactions)
-    net->return_status= &thd->server_status;
-
-  char *user= end;
-  char *passwd= strend(user)+1;
-  uint user_len= passwd - user - 1;
-  char *db= passwd;
-  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;
-
-  /*
-    Old clients send null-terminated string as password; new clients send
-    the size (1 byte) + string (not null-terminated). Hence in case of empty
-    password both send '\0'.
-
-    This strlen() can't be easily deleted without changing protocol.
-
-    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
-    *passwd > 127 and become 2**32-127+ after casting to uint.
-  */
-  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
-    (uchar)(*passwd++) : strlen(passwd);
-  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
-    db + passwd_len + 1 : 0;
-  /* strlen() can't be easily deleted without changing protocol */
-  uint db_len= db ? strlen(db) : 0;
-
-  if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
-  {
-    inc_host_errors(thd->main_security_ctx.ip);
-
-    my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
-    return 1;
-  }
-
-  /* Since 4.1 all database names are stored in utf8 */
-  if (db)
-  {
-    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
-                             system_charset_info,
-                             db, db_len,
-                             thd->charset(), &dummy_errors)]= 0;
-    db= db_buff;
-  }
-
-  user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
-                                       system_charset_info, user, user_len,
-                                       thd->charset(), &dummy_errors)]= '\0';
-  user= user_buff;
-
-  /* If username starts and ends in "'", chop them off */
-  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
-  {
-    user[user_len-1]= 0;
-    user++;
-    user_len-= 2;
-  }
-
-  my_free(thd->main_security_ctx.user);
-  if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME))))
-    return 1; /* The error is set by my_strdup(). */
-  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
+  return acl_authenticate(thd, connect_errors, 0);
 }
 
 

=== modified file 'sql/sql_connect.h'
--- a/sql/sql_connect.h	2010-06-07 14:01:39 +0000
+++ b/sql/sql_connect.h	2010-09-20 14:17:32 +0000
@@ -43,5 +43,8 @@ int check_user(THD *thd, enum enum_serve
 bool login_connection(THD *thd);
 void prepare_new_connection_state(THD* thd);
 void end_connection(THD *thd);
+int get_or_create_user_conn(THD *thd, const char *user,
+                            const char *host, const USER_RESOURCES *mqh);
+int check_for_max_user_connections(THD *thd, USER_CONN *uc);
 
 #endif /* SQL_CONNECT_INCLUDED */

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-09-30 10:43:43 +0000
+++ b/sql/sql_insert.cc	2010-10-04 12:42:16 +0000
@@ -1896,8 +1896,10 @@ public:
      status(0), handler_thread_initialized(FALSE), group_count(0)
   {
     DBUG_ENTER("Delayed_insert constructor");
-    thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user;
+    thd.security_ctx->user=(char*) delayed_user;
     thd.security_ctx->host=(char*) my_localhost;
+    strmake(thd.security_ctx->priv_user, thd.security_ctx->user,
+            USERNAME_LENGTH);
     thd.current_tablenr=0;
     thd.command=COM_DELAYED_INSERT;
     thd.lex->current_select= 0; 		// for my_message_sql

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-09-02 13:57:59 +0000
+++ b/sql/sql_lex.cc	2010-10-05 14:22:30 +0000
@@ -41,6 +41,7 @@ sys_var *trg_new_row_fake_var= (sys_var*
   LEX_STRING constant for null-string to be used in parser and other places.
 */
 const LEX_STRING null_lex_str= {NULL, 0};
+const LEX_STRING empty_lex_str= {(char *) "", 0};
 /**
   @note The order of the elements of this array must correspond to
   the order of elements in enum_binlog_stmt_unsafe.

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2010-09-24 08:44:09 +0000
+++ b/sql/sql_lex.h	2010-10-04 12:42:16 +0000
@@ -1047,6 +1047,8 @@ struct st_sp_chistics
   enum enum_sp_data_access daccess;
 };
 
+extern const LEX_STRING null_lex_str;
+extern const LEX_STRING empty_lex_str;
 
 struct st_trg_chistics
 {
@@ -2317,6 +2319,7 @@ struct LEX: public Query_tables_list
   sp_name *spname;
   bool sp_lex_in_use;	/* Keep track on lex usage in SPs for error handling */
   bool all_privileges;
+  bool proxy_priv;
   sp_pcontext *spcont;
 
   st_sp_chistics sp_chistics;

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-09-22 08:15:41 +0000
+++ b/sql/sql_parse.cc	2010-10-04 12:42:16 +0000
@@ -502,9 +502,8 @@ static void handle_bootstrap_impl(THD *t
 #endif /* EMBEDDED_LIBRARY */
 
   thd_proc_info(thd, 0);
-  thd->security_ctx->priv_user=
-    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
-  thd->security_ctx->priv_host[0]=0;
+  thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+  thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
   /*
     Make the "client" handle multiple results. This is necessary
     to enable stored procedures with SELECTs and Dynamic SQL
@@ -937,96 +936,34 @@ bool dispatch_command(enum enum_server_c
   case COM_CHANGE_USER:
   {
     status_var_increment(thd->status_var.com_other);
-    char *user= (char*) packet, *packet_end= packet + packet_length;
-    /* Safe because there is always a trailing \0 at the end of the packet */
-    char *passwd= strend(user)+1;
 
     thd->change_user();
     thd->clear_error();                         // if errors from rollback
 
-    /*
-      Old clients send null-terminated string ('\0' for empty string) for
-      password.  New clients send the size (1 byte) + string (not null
-      terminated, so also '\0' for empty string).
-
-      Cast *passwd to an unsigned char, so that it doesn't extend the sign
-      for *passwd > 127 and become 2**32-127 after casting to uint.
-    */
-    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8
-    char *db= passwd;
-    char *save_db;
-    /*
-      If there is no password supplied, the packet must contain '\0',
-      in any type of handshake (4.1 or pre-4.1).
-     */
-    if (passwd >= packet_end)
-    {
-      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
-      break;
-    }
-    uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
-                      (uchar)(*passwd++) : strlen(passwd));
-    uint dummy_errors, save_db_length, db_length;
-    int res;
-    Security_context save_security_ctx= *thd->security_ctx;
-    USER_CONN *save_user_connect;
-
-    db+= passwd_len + 1;
-    /*
-      Database name is always NUL-terminated, so in case of empty database
-      the packet must contain at least the trailing '\0'.
-    */
-    if (db >= packet_end)
-    {
-      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
-      break;
-    }
-    db_length= strlen(db);
-
-    char *ptr= db + db_length + 1;
-    uint cs_number= 0;
-
-    if (ptr < packet_end)
-    {
-      if (ptr + 2 > packet_end)
-      {
-        my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
-        break;
-      }
-
-      cs_number= uint2korr(ptr);
-    }
-
-    /* Convert database name to utf8 */
-    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
-                             system_charset_info, db, db_length,
-                             thd->charset(), &dummy_errors)]= 0;
-    db= db_buff;
+    /* acl_authenticate() takes the data from net->read_pos */
+    net->read_pos= (uchar*)packet;
 
-    /* Save user and privileges */
-    save_db_length= thd->db_length;
-    save_db= thd->db;
-    save_user_connect= thd->user_connect;
-
-    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
-    {
-      thd->security_ctx->user= save_security_ctx.user;
-      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
-      break;
-    }
-
-    /* Clear variables that are allocated */
-    thd->user_connect= 0;
-    thd->security_ctx->priv_user= thd->security_ctx->user;
-    res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
+    uint save_db_length= thd->db_length;
+    char *save_db= thd->db;
+    USER_CONN *save_user_connect= thd->user_connect;
+    Security_context save_security_ctx= *thd->security_ctx;
+    CHARSET_INFO *save_character_set_client=
+      thd->variables.character_set_client;
+    CHARSET_INFO *save_collation_connection=
+      thd->variables.collation_connection;
+    CHARSET_INFO *save_character_set_results=
+      thd->variables.character_set_results;
 
-    if (res)
+    if (acl_authenticate(thd, 0, packet_length))
     {
       my_free(thd->security_ctx->user);
       *thd->security_ctx= save_security_ctx;
       thd->user_connect= save_user_connect;
-      thd->db= save_db;
-      thd->db_length= save_db_length;
+      thd->reset_db (save_db, save_db_length);
+      thd->variables.character_set_client= save_character_set_client;
+      thd->variables.collation_connection= save_collation_connection;
+      thd->variables.character_set_results= save_character_set_results;
+      thd->update_charset();
     }
     else
     {
@@ -1037,12 +974,6 @@ bool dispatch_command(enum enum_server_c
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
       my_free(save_db);
       my_free(save_security_ctx.user);
-
-      if (cs_number)
-      {
-        thd_init_client_charset(thd, cs_number);
-        thd->update_charset();
-      }
     }
     break;
   }
@@ -3483,7 +3414,8 @@ end_with_restore_list:
   case SQLCOM_REVOKE:
   case SQLCOM_GRANT:
   {
-    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
+    if (lex->type != TYPE_ENUM_PROXY &&
+        check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
                      first_table ?  first_table->db : select_lex->db,
                      first_table ? &first_table->grant.privilege : NULL,
                      first_table ? &first_table->grant.m_internal : NULL,
@@ -3493,6 +3425,7 @@ end_with_restore_list:
     if (thd->security_ctx->user)              // If not replication
     {
       LEX_USER *user, *tmp_user;
+      bool first_user= TRUE;
 
       List_iterator <LEX_USER> user_list(lex->users_list);
       while ((tmp_user= user_list++))
@@ -3507,20 +3440,23 @@ end_with_restore_list:
                               user->host.str);
         // Are we trying to change a password of another user
         DBUG_ASSERT(user->host.str != 0);
-        if (strcmp(thd->security_ctx->user, user->user.str) ||
-            my_strcasecmp(system_charset_info,
-                          user->host.str, thd->security_ctx->host_or_ip))
+
+        /*
+          GRANT/REVOKE PROXY has the target user as a first entry in the list. 
+         */
+        if (lex->type == TYPE_ENUM_PROXY && first_user)
         {
-          // TODO: use check_change_password()
-          if (is_acl_user(user->host.str, user->user.str) &&
-              user->password.str &&
-              check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1))
-          {
-            my_message(ER_PASSWORD_NOT_ALLOWED,
-                       ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
+          first_user= FALSE;
+          if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
+                                        lex->grant & GRANT_ACL))
             goto error;
-          }
-        }
+        } 
+        else if (is_acl_user(user->host.str, user->user.str) &&
+                 user->password.str &&
+                 check_change_password (thd, user->host.str, user->user.str, 
+                                        user->password.str, 
+                                        user->password.length))
+          goto error;
       }
     }
     if (first_table)
@@ -3555,16 +3491,19 @@ end_with_restore_list:
     }
     else
     {
-      if (lex->columns.elements || lex->type)
+      if (lex->columns.elements || (lex->type && lex->type != TYPE_ENUM_PROXY))
       {
 	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                    MYF(0));
         goto error;
       }
       else
-	/* Conditionally writes to binlog */
-	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
-			  lex->sql_command == SQLCOM_REVOKE);
+      {
+        /* Conditionally writes to binlog */
+        res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+                          lex->sql_command == SQLCOM_REVOKE,
+                          lex->type == TYPE_ENUM_PROXY);
+      }
       if (!res)
       {
 	if (lex->sql_command == SQLCOM_GRANT)
@@ -3864,8 +3803,8 @@ end_with_restore_list:
         if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
                                 lex->sql_command == SQLCOM_CREATE_PROCEDURE))
           push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                       ER_PROC_AUTO_GRANT_FAIL,
-                       ER(ER_PROC_AUTO_GRANT_FAIL));
+                       ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
+        thd->clear_error();
       }
 
       /*
@@ -4759,12 +4698,19 @@ check_access(THD *thd, ulong want_access
   {						// We can never grant this
     DBUG_PRINT("error",("No possible access"));
     if (!no_errors)
-      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
-               sctx->priv_user,
-               sctx->priv_host,
-               (thd->password ?
-                ER(ER_YES) :
-                ER(ER_NO)));                    /* purecov: tested */
+    {
+      if (thd->password == 2)
+        my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+                 sctx->priv_user,
+                 sctx->priv_host);
+      else
+        my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+                 sctx->priv_user,
+                 sctx->priv_host,
+                 (thd->password ?
+                  ER(ER_YES) :
+                  ER(ER_NO)));                    /* purecov: tested */
+    }
     DBUG_RETURN(TRUE);				/* purecov: tested */
   }
 
@@ -7008,8 +6954,9 @@ void get_default_definer(THD *thd, LEX_U
   definer->host.str= (char *) sctx->priv_host;
   definer->host.length= strlen(definer->host.str);
 
-  definer->password.str= NULL;
-  definer->password.length= 0;
+  definer->password= null_lex_str;
+  definer->plugin= empty_lex_str;
+  definer->auth= empty_lex_str;
 }
 
 

=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc	2010-09-21 21:24:29 +0000
+++ b/sql/sql_plugin.cc	2010-10-04 12:42:16 +0000
@@ -30,6 +30,7 @@
 #include <my_pthread.h>
 #include <my_getopt.h>
 #include "sql_audit.h"
+#include <mysql/plugin_auth.h>
 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
 #define REPORT_TO_LOG  1
 #define REPORT_TO_USER 2
@@ -65,6 +66,7 @@ const LEX_STRING plugin_type_names[MYSQL
   { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
   { C_STRING_WITH_LEN("AUDIT") },
   { C_STRING_WITH_LEN("REPLICATION") },
+  { C_STRING_WITH_LEN("AUTHENTICATION") }
 };
 
 extern int initialize_schema_table(st_plugin_int *plugin);
@@ -81,13 +83,13 @@ extern int finalize_audit_plugin(st_plug
 plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
   0,ha_initialize_handlerton,0,0,initialize_schema_table,
-  initialize_audit_plugin
+  initialize_audit_plugin,0,0
 };
 
 plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
   0,ha_finalize_handlerton,0,0,finalize_schema_table,
-  finalize_audit_plugin
+  finalize_audit_plugin,0,0
 };
 
 #ifdef HAVE_DLOPEN
@@ -110,7 +112,8 @@ static int min_plugin_info_interface_ver
   MYSQL_DAEMON_INTERFACE_VERSION,
   MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
   MYSQL_AUDIT_INTERFACE_VERSION,
-  MYSQL_REPLICATION_INTERFACE_VERSION
+  MYSQL_REPLICATION_INTERFACE_VERSION,
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION
 };
 static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
 {
@@ -120,7 +123,8 @@ static int cur_plugin_info_interface_ver
   MYSQL_DAEMON_INTERFACE_VERSION,
   MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
   MYSQL_AUDIT_INTERFACE_VERSION,
-  MYSQL_REPLICATION_INTERFACE_VERSION
+  MYSQL_REPLICATION_INTERFACE_VERSION,
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION
 };
 
 /* support for Services */

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2010-10-01 13:32:03 +0000
+++ b/sql/sql_yacc.yy	2010-10-04 12:42:16 +0000
@@ -1189,6 +1189,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  PROCESSLIST_SYM
 %token  PROFILE_SYM
 %token  PROFILES_SYM
+%token  PROXY_SYM
 %token  PURGE
 %token  QUARTER_SYM
 %token  QUERY_SYM
@@ -12316,6 +12317,9 @@ user:
             $$->user = $1;
             $$->host.str= (char *) "%";
             $$->host.length= 1;
+            $$->password= null_lex_str; 
+            $$->plugin= empty_lex_str;
+            $$->auth= empty_lex_str;
 
             if (check_string_char_length(&$$->user, ER(ER_USERNAME),
                                          USERNAME_CHAR_LENGTH,
@@ -12328,6 +12332,9 @@ user:
             if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
               MYSQL_YYABORT;
             $$->user = $1; $$->host=$3;
+            $$->password= null_lex_str; 
+            $$->plugin= empty_lex_str;
+            $$->auth= empty_lex_str;
 
             if (check_string_char_length(&$$->user, ER(ER_USERNAME),
                                          USERNAME_CHAR_LENGTH,
@@ -12590,6 +12597,7 @@ keyword_sp:
         | PROCESSLIST_SYM          {}
         | PROFILE_SYM              {}
         | PROFILES_SYM             {}
+        | PROXY_SYM                {}
         | QUARTER_SYM              {}
         | QUERY_SYM                {}
         | QUICK                    {}
@@ -12976,7 +12984,7 @@ option_value:
             if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
               MYSQL_YYABORT;
             user->host=null_lex_str;
-            user->user.str=thd->security_ctx->priv_user;
+            user->user.str=thd->security_ctx->user;
             set_var_password *var= new set_var_password(user, $3);
             if (var == NULL)
               MYSQL_YYABORT;
@@ -13336,6 +13344,13 @@ revoke_command:
           {
             Lex->sql_command = SQLCOM_REVOKE_ALL;
           }
+        | PROXY_SYM ON user FROM grant_list
+          {
+            LEX *lex= Lex;
+            lex->users_list.push_front ($3);
+            lex->sql_command= SQLCOM_REVOKE;
+            lex->type= TYPE_ENUM_PROXY;
+          } 
         ;
 
 grant:
@@ -13375,6 +13390,13 @@ grant_command:
             lex->sql_command= SQLCOM_GRANT;
             lex->type= TYPE_ENUM_PROCEDURE;
           }
+        | PROXY_SYM ON user TO_SYM grant_list opt_grant_option
+          {
+            LEX *lex= Lex;
+            lex->users_list.push_front ($3);
+            lex->sql_command= SQLCOM_GRANT;
+            lex->type= TYPE_ENUM_PROXY;
+          } 
         ;
 
 opt_table:
@@ -13568,6 +13590,8 @@ grant_user:
           user IDENTIFIED_SYM BY TEXT_STRING
           {
             $$=$1; $1->password=$4;
+            if (Lex->sql_command == SQLCOM_REVOKE)
+              MYSQL_YYABORT;
             if ($4.length)
             {
               if (YYTHD->variables.old_passwords)
@@ -13593,7 +13617,28 @@ grant_user:
             }
           }
         | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
-          { $$= $1; $1->password= $5; }
+          { 
+            if (Lex->sql_command == SQLCOM_REVOKE)
+              MYSQL_YYABORT;
+            $$= $1; 
+            $1->password= $5; 
+          }
+        | user IDENTIFIED_SYM WITH ident_or_text
+          {
+            if (Lex->sql_command == SQLCOM_REVOKE)
+              MYSQL_YYABORT;
+            $$= $1;
+            $1->plugin= $4;
+            $1->auth= empty_lex_str;
+          }
+        | user IDENTIFIED_SYM WITH ident_or_text AS TEXT_STRING_sys
+          {
+            if (Lex->sql_command == SQLCOM_REVOKE)
+              MYSQL_YYABORT;
+            $$= $1;
+            $1->plugin= $4;
+            $1->auth= $6;
+          }
         | user
           { $$= $1; $1->password= null_lex_str; }
         ;
@@ -13665,6 +13710,11 @@ grant_options:
         | WITH grant_option_list
         ;
 
+opt_grant_option:
+          /* empty */ {}
+        | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
+        ;
+
 grant_option_list:
           grant_option_list grant_option {}
         | grant_option {}

=== modified file 'sql/structs.h'
--- a/sql/structs.h	2010-03-31 14:05:33 +0000
+++ b/sql/structs.h	2010-08-09 08:32:50 +0000
@@ -156,7 +156,7 @@ extern const char *show_comp_option_name
 typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
 
 typedef struct	st_lex_user {
-  LEX_STRING user, host, password;
+  LEX_STRING user, host, password, plugin, auth;
 } LEX_USER;
 
 /*

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2010-08-30 14:07:40 +0000
+++ b/sql/sys_vars.cc	2010-09-20 14:17:32 +0000
@@ -1419,6 +1419,14 @@ static Sys_var_uint Sys_protocol_version
        READ_ONLY GLOBAL_VAR(protocol_version), NO_CMD_LINE,
        VALID_RANGE(0, ~0), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1));
 
+static Sys_var_proxy_user Sys_proxy_user(
+       "proxy_user", "The proxy user account name used when logging in",
+       IN_SYSTEM_CHARSET);
+
+static Sys_var_external_user Sys_exterenal_user(
+       "external_user", "The external user account used when logging in",
+       IN_SYSTEM_CHARSET);
+
 static Sys_var_ulong Sys_read_buff_size(
        "read_buffer_size",
        "Each thread that does a sequential scan allocates a buffer of "

=== modified file 'sql/sys_vars.h'
--- a/sql/sys_vars.h	2010-09-02 18:37:04 +0000
+++ b/sql/sys_vars.h	2010-10-04 12:42:16 +0000
@@ -452,6 +452,67 @@ public:
   { return type != STRING_RESULT; }
 };
 
+
+class Sys_var_proxy_user: public sys_var
+{
+public:
+  Sys_var_proxy_user(const char *name_arg,
+          const char *comment, enum charset_enum is_os_charset_arg)
+    : sys_var(&all_sys_vars, name_arg, comment, 
+              sys_var::READONLY+sys_var::ONLY_SESSION, 0, -1,
+              NO_ARG, SHOW_CHAR, (intptr)NULL,
+              0, VARIABLE_NOT_IN_BINLOG,
+              0, 0,
+              0, 0, PARSE_NORMAL)
+  {
+    is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
+    option.var_type= GET_STR;
+  }
+  bool do_check(THD *thd, set_var *var)
+  {
+    DBUG_ASSERT(FALSE);
+    return true;
+  }
+  bool session_update(THD *thd, set_var *var)
+  {
+    DBUG_ASSERT(FALSE);
+    return true;
+  }
+  bool global_update(THD *thd, set_var *var)
+  {
+    DBUG_ASSERT(FALSE);
+    return false;
+  }
+  void session_save_default(THD *thd, set_var *var)
+  { DBUG_ASSERT(FALSE); }
+  void global_save_default(THD *thd, set_var *var)
+  { DBUG_ASSERT(FALSE); }
+  bool check_update_type(Item_result type)
+  { return true; }
+protected:
+  virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+  {
+    return thd->security_ctx->proxy_user[0] ?
+      (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
+  }
+};
+
+class Sys_var_external_user : public Sys_var_proxy_user
+{
+public:
+  Sys_var_external_user(const char *name_arg, const char *comment_arg, 
+          enum charset_enum is_os_charset_arg) 
+    : Sys_var_proxy_user (name_arg, comment_arg, is_os_charset_arg)
+  {}
+
+protected:
+  virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+  {
+    return thd->security_ctx->proxy_user[0] ?
+      (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
+  }
+};
+
 /**
   The class for string variables. Useful for strings that aren't necessarily
   \0-terminated. Otherwise the same as Sys_var_charptr.

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2010-10-01 12:16:00 +0000
+++ b/sql/table.cc	2010-10-04 12:42:16 +0000
@@ -4091,11 +4091,8 @@ bool TABLE_LIST::prepare_view_securety_c
   {
     DBUG_PRINT("info", ("This table is suid view => load contest"));
     DBUG_ASSERT(view && view_sctx);
-    if (acl_getroot_no_password(view_sctx,
-                                definer.user.str,
-                                definer.host.str,
-                                definer.host.str,
-                                thd->db))
+    if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
+                                definer.host.str, thd->db))
     {
       if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
           (thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -4114,10 +4111,15 @@ bool TABLE_LIST::prepare_view_securety_c
         }
         else
         {
-           my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
-                    thd->security_ctx->priv_user,
-                    thd->security_ctx->priv_host,
-                    (thd->password ?  ER(ER_YES) : ER(ER_NO)));
+          if (thd->password == 2)
+            my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+                     thd->security_ctx->priv_user,
+                     thd->security_ctx->priv_host);
+          else
+            my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+                     thd->security_ctx->priv_user,
+                     thd->security_ctx->priv_host,
+                     (thd->password ?  ER(ER_YES) : ER(ER_NO)));
         }
         DBUG_RETURN(TRUE);
       }

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2010-09-07 07:49:47 +0000
+++ b/tests/mysql_client_test.c	2010-10-04 12:42:16 +0000
@@ -33,6 +33,7 @@
 #include <my_getopt.h>
 #include <m_string.h>
 #include <mysqld_error.h>
+#include <sql_common.h>
 
 #define VER "2.1"
 #define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */

=== modified file 'unittest/mysys/CMakeLists.txt'
--- a/unittest/mysys/CMakeLists.txt	2010-08-12 15:19:57 +0000
+++ b/unittest/mysys/CMakeLists.txt	2010-08-16 15:16:07 +0000
@@ -22,7 +22,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/
 
 MACRO (MY_ADD_TEST name)
   ADD_EXECUTABLE(${name}-t ${name}-t.c)
-  TARGET_LINK_LIBRARIES(${name}-t mytap mysys)
+  TARGET_LINK_LIBRARIES(${name}-t mytap mysys strings)
   ADD_TEST(${name} ${name}-t)
 ENDMACRO()
 

Attachment: [text/bzr-bundle] bzr/jon.hauglid@oracle.com-20101006092944-bb621wfi4ai8ja7i.bundle
Thread
bzr push into mysql-5.5-runtime branch (jon.hauglid:3154 to 3155) Jon Olav Hauglid6 Oct