List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:October 4 2010 3:31pm
Subject:bzr push into mysql-trunk-merge branch (Georgi.Kodinov:3223)
View as plain text  
 3223 Georgi Kodinov	2010-10-04 [merge]
      merge

=== modified file 'Makefile.am'
--- a/Makefile.am	2010-10-04 12:06:54 +0000
+++ b/Makefile.am	2010-10-04 15:25:10 +0000
@@ -269,7 +269,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:41:37 +0000
+++ b/client/client_priv.h	2010-10-04 15:25:10 +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_RAW_OUTPUT, OPT_WAIT_SERVER_ID, OPT_STOP_NEVER,
   OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
   OPT_MAX_CLIENT_OPTION

=== modified file 'client/mysql.cc'
--- a/client/mysql.cc	2010-08-23 17:01:12 +0000
+++ b/client/mysql.cc	2010-10-04 15:25:10 +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 11:08:23 +0000
+++ b/client/mysqltest.cc	2010-10-04 15:25:10 +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");
@@ -5435,6 +5442,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, "");
@@ -6233,6 +6246,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 'config.h.cmake'
--- a/config.h.cmake	2010-10-01 10:23:16 +0000
+++ b/config.h.cmake	2010-10-04 15:25:10 +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 12:06:54 +0000
+++ b/configure.in	2010-10-04 15:25:10 +0000
@@ -1619,9 +1619,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:41:24 +0000
+++ b/include/Makefile.am	2010-10-04 15:25:10 +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 \
@@ -57,7 +59,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 12:06:54 +0000
+++ b/include/my_global.h	2010-10-04 15:25:10 +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.
 
@@ -1365,7 +1375,7 @@ do { doubleget_union _tmp; \
 #endif
 
 #ifndef HAVE_DLERROR
-#define dlerror() ""
+#define dlerror() "No support for dynamic loading (static build?)"
 #endif
 
 

=== modified file 'include/my_no_pthread.h'
--- a/include/my_no_pthread.h	2010-02-14 00:59:39 +0000
+++ b/include/my_no_pthread.h	2010-10-04 15:25:10 +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:59:42 +0000
+++ b/include/my_sys.h	2010-10-04 15:25:10 +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-10 08:06:58 +0000
+++ b/include/mysql.h.pp	2010-10-04 15:25:10 +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-08-09 08:32:50 +0000
@@ -0,0 +1,41 @@
+#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]; int (*init)(char *, size_t, int, va_list); int (*deinit)();
+};
+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]; int (*init)(char *, size_t, int, va_list); int (*deinit)();
+  int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
+};
+typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql,
+                      int type, const char *prompt, char *buf, int buf_len);
+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);

=== modified file 'include/mysql/plugin.h'
--- a/include/mysql/plugin.h	2010-09-01 13:05:01 +0000
+++ b/include/mysql/plugin.h	2010-10-04 15:25:10 +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-08-09 08:32:50 +0000
@@ -0,0 +1,205 @@
+#include <mysql/plugin.h>
+#include <mysql/services.h>
+#include <mysql/service_my_snprintf.h>
+#include <stdarg.h>
+#include <stdlib.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>
+#include <stdlib.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);
+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);
+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
+{
+  const 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 13:10:22 +0000
+++ b/include/mysql_com.h	2010-10-04 15:25:10 +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/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-20 08:48:59 +0000
+++ b/libmysqld/CMakeLists.txt	2010-10-04 15:25:10 +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 12:50:57 +0000
+++ b/libmysqld/Makefile.am	2010-10-04 15:25:10 +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-08 07:09:13 +0000
+++ b/libmysqld/lib_sql.cc	2010-10-04 15:25:10 +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
 };
 
@@ -615,6 +613,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);
 }
 
@@ -678,14 +677,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;
@@ -694,14 +699,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));
@@ -712,37 +718,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;
 

=== 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/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl	2010-09-30 10:55:39 +0000
+++ b/mysql-test/mysql-test-run.pl	2010-10-04 15:25:10 +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 == $$ )
   {
@@ -1057,6 +1060,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 +1963,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) {
@@ -5038,6 +5075,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")

=== 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 13:10:22 +0000
+++ b/mysql-test/r/grant.result	2010-10-04 15:25:10 +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-16 21:00:50 +0000
+++ b/mysql-test/r/information_schema.result	2010-10-04 15:25:10 +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/partition_exchange.result'
--- a/mysql-test/r/partition_exchange.result	2010-10-01 19:28:07 +0000
+++ b/mysql-test/r/partition_exchange.result	2010-10-04 15:25:10 +0000
@@ -583,8 +583,8 @@ ALTER TABLE tp EXCHANGE PARTITION p0 WIT
 ERROR HY000: Non matching attribute 'MAX_ROWS' between partition and table
 SHOW WARNINGS;
 Level	Code	Message
-Error	1702	Non matching attribute 'MAX_ROWS' between partition and table
-Error	1702	Non matching attribute 'MIN_ROWS' between partition and table
+Error	1705	Non matching attribute 'MAX_ROWS' between partition and table
+Error	1705	Non matching attribute 'MIN_ROWS' between partition and table
 DROP TABLE t;
 RENAME TABLE tmp2 TO t;
 ALTER TABLE t ADD KEY ba_key (b, a);

=== 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-destruct.result'
--- a/mysql-test/r/sp-destruct.result	2010-10-01 19:28:07 +0000
+++ b/mysql-test/r/sp-destruct.result	2010-10-04 15:25:10 +0000
@@ -147,6 +147,6 @@ alter table mysql.proc drop column type;
 # The below statement should not cause assertion failure.
 drop database mysqltest;
 Warnings:
-Error	1698	Column count of mysql.proc is wrong. Expected 20, found 19. The table is probably corrupted
+Error	1701	Column count of mysql.proc is wrong. Expected 20, found 19. The table is probably corrupted
 # Restore mysql.proc.
 drop table mysql.proc;

=== 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 07:22:36 +0000
+++ b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result	2010-10-04 15:25:10 +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 07:22:36 +0000
+++ b/mysql-test/suite/funcs_1/r/memory_trig_03e.result	2010-10-04 15:25:10 +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 07:22:36 +0000
+++ b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result	2010-10-04 15:25:10 +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_lc0.result'
--- a/mysql-test/suite/perfschema/r/pfs_upgrade_lc0.result	2010-08-12 15:25:01 +0000
+++ b/mysql-test/suite/perfschema/r/pfs_upgrade_lc0.result	2010-10-04 15:25:10 +0000
@@ -25,7 +25,7 @@ ERROR 1050 (42S01) at line 240: Table 'S
 ERROR 1050 (42S01) at line 249: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 257: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 266: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 682: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 684: 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 240: Table 'S
 ERROR 1050 (42S01) at line 249: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 257: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 266: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 682: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 684: 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 240: Table 'S
 ERROR 1050 (42S01) at line 249: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 257: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 266: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 682: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 684: 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 240: Table 'S
 ERROR 1050 (42S01) at line 249: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 257: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 266: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 682: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 684: 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 240: Table 'S
 ERROR 1050 (42S01) at line 249: Table 'SETUP_INSTRUMENTS' already exists
 ERROR 1050 (42S01) at line 257: Table 'SETUP_TIMERS' already exists
 ERROR 1050 (42S01) at line 266: Table 'THREADS' already exists
-ERROR 1644 (HY000) at line 682: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 684: 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-03-18 15:35:06 +0000
+++ b/mysql-test/suite/perfschema/r/privilege.result	2010-10-04 15:25:10 +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/perfschema/r/privilege_table_io.result'
--- a/mysql-test/suite/perfschema/r/privilege_table_io.result	2010-07-13 14:17:39 +0000
+++ b/mysql-test/suite/perfschema/r/privilege_table_io.result	2010-10-04 15:25:10 +0000
@@ -34,6 +34,9 @@ wait/io/table/sql/handler	handler.cc:	TA
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	db	fetch	NULL
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	db	fetch	NULL
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	db	fetch	NULL
+wait/io/table/sql/handler	handler.cc:	TABLE	mysql	proxy_priv	fetch	NULL
+wait/io/table/sql/handler	handler.cc:	TABLE	mysql	proxy_priv	fetch	NULL
+wait/io/table/sql/handler	handler.cc:	TABLE	mysql	proxy_priv	fetch	NULL
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	tables_priv	fetch	NULL
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	procs_priv	fetch	NULL
 wait/io/table/sql/handler	handler.cc:	TABLE	mysql	servers	fetch	NULL

=== modified file 'mysql-test/suite/rpl/r/rpl_do_grant.result'
--- a/mysql-test/suite/rpl/r/rpl_do_grant.result	2010-07-20 09:24:47 +0000
+++ b/mysql-test/suite/rpl/r/rpl_do_grant.result	2010-10-04 15:25:10 +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
 

=== 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/mysql_system_tables.sql'
--- a/scripts/mysql_system_tables.sql	2010-08-12 15:25:01 +0000
+++ b/scripts/mysql_system_tables.sql	2010-10-04 15:25:10 +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;
@@ -474,3 +474,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-23 17:20:00 +0000
+++ b/scripts/mysql_system_tables_fix.sql	2010-10-04 15:25:10 +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:41:37 +0000
+++ b/sql-common/client.c	2010-10-04 15:25:10 +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 12:54:41 +0000
@@ -0,0 +1,475 @@
+/* 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
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+                  0, GetLastError(), 0, win_errormsg, 2048, NULL);
+    errormsg= win_errormsg;
+#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:29:26 +0000
+++ b/sql/CMakeLists.txt	2010-10-04 15:25:10 +0000
@@ -48,6 +48,7 @@ SET (SQL_SOURCE
                key.cc log.cc lock.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 set_var.cc 

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2010-10-04 12:06:54 +0000
+++ b/sql/Makefile.am	2010-10-04 15:25:10 +0000
@@ -178,7 +178,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			sql_alter.cc \
 			sql_partition_admin.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
 
 libbinlog_la_SOURCES =	log_event.cc log_event_old.cc binlog.cc \
 			rpl_filter.cc rpl_record.cc rpl_record_old.cc \
@@ -199,10 +199,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@
 
@@ -226,6 +226,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	2010-07-02 18:15:21 +0000
+++ b/sql/client_settings.h	2010-10-04 15:25:10 +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-20 08:48:59 +0000
+++ b/sql/ha_ndbcluster.cc	2010-10-04 15:25:10 +0000
@@ -9502,7 +9502,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-20 08:48:59 +0000
+++ b/sql/ha_ndbcluster_binlog.cc	2010-10-04 15:25:10 +0000
@@ -3655,7 +3655,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-07-29 14:15:38 +0000
+++ b/sql/lex.h	2010-10-04 15:25:10 +0000
@@ -422,8 +422,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 12:06:54 +0000
+++ b/sql/mysqld.cc	2010-10-04 15:25:10 +0000
@@ -57,6 +57,7 @@
 #include "rpl_master.h"
 #include "rpl_mi.h"
 #include "rpl_filter.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"));
@@ -3329,6 +3331,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:33:56 +0000
+++ b/sql/protocol.cc	2010-10-04 15:25:10 +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-07-02 18:15:21 +0000
+++ b/sql/protocol.h	2010-10-04 15:25:10 +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-08 07:09:13 +0000
+++ b/sql/set_var.cc	2010-10-04 15:25:10 +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-10-01 16:11:50 +0000
+++ b/sql/share/errmsg-utf8.txt	2010-10-04 15:25:10 +0000
@@ -6356,6 +6356,35 @@ 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"
+
 ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2
   eng "Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted"
   ger "Spaltenanzahl von %s.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"

=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h	2010-08-05 13:10:22 +0000
+++ b/sql/sp_head.h	2010-10-04 15:25:10 +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-10-01 10:23:16 +0000
+++ b/sql/sql_acl.cc	2010-10-04 15:25:10 +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->ha_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:
@@ -3514,10 +3947,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];
@@ -3538,11 +3971,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;
 
   /*
@@ -3628,6 +4076,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;
+    }
 
     /*
       Even if there is an error, we should write to binary log.
@@ -5066,6 +5521,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);
@@ -5225,7 +5686,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");
@@ -5249,10 +5710,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
   /*
@@ -5265,12 +5730,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
 
@@ -5399,7 +5864,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;
@@ -5482,12 +5947,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))
@@ -5549,6 +6017,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'",
@@ -5573,6 +6042,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;
   }
@@ -5611,6 +6083,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);
     }
@@ -5646,6 +6123,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--;
@@ -5681,6 +6163,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
@@ -5815,6 +6303,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);
 }
@@ -6534,38 +7036,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)
-    {
-      make_password_from_salt(passwd_buff, au->salt);
-      combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
-    }
-    else if (au->salt_len == SCRAMBLE_LENGTH_323)
+    if (au->salt_len)
     {
-      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))
@@ -6598,6 +7106,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 */
 
 
@@ -7165,3 +7796,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-07-02 02:58:51 +0000
+++ b/sql/sql_acl.h	2010-10-04 15:25:10 +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-23 17:02:22 +0000
+++ b/sql/sql_audit.h	2010-10-04 15:25:10 +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-10 08:06:58 +0000
+++ b/sql/sql_class.cc	2010-10-04 15:25:10 +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-09-01 13:05:01 +0000
+++ b/sql/sql_class.h	2010-10-04 15:25:10 +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-09-01 13:05:01 +0000
+++ b/sql/sql_connect.cc	2010-10-04 15:25:10 +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-09-01 13:05:01 +0000
+++ b/sql/sql_connect.h	2010-10-04 15:25:10 +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-10-01 10:23:16 +0000
+++ b/sql/sql_insert.cc	2010-10-04 15:25:10 +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-29 14:41:24 +0000
+++ b/sql/sql_lex.cc	2010-10-04 15:25:10 +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= {"", 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-10-01 10:23:16 +0000
+++ b/sql/sql_lex.h	2010-10-04 15:25:10 +0000
@@ -1059,6 +1059,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
 {
@@ -2329,6 +2331,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-10-01 10:23:16 +0000
+++ b/sql/sql_parse.cc	2010-10-04 15:25:10 +0000
@@ -511,9 +511,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
@@ -928,96 +927,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
     {
@@ -1028,12 +965,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;
   }
@@ -3475,7 +3406,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,
@@ -3485,6 +3417,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++))
@@ -3499,20 +3432,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)
@@ -3547,16 +3483,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)
@@ -3856,8 +3795,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();
       }
 
       /*
@@ -4751,12 +4690,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 */
   }
 
@@ -7000,8 +6946,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:26:13 +0000
+++ b/sql/sql_plugin.cc	2010-10-04 15:25:10 +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 16:11:50 +0000
+++ b/sql/sql_yacc.yy	2010-10-04 15:25:10 +0000
@@ -1191,6 +1191,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
@@ -12480,6 +12481,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,
@@ -12492,6 +12496,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,
@@ -12756,6 +12763,7 @@ keyword_sp:
         | PROCESSLIST_SYM          {}
         | PROFILE_SYM              {}
         | PROFILES_SYM             {}
+        | PROXY_SYM                {}
         | QUARTER_SYM              {}
         | QUERY_SYM                {}
         | QUICK                    {}
@@ -13144,7 +13152,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;
@@ -13504,6 +13512,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:
@@ -13543,6 +13558,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:
@@ -13736,6 +13758,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)
@@ -13761,7 +13785,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; }
         ;
@@ -13833,6 +13878,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-07-02 18:15:21 +0000
+++ b/sql/structs.h	2010-10-04 15:25:10 +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-09-01 13:05:01 +0000
+++ b/sql/sys_vars.cc	2010-10-04 15:25:10 +0000
@@ -1404,6 +1404,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-08 07:09:13 +0000
+++ b/sql/sys_vars.h	2010-10-04 15:25:10 +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 16:11:50 +0000
+++ b/sql/table.cc	2010-10-04 15:25:10 +0000
@@ -4099,11 +4099,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))
@@ -4122,10 +4119,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-10 08:06:58 +0000
+++ b/tests/mysql_client_test.c	2010-10-04 15:25:10 +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:27:53 +0000
+++ b/unittest/mysys/CMakeLists.txt	2010-10-04 15:25:10 +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/georgi.kodinov@oracle.com-20101004153056-fwwif4428pkkqezt.bundle
Thread
bzr push into mysql-trunk-merge branch (Georgi.Kodinov:3223) Georgi Kodinov4 Oct