From b2f16628cf4500111d59a8e52cc44851742ddfcc Mon Sep 17 00:00:00 2001
From: Sergei Golubchik <sergii@pisem.net>
Date: Fri, 18 Oct 2013 12:17:49 -0700
Subject: [PATCH] support DEFINER=role and DEFINER=current_role

---
 include/my_user.h                             |   6 +-
 mysql-test/r/acl_roles_definer.result         | 625 ++++++++++++++++++
 mysql-test/r/acl_roles_rename_user.result     |  28 +-
 mysql-test/r/acl_roles_rpl_definer.result     |  49 ++
 .../r/acl_roles_set_role-recursive.result     |   3 +-
 mysql-test/r/trigger_notembedded.result       |   4 +-
 mysql-test/t/acl_roles_definer.test           | 330 +++++++++
 mysql-test/t/acl_roles_rename_user.test       |  36 +-
 mysql-test/t/acl_roles_rpl_definer.test       |  40 ++
 sql-common/my_user.c                          |  32 +-
 sql/log_event.cc                              |  15 +-
 sql/share/errmsg-utf8.txt                     |   5 +-
 sql/sp.cc                                     |  19 +-
 sql/sp_head.cc                                |   9 +-
 sql/sql_acl.cc                                | 503 +++++++-------
 sql/sql_class.cc                              |  10 +-
 sql/sql_class.h                               |  21 +-
 sql/sql_parse.cc                              |  74 +--
 sql/sql_parse.h                               |   6 +-
 sql/sql_show.cc                               |   7 +-
 sql/sql_trigger.cc                            |   4 +-
 sql/sql_view.cc                               |  12 +-
 sql/sql_yacc.yy                               |   8 +-
 sql/structs.h                                 |   8 +
 24 files changed, 1467 insertions(+), 387 deletions(-)
 create mode 100644 mysql-test/r/acl_roles_definer.result
 create mode 100644 mysql-test/r/acl_roles_rpl_definer.result
 create mode 100644 mysql-test/t/acl_roles_definer.test
 create mode 100644 mysql-test/t/acl_roles_rpl_definer.test

diff --git a/include/my_user.h b/include/my_user.h
index 46eb11a500d..404996e996c 100644
--- a/include/my_user.h
+++ b/include/my_user.h
@@ -26,9 +26,9 @@
 
 C_MODE_START
 
-void parse_user(const char *user_id_str, size_t user_id_len,
-                char *user_name_str, size_t *user_name_len,
-                char *host_name_str, size_t *host_name_len);
+int parse_user(const char *user_id_str, size_t user_id_len,
+               char *user_name_str, size_t *user_name_len,
+               char *host_name_str, size_t *host_name_len);
 
 C_MODE_END
 
diff --git a/mysql-test/r/acl_roles_definer.result b/mysql-test/r/acl_roles_definer.result
new file mode 100644
index 00000000000..0010853be78
--- /dev/null
+++ b/mysql-test/r/acl_roles_definer.result
@@ -0,0 +1,625 @@
+create database mysqltest1;
+use mysqltest1;
+create table t1 (a int, b int, c int);
+insert t1 values (1,10,100),(2,20,200);
+create role role1;
+grant select (a) on mysqltest1.t1 to role1;
+grant event,execute,trigger on mysqltest1.* to role1;
+grant role1 to current_user;
+create role role2;
+grant insert,select on mysqltest1.t1 to role2;
+grant event,execute,trigger on mysqltest1.* to role2;
+grant create view on mysqltest1.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+create definer=current_role view test.v1 as select a+b,c from t1;
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role view test.v1 as select a+b,c from t1;
+show create view test.v1;
+View	Create View	character_set_client	collation_connection
+v1	CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `test`.`v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+set role none;
+create definer=role2 view test.v2 as select a+b,c from t1;
+show create view test.v2;
+View	Create View	character_set_client	collation_connection
+v2	CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `test`.`v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+create definer=role3 view test.v3 as select a+b,c from t1;
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show create view test.v3;
+View	Create View	character_set_client	collation_connection
+v3	CREATE ALGORITHM=UNDEFINED DEFINER=`role3`@`%` SQL SECURITY DEFINER VIEW `test`.`v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show grants;
+Grants for foo@localhost
+GRANT role4 TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'foo'@'localhost'
+GRANT CREATE VIEW ON `mysqltest1`.* TO 'foo'@'localhost'
+select * from test.v1;
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v2;
+a+b	c
+11	100
+22	200
+select * from test.v3;
+ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO)
+create definer=role4 view test.v4 as select a+b,c from t1;
+ERROR 42000: ANY command denied to user 'foo'@'localhost' for table 't1'
+select * from t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1'
+set role role4;
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+create view test.v4 as select a+b,c from t1;
+create definer=role4 view test.v5 as select a+b,c from t1;
+select * from test.v4;
+ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v5;
+a+b	c
+11	100
+22	200
+set role none;
+select * from test.v4;
+ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v5;
+a+b	c
+11	100
+22	200
+drop role role4;
+show create view test.v5;
+View	Create View	character_set_client	collation_connection
+v5	CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+Warnings:
+Note	1449	The user specified as a definer ('role4'@'') does not exist
+select * from test.v5;
+ERROR HY000: The user specified as a definer ('role4'@'') does not exist
+grant select on mysqltest1.t1 to role4;
+show create view test.v5;
+View	Create View	character_set_client	collation_connection
+v5	CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+Warnings:
+Note	1449	The user specified as a definer ('role4'@'') does not exist
+select * from test.v5;
+ERROR HY000: The user specified as a definer ('role4'@'') does not exist
+show create view test.v5;
+View	Create View	character_set_client	collation_connection
+v5	CREATE ALGORITHM=UNDEFINED DEFINER=`role4`@`%` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1`	latin1	latin1_swedish_ci
+select * from test.v5;
+a+b	c
+11	100
+22	200
+drop user role4;
+create table t2 select * from t1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333);
+show create trigger tr1;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+tr1		CREATE DEFINER=`role1` trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+set role none;
+insert t2 values (11,22,33);
+ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1'
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+select * from t2;
+a	b	c
+1	10	100
+2	20	200
+create definer=role2 trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333);
+show create trigger tr2;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+tr2		CREATE DEFINER=`role2` trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+delete from t2 where a=1;
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+111	222	333
+select * from t2;
+a	b	c
+2	20	200
+delete from t1 where a=111;
+create definer=role3 trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333);
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show create trigger tr3;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+tr3		CREATE DEFINER=`role3`@`%` trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+update t2 set b=2 where a=2;
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+select * from t2;
+a	b	c
+2	20	200
+flush tables;
+show create trigger tr2;
+Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation
+tr2		CREATE DEFINER=`role2`@`` trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+delete from t2 where a=2;
+ERROR HY000: The user specified as a definer ('role2'@'%') does not exist
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+select * from t2;
+a	b	c
+2	20	200
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+show create procedure pr1;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pr1		CREATE DEFINER=`role1` PROCEDURE `pr1`()
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+set role none;
+call pr1();
+ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1'
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+create definer=role2 procedure pr2() insert t1 values (111, 222, 333);
+show create procedure pr2;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pr2		CREATE DEFINER=`role2` PROCEDURE `pr2`()
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+call pr2();
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+111	222	333
+delete from t1 where a=111;
+create definer=role3 procedure pr3() insert t1 values (111, 222, 333);
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show create procedure pr3;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pr3		CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`()
+insert t1 values (111, 222, 333)	latin1	latin1_swedish_ci	latin1_swedish_ci
+call pr3();
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+update mysql.proc set definer='role2@' where definer='role2';
+call pr2();
+ERROR HY000: The user specified as a definer ('role2'@'%') does not exist
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+show create function fn1;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+fn1		CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11)
+return (select sum(a+b) from t1)	latin1	latin1_swedish_ci	latin1_swedish_ci
+set role none;
+select fn1();
+ERROR 42000: SELECT command denied to user 'role1'@'' for column 'b' in table 't1'
+select * from t1;
+a	b	c
+1	10	100
+2	20	200
+create definer=role2 function fn2() returns int return (select sum(a+b) from t1);
+show create function fn2;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+fn2		CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11)
+return (select sum(a+b) from t1)	latin1	latin1_swedish_ci	latin1_swedish_ci
+select fn2();
+fn2()
+33
+create definer=role3 function fn3() returns int return (select sum(a+b) from t1);
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show create function fn3;
+Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
+fn3		CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11)
+return (select sum(a+b) from t1)	latin1	latin1_swedish_ci	latin1_swedish_ci
+select fn3();
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+set global event_scheduler=on;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 1, 0);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 2, 0);
+show create event e1;
+Event	sql_mode	time_zone	Create Event	character_set_client	collation_connection	Database Collation
+e1		SYSTEM	CREATE DEFINER=`role1` EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0)	latin1	latin1_swedish_ci	latin1_swedish_ci
+set role none;
+create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 3, 0);
+Warnings:
+Note	1449	The user specified as a definer ('role3'@'%') does not exist
+show create event e3;
+Event	sql_mode	time_zone	Create Event	character_set_client	collation_connection	Database Collation
+e3		SYSTEM	CREATE DEFINER=`role3`@`%` EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0)	latin1	latin1_swedish_ci	latin1_swedish_ci
+create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 4, 0);
+show create event e2;
+Event	sql_mode	time_zone	Create Event	character_set_client	collation_connection	Database Collation
+e2		SYSTEM	CREATE DEFINER=`role2` EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0)	latin1	latin1_swedish_ci	latin1_swedish_ci
+set global event_scheduler=off;
+select distinct * from t1;
+a	b	c
+1	10	100
+111	4	0
+2	20	200
+delete from t1 where a=111;
+
+CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET latin1 */;
+
+USE `test`;
+SET @saved_cs_client     = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v1` (
+  `a+b` tinyint NOT NULL,
+  `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client     = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v2` (
+  `a+b` tinyint NOT NULL,
+  `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client     = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v3` (
+  `a+b` tinyint NOT NULL,
+  `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client     = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v4` (
+  `a+b` tinyint NOT NULL,
+  `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client     = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v5` (
+  `a+b` tinyint NOT NULL,
+  `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+
+CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET latin1 */;
+
+USE `mysqltest1`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL,
+  `c` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `t1` VALUES (1,10,100),(2,20,200);
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `t2` (
+  `a` int(11) DEFAULT NULL,
+  `b` int(11) DEFAULT NULL,
+  `c` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `t2` VALUES (2,20,200);
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role1`*/ /*!50003 trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role3`@`%`*/ /*!50003 trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role2`@``*/ /*!50003 trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client  = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;
+/*!50003 SET sql_mode              = '' */ ;;
+/*!50003 SET @saved_time_zone      = @@time_zone */ ;;
+/*!50003 SET time_zone             = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role1`*/ /*!50106 EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0) */ ;;
+/*!50003 SET time_zone             = @saved_time_zone */ ;;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client  = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;
+/*!50003 SET sql_mode              = '' */ ;;
+/*!50003 SET @saved_time_zone      = @@time_zone */ ;;
+/*!50003 SET time_zone             = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role2`*/ /*!50106 EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0) */ ;;
+/*!50003 SET time_zone             = @saved_time_zone */ ;;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client  = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;
+/*!50003 SET sql_mode              = '' */ ;;
+/*!50003 SET @saved_time_zone      = @@time_zone */ ;;
+/*!50003 SET time_zone             = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role3`@`%`*/ /*!50106 EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0) */ ;;
+/*!50003 SET time_zone             = @saved_time_zone */ ;;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;;
+DELIMITER ;
+/*!50106 SET TIME_ZONE= @save_time_zone */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role1` PROCEDURE `pr1`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role2`@`%` PROCEDURE `pr2`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client      = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results     = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client  = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection  = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;
+/*!50003 SET sql_mode              = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode              = @saved_sql_mode */ ;
+/*!50003 SET character_set_client  = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection  = @saved_col_connection */ ;
+
+USE `test`;
+/*!50001 DROP TABLE IF EXISTS `v1`*/;
+/*!50001 SET @saved_cs_client          = @@character_set_client */;
+/*!50001 SET @saved_cs_results         = @@character_set_results */;
+/*!50001 SET @saved_col_connection     = @@collation_connection */;
+/*!50001 SET character_set_client      = latin1 */;
+/*!50001 SET character_set_results     = latin1 */;
+/*!50001 SET collation_connection      = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client      = @saved_cs_client */;
+/*!50001 SET character_set_results     = @saved_cs_results */;
+/*!50001 SET collation_connection      = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v2`*/;
+/*!50001 SET @saved_cs_client          = @@character_set_client */;
+/*!50001 SET @saved_cs_results         = @@character_set_results */;
+/*!50001 SET @saved_col_connection     = @@collation_connection */;
+/*!50001 SET character_set_client      = latin1 */;
+/*!50001 SET character_set_results     = latin1 */;
+/*!50001 SET collation_connection      = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client      = @saved_cs_client */;
+/*!50001 SET character_set_results     = @saved_cs_results */;
+/*!50001 SET collation_connection      = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v3`*/;
+/*!50001 SET @saved_cs_client          = @@character_set_client */;
+/*!50001 SET @saved_cs_results         = @@character_set_results */;
+/*!50001 SET @saved_col_connection     = @@collation_connection */;
+/*!50001 SET character_set_client      = latin1 */;
+/*!50001 SET character_set_results     = latin1 */;
+/*!50001 SET collation_connection      = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`role3`@`%` SQL SECURITY DEFINER */
+/*!50001 VIEW `v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client      = @saved_cs_client */;
+/*!50001 SET character_set_results     = @saved_cs_results */;
+/*!50001 SET collation_connection      = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v4`*/;
+/*!50001 SET @saved_cs_client          = @@character_set_client */;
+/*!50001 SET @saved_cs_results         = @@character_set_results */;
+/*!50001 SET @saved_col_connection     = @@collation_connection */;
+/*!50001 SET character_set_client      = latin1 */;
+/*!50001 SET character_set_results     = latin1 */;
+/*!50001 SET collation_connection      = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`foo`@`localhost` SQL SECURITY DEFINER */
+/*!50001 VIEW `v4` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client      = @saved_cs_client */;
+/*!50001 SET character_set_results     = @saved_cs_results */;
+/*!50001 SET collation_connection      = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v5`*/;
+/*!50001 SET @saved_cs_client          = @@character_set_client */;
+/*!50001 SET @saved_cs_results         = @@character_set_results */;
+/*!50001 SET @saved_col_connection     = @@collation_connection */;
+/*!50001 SET character_set_client      = latin1 */;
+/*!50001 SET character_set_results     = latin1 */;
+/*!50001 SET collation_connection      = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`role4`@`%` SQL SECURITY DEFINER */
+/*!50001 VIEW `v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client      = @saved_cs_client */;
+/*!50001 SET character_set_results     = @saved_cs_results */;
+/*!50001 SET collation_connection      = @saved_col_connection */;
+
+USE `mysqltest1`;
+drop trigger tr1;
+drop trigger tr2;
+drop trigger tr3;
+drop procedure pr1;
+drop procedure pr2;
+drop procedure pr3;
+drop function fn1;
+drop function fn2;
+drop function fn3;
+drop event e1;
+drop event e2;
+drop event e3;
+drop view test.v1, test.v2, test.v3, test.v4, test.v5;
+drop table t1, t2;
+drop role role1, role2;
+drop user foo@localhost;
+drop database mysqltest1;
+use test;
+create user utest;
+prepare stmt1 from 'grant select on *.* to utest';
+execute stmt1;
+show grants for utest;
+Grants for utest@%
+GRANT SELECT ON *.* TO 'utest'@'%'
+drop user utest;
+create role utest;
+execute stmt1;
+show grants for utest;
+Grants for utest
+GRANT SELECT ON *.* TO 'utest'
+drop role utest;
diff --git a/mysql-test/r/acl_roles_rename_user.result b/mysql-test/r/acl_roles_rename_user.result
index 9108a34c143..2145c7e117c 100644
--- a/mysql-test/r/acl_roles_rename_user.result
+++ b/mysql-test/r/acl_roles_rename_user.result
@@ -1,11 +1,9 @@
-create user 'test_user'@'localhost';
-create user 'test_role1'@'';
-update mysql.user set is_role='Y' where user='test_role1';
+create user test_user@localhost;
+create role test_role1;
 insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost',
 'test_user',
 'test_role1');
-create user 'test_role2'@'';
-update mysql.user set is_role='Y' where user='test_role2';
+create role test_role2;
 insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('',
 'test_role1',
 'test_role2');
@@ -25,26 +23,6 @@ select * from roles_mapping;
 HostFk	UserFk	RoleFk
 	test_role1	test_role2
 newhost	test_user_rm	test_role1
-rename user 'test_role2'@'' to 'test_role2_rm'@'';
-select user, host from user where user like 'test%';
-user	host
-test_role1	
-test_role2_rm	
-test_user_rm	newhost
-select * from roles_mapping;
-HostFk	UserFk	RoleFk
-	test_role1	test_role2_rm
-newhost	test_user_rm	test_role1
-rename user 'test_role1'@'' to 'test_role1_rm'@'';
-select user, host from user where user like 'test%';
-user	host
-test_role1_rm	
-test_role2_rm	
-test_user_rm	newhost
-select * from roles_mapping;
-HostFk	UserFk	RoleFk
-	test_role1_rm	test_role2_rm
-newhost	test_user_rm	test_role1_rm
 delete from mysql.roles_mapping;
 delete from mysql.user where user like 'test%';
 flush privileges;
diff --git a/mysql-test/r/acl_roles_rpl_definer.result b/mysql-test/r/acl_roles_rpl_definer.result
new file mode 100644
index 00000000000..158e420c03e
--- /dev/null
+++ b/mysql-test/r/acl_roles_rpl_definer.result
@@ -0,0 +1,49 @@
+include/master-slave.inc
+[connection master]
+create role role1;
+grant execute on test.* to role1;
+grant role1 to current_user;
+set role role1;
+create definer=current_user procedure pcu() select current_user;
+create definer=root@localhost procedure pu() select "root@localhost";
+create definer=current_role procedure pcr() select current_role;
+create definer=role1 procedure pr() select "role1";
+show create procedure pcu;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pcu		CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`()
+select current_user	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pu;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pu		CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`()
+select "root@localhost"	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pcr;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pcr		CREATE DEFINER=`role1` PROCEDURE `pcr`()
+select current_role	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pr;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pr		CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1"	latin1	latin1_swedish_ci	latin1_swedish_ci
+[connection slave]
+show create procedure pcu;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pcu		CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`()
+select current_user	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pu;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pu		CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`()
+select "root@localhost"	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pcr;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pcr		CREATE DEFINER=`role1` PROCEDURE `pcr`()
+select current_role	latin1	latin1_swedish_ci	latin1_swedish_ci
+show create procedure pr;
+Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
+pr		CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1"	latin1	latin1_swedish_ci	latin1_swedish_ci
+drop procedure pcu;
+drop procedure pu;
+drop procedure pcr;
+drop procedure pr;
+drop role role1;
+include/rpl_end.inc
diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result
index 168960fbe62..7c1f46aa1d9 100644
--- a/mysql-test/r/acl_roles_set_role-recursive.result
+++ b/mysql-test/r/acl_roles_set_role-recursive.result
@@ -27,8 +27,7 @@ Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	D
 %	test_role1		N	N	N	N	N	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			Y
 select * from mysql.user where user like 'test_role2';
 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	plugin	authentication_string	is_role
-	test_role2		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			Y
-%	test_role2		N	N	N	N	N	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			Y
+%	test_role2		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			Y
 flush privileges;
 select * from mysql.roles_mapping;
 ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping'
diff --git a/mysql-test/r/trigger_notembedded.result b/mysql-test/r/trigger_notembedded.result
index d94713cbe02..3349d5b007b 100644
--- a/mysql-test/r/trigger_notembedded.result
+++ b/mysql-test/r/trigger_notembedded.result
@@ -162,7 +162,7 @@ SELECT trigger_name, definer FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_n
 trigger_name	definer
 trg1	
 trg2	@
-trg3	@abc@def@@
+trg3	@abc@def@@%
 trg4	@hostname
 trg5	@abcdef@@@hostname
 
@@ -170,7 +170,7 @@ SELECT * FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_name;
 TRIGGER_CATALOG	TRIGGER_SCHEMA	TRIGGER_NAME	EVENT_MANIPULATION	EVENT_OBJECT_CATALOG	EVENT_OBJECT_SCHEMA	EVENT_OBJECT_TABLE	ACTION_ORDER	ACTION_CONDITION	ACTION_STATEMENT	ACTION_ORIENTATION	ACTION_TIMING	ACTION_REFERENCE_OLD_TABLE	ACTION_REFERENCE_NEW_TABLE	ACTION_REFERENCE_OLD_ROW	ACTION_REFERENCE_NEW_ROW	CREATED	SQL_MODE	DEFINER	CHARACTER_SET_CLIENT	COLLATION_CONNECTION	DATABASE_COLLATION
 def	mysqltest_db1	trg1	INSERT	def	mysqltest_db1	t1	0	NULL	SET @a = 1	ROW	BEFORE	NULL	NULL	OLD	NEW	NULL			latin1	latin1_swedish_ci	latin1_swedish_ci
 def	mysqltest_db1	trg2	INSERT	def	mysqltest_db1	t1	0	NULL	SET @a = 2	ROW	AFTER	NULL	NULL	OLD	NEW	NULL		@	latin1	latin1_swedish_ci	latin1_swedish_ci
-def	mysqltest_db1	trg3	UPDATE	def	mysqltest_db1	t1	0	NULL	SET @a = 3	ROW	BEFORE	NULL	NULL	OLD	NEW	NULL		@abc@def@@	latin1	latin1_swedish_ci	latin1_swedish_ci
+def	mysqltest_db1	trg3	UPDATE	def	mysqltest_db1	t1	0	NULL	SET @a = 3	ROW	BEFORE	NULL	NULL	OLD	NEW	NULL		@abc@def@@%	latin1	latin1_swedish_ci	latin1_swedish_ci
 def	mysqltest_db1	trg4	UPDATE	def	mysqltest_db1	t1	0	NULL	SET @a = 4	ROW	AFTER	NULL	NULL	OLD	NEW	NULL		@hostname	latin1	latin1_swedish_ci	latin1_swedish_ci
 def	mysqltest_db1	trg5	DELETE	def	mysqltest_db1	t1	0	NULL	SET @a = 5	ROW	BEFORE	NULL	NULL	OLD	NEW	NULL		@abcdef@@@hostname	latin1	latin1_swedish_ci	latin1_swedish_ci
 
diff --git a/mysql-test/t/acl_roles_definer.test b/mysql-test/t/acl_roles_definer.test
new file mode 100644
index 00000000000..49e948d60c3
--- /dev/null
+++ b/mysql-test/t/acl_roles_definer.test
@@ -0,0 +1,330 @@
+# create view
+# create trigger
+# create procedure
+# create event
+# mysqldump dumping the definer
+
+let MYSQLD_DATADIR=`select @@datadir`;
+
+create database mysqltest1;
+use mysqltest1;
+
+create table t1 (a int, b int, c int);
+insert t1 values (1,10,100),(2,20,200);
+
+# non-priv role granted
+create role role1;
+grant select (a) on mysqltest1.t1 to role1;
+grant event,execute,trigger on mysqltest1.* to role1;
+
+grant role1 to current_user;
+
+# priv role
+create role role2;
+grant insert,select on mysqltest1.t1 to role2;
+grant event,execute,trigger on mysqltest1.* to role2;
+
+# create a non-priv user and a priv role granted to him
+grant create view on mysqltest1.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+
+##################################################
+# views
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role view test.v1 as select a+b,c from t1;
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role view test.v1 as select a+b,c from t1;
+show create view test.v1;
+set role none;
+
+# definer=role_name, privileges ok
+create definer=role2 view test.v2 as select a+b,c from t1;
+show create view test.v2;
+
+# definer=non_existent_role
+create definer=role3 view test.v3 as select a+b,c from t1;
+show create view test.v3;
+
+connect (c1, localhost, foo,,mysqltest1);
+connection c1;
+show grants;
+
+# role1 doesn't have enough privileges for v1 to work
+--error ER_VIEW_INVALID
+select * from test.v1;
+
+# role2 is ok, v2 is ok
+select * from test.v2;
+
+# role3 is treated as a user name role3@%, doesn't exist, v3 fails
+--error ER_ACCESS_DENIED_ERROR
+select * from test.v3;
+
+# fails, no SUPER - cannot specify a definer arbitrarily
+--error ER_TABLEACCESS_DENIED_ERROR
+create definer=role4 view test.v4 as select a+b,c from t1;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from t1;
+set role role4;
+select * from t1;
+
+# can select from t1, but the view won't work, by default definer=current_user
+create view test.v4 as select a+b,c from t1;
+
+# now role4 is the current_role, can be specified as a definer
+create definer=role4 view test.v5 as select a+b,c from t1;
+
+--error ER_VIEW_INVALID
+select * from test.v4;
+select * from test.v5;
+set role none;
+--error ER_VIEW_INVALID
+select * from test.v4;
+select * from test.v5;
+
+connection default;
+
+drop role role4;
+
+show create view test.v5;
+--error ER_NO_SUCH_USER
+select * from test.v5;
+
+grant select on mysqltest1.t1 to role4;
+show create view test.v5;
+--error ER_NO_SUCH_USER
+select * from test.v5;
+
+# pretend it's an old view from before 10.0.5
+perl;
+local $/;
+my $f= "$ENV{MYSQLD_DATADIR}/test/v5.frm";
+open(F, '<', $f) or die "open(<$f): $!";
+$_=<F>;
+s/create-version=2/create-version=1/;
+open(F, '>', $f) or die "open(>$f): $!";
+syswrite F, $_ or die "syswrite($f): $!" 
+EOF
+
+show create view test.v5;
+select * from test.v5;
+drop user role4;
+
+
+##################################################
+# trigger
+##################################################
+
+create table t2 select * from t1;
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role trigger tr1 before insert on t2 for each row
+  insert t1 values (111, 222, 333);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+  insert t1 values (111, 222, 333);
+show create trigger tr1;
+set role none;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+insert t2 values (11,22,33);
+select * from t1;
+select * from t2;
+
+# definer=role_name, privileges ok
+create definer=role2 trigger tr2 before delete on t2 for each row
+  insert t1 values (111, 222, 333);
+show create trigger tr2;
+delete from t2 where a=1;
+select * from t1;
+select * from t2;
+delete from t1 where a=111;
+
+# definer=non_existent_role
+create definer=role3 trigger tr3 before update on t2 for each row
+  insert t1 values (111, 222, 333);
+show create trigger tr3;
+--error ER_NO_SUCH_USER
+update t2 set b=2 where a=2;
+select * from t1;
+select * from t2;
+
+flush tables;
+
+# change triggers to use pre-10.0.5 definer with an empty hostname
+perl;
+local $/;
+my $f= "$ENV{MYSQLD_DATADIR}/mysqltest1/t2.TRG";
+open(F, '<', $f) or die "open(<$f): $!";
+$_=<F>;
+s/'role2'/'role2\@'/;
+s/`role2`/$&\@``/;
+open(F, '>', $f) or die "open(>$f): $!";
+syswrite F, $_ or die "syswrite($f): $!" 
+EOF
+
+show create trigger tr2;
+--error ER_NO_SUCH_USER
+delete from t2 where a=2;
+select * from t1;
+select * from t2;
+
+##################################################
+# stored procedures
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+show create procedure pr1;
+set role none;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+call pr1();
+select * from t1;
+
+# definer=role_name, privileges ok
+create definer=role2 procedure pr2() insert t1 values (111, 222, 333);
+show create procedure pr2;
+call pr2();
+select * from t1;
+delete from t1 where a=111;
+
+# definer=non_existent_role
+create definer=role3 procedure pr3() insert t1 values (111, 222, 333);
+show create procedure pr3;
+--error ER_NO_SUCH_USER
+call pr3();
+select * from t1;
+
+# change a procedure to use pre-10.0.5 definer with an empty hostname
+update mysql.proc set definer='role2@' where definer='role2';
+--error ER_NO_SUCH_USER
+call pr2();
+
+##################################################
+# stored functions
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+show create function fn1;
+set role none;
+
+--error ER_COLUMNACCESS_DENIED_ERROR
+select fn1();
+select * from t1;
+
+# definer=role_name, privileges ok
+create definer=role2 function fn2() returns int return (select sum(a+b) from t1);
+show create function fn2;
+select fn2();
+
+# definer=non_existent_role
+create definer=role3 function fn3() returns int return (select sum(a+b) from t1);
+show create function fn3;
+--error ER_NO_SUCH_USER
+select fn3();
+
+##################################################
+# events
+##################################################
+
+set global event_scheduler=on;
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+  insert t1 values (111, 1, 0);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+  insert t1 values (111, 2, 0);
+show create event e1;
+set role none;
+
+# definer=non_existent_role
+create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do
+  insert t1 values (111, 3, 0);
+show create event e3;
+
+# definer=role_name, privileges ok
+create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do
+  insert t1 values (111, 4, 0);
+show create event e2;
+
+let $wait_condition=select count(*) >= 4 from t1;
+--source include/wait_condition.inc
+
+set global event_scheduler=off;
+
+--sorted_result
+select distinct * from t1;
+delete from t1 where a=111;
+
+##################################################
+# mysqldump
+##################################################
+
+# note that LOCK TABLES won't work because v3 has invalid definer
+
+--exec $MYSQL_DUMP --compact --events --routines --skip-lock-tables --databases test mysqltest1
+
+##################################################
+# cleanup
+##################################################
+
+drop trigger tr1;
+drop trigger tr2;
+drop trigger tr3;
+drop procedure pr1;
+drop procedure pr2;
+drop procedure pr3;
+drop function fn1;
+drop function fn2;
+drop function fn3;
+drop event e1;
+drop event e2;
+drop event e3;
+drop view test.v1, test.v2, test.v3, test.v4, test.v5;
+drop table t1, t2;
+drop role role1, role2;
+drop user foo@localhost;
+drop database mysqltest1;
+use test;
+
+##################################################
+# reexecution
+##################################################
+
+create user utest;
+prepare stmt1 from 'grant select on *.* to utest';
+execute stmt1;
+show grants for utest;
+drop user utest;
+create role utest;
+execute stmt1;
+show grants for utest;
+drop role utest;
+
diff --git a/mysql-test/t/acl_roles_rename_user.test b/mysql-test/t/acl_roles_rename_user.test
index e2808c661ae..37bbfaade2c 100644
--- a/mysql-test/t/acl_roles_rename_user.test
+++ b/mysql-test/t/acl_roles_rename_user.test
@@ -1,15 +1,11 @@
 
 #create a user with no privileges
-create user 'test_user'@'localhost';
-create user 'test_role1'@'';
-#manualy create role
-update mysql.user set is_role='Y' where user='test_role1';
+create user test_user@localhost;
+create role test_role1;
 insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost',
                                                                  'test_user',
                                                                  'test_role1');
-create user 'test_role2'@'';
-#manualy create role
-update mysql.user set is_role='Y' where user='test_role2';
+create role test_role2;
 insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('',
                                                                  'test_role1',
                                                                  'test_role2');
@@ -26,19 +22,21 @@ select user, host from user where user like 'test%';
 --sorted_result
 select * from roles_mapping;
 
-rename user 'test_role2'@'' to 'test_role2_rm'@'';
---sorted_result
-select user, host from user where user like 'test%';
---sorted_result
-select * from roles_mapping;
-
-#role rename
-rename user 'test_role1'@'' to 'test_role1_rm'@'';
---sorted_result
-select user, host from user where user like 'test%';
---sorted_result
-select * from roles_mapping;
+######### role rename does not work yet
+#rename user 'test_role2'@'' to 'test_role2_rm'@'';
+#--sorted_result
+#select user, host from user where user like 'test%';
+#--sorted_result
+#select * from roles_mapping;
+#
+##role rename
+#rename user 'test_role1'@'' to 'test_role1_rm'@'';
+#--sorted_result
+#select user, host from user where user like 'test%';
+#--sorted_result
+#select * from roles_mapping;
 
 delete from mysql.roles_mapping;
 delete from mysql.user where user like 'test%';
 flush privileges;
+
diff --git a/mysql-test/t/acl_roles_rpl_definer.test b/mysql-test/t/acl_roles_rpl_definer.test
new file mode 100644
index 00000000000..ba192d1fa2a
--- /dev/null
+++ b/mysql-test/t/acl_roles_rpl_definer.test
@@ -0,0 +1,40 @@
+#
+# replication of the DEFINER=current_role
+#
+
+--source include/master-slave.inc
+
+create role role1;
+grant execute on test.* to role1;
+grant role1 to current_user;
+set role role1;
+
+create definer=current_user procedure pcu() select current_user;
+create definer=root@localhost procedure pu() select "root@localhost";
+create definer=current_role procedure pcr() select current_role;
+create definer=role1 procedure pr() select "role1";
+
+show create procedure pcu;
+show create procedure pu;
+show create procedure pcr;
+show create procedure pr;
+
+sync_slave_with_master;
+connection slave;
+echo [connection slave];
+
+show create procedure pcu;
+show create procedure pu;
+show create procedure pcr;
+show create procedure pr;
+
+connection master;
+
+drop procedure pcu;
+drop procedure pu;
+drop procedure pcr;
+drop procedure pr;
+drop role role1;
+
+--source include/rpl_end.inc
+
diff --git a/sql-common/my_user.c b/sql-common/my_user.c
index 8d717ea7131..a486f77bc1e 100644
--- a/sql-common/my_user.c
+++ b/sql-common/my_user.c
@@ -30,34 +30,40 @@
     host_name_str   [OUT] Buffer to store host name part.
                           Must be not less than HOSTNAME_LENGTH + 1.
     host_name_len   [OUT] A place to store length of the host name part.
+
+ RETURN
+   0 - if only a user was set, no '@' was found
+   1 - if both user and host were set
 */
 
-void parse_user(const char *user_id_str, size_t user_id_len,
-                char *user_name_str, size_t *user_name_len,
-                char *host_name_str, size_t *host_name_len)
+int parse_user(const char *user_id_str, size_t user_id_len,
+               char *user_name_str, size_t *user_name_len,
+               char *host_name_str, size_t *host_name_len)
 {
   char *p= strrchr(user_id_str, '@');
 
   if (!p)
   {
-    *user_name_len= 0;
+    *user_name_len= user_id_len;
     *host_name_len= 0;
   }
   else
   {
     *user_name_len= (uint) (p - user_id_str);
     *host_name_len= (uint) (user_id_len - *user_name_len - 1);
-
-    if (*user_name_len > USERNAME_LENGTH)
-      *user_name_len= USERNAME_LENGTH;
-
-    if (*host_name_len > HOSTNAME_LENGTH)
-      *host_name_len= HOSTNAME_LENGTH;
-
-    memcpy(user_name_str, user_id_str, *user_name_len);
-    memcpy(host_name_str, p + 1, *host_name_len);
   }
 
+  if (*user_name_len > USERNAME_LENGTH)
+    *user_name_len= USERNAME_LENGTH;
+
+  if (*host_name_len > HOSTNAME_LENGTH)
+    *host_name_len= HOSTNAME_LENGTH;
+
+  memcpy(user_name_str, user_id_str, *user_name_len);
+  memcpy(host_name_str, p + 1, *host_name_len);
+
   user_name_str[*user_name_len]= 0;
   host_name_str[*host_name_len]= 0;
+
+  return p != NULL;
 }
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 9f33c3d8568..4700cbc4d1c 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2853,17 +2853,22 @@ bool Query_log_event::write(IO_CACHE* file)
       user= thd->get_invoker_user();
       host= thd->get_invoker_host();
     }
-    else if (thd->security_ctx->priv_user)
+    else
     {
       Security_context *ctx= thd->security_ctx;
 
-      user.length= strlen(ctx->priv_user);
-      user.str= ctx->priv_user;
-      if (ctx->priv_host[0] != '\0')
+      if (thd->need_binlog_invoker() == THD::INVOKER_USER)
       {
+        user.str= ctx->priv_user;
         host.str= ctx->priv_host;
-        host.length= strlen(ctx->priv_host);
+        host.length= strlen(host.str);
       }
+      else
+      {
+        user.str= ctx->priv_role;
+        host= null_lex_str; // XXX FIXME or empty_lex_str ?
+      }
+      user.length= strlen(user.str);
     }
 
     if (user.length > 0)
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 7516700e56e..6135f8f1b96 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -5489,9 +5489,8 @@ ER_PS_NO_RECURSION
 ER_SP_CANT_SET_AUTOCOMMIT
         eng "Not allowed to set autocommit from a stored function or trigger"
         ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
-ER_MALFORMED_DEFINER
-        eng "Definer is not fully qualified"
-        ger "Definierer des View ist nicht vollständig spezifiziert"
+ER_MALFORMED_DEFINER 0L000
+        eng "Invalid definer"
 ER_VIEW_FRM_NO_USER
         eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
         ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
diff --git a/sql/sp.cc b/sql/sp.cc
index ca0cd553716..5d91279af2a 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -662,9 +662,14 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
   close_system_tables(thd, &open_tables_state_backup);
   table= 0;
 
-  parse_user(definer, strlen(definer),
-             definer_user_name.str, &definer_user_name.length,
-             definer_host_name.str, &definer_host_name.length);
+  if (parse_user(definer, strlen(definer),
+                 definer_user_name.str, &definer_user_name.length,
+                 definer_host_name.str, &definer_host_name.length) &&
+      definer_user_name.length && !definer_host_name.length)
+  {
+    // 'user@' -> 'user@%'
+    definer_host_name= host_not_specified;
+  }
 
   ret= db_load_routine(thd, type, name, sphp,
                        sql_mode, params, returns, body, chistics,
@@ -975,7 +980,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
 {
   int ret;
   TABLE *table;
-  char definer[USER_HOST_BUFF_SIZE];
+  char definer_buf[USER_HOST_BUFF_SIZE];
+  LEX_STRING definer;
   ulonglong saved_mode= thd->variables.sql_mode;
   MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
                                         MDL_key::FUNCTION : MDL_key::PROCEDURE;
@@ -1012,8 +1018,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
     restore_record(table, s->default_values); // Get default values for fields
 
     /* NOTE: all needed privilege checks have been already done. */
-    strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@",
-            thd->lex->definer->host.str, NullS);
+    thd->lex->definer->set_lex_string(&definer, definer_buf);
 
     if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
     {
@@ -1088,7 +1093,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
 
     store_failed= store_failed ||
       table->field[MYSQL_PROC_FIELD_DEFINER]->
-        store(definer, (uint)strlen(definer), system_charset_info);
+        store(definer.str, definer.length, system_charset_info);
 
     ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
     ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 25e64339379..122b6c9b796 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2549,8 +2549,13 @@ sp_head::set_definer(const char *definer, uint definerlen)
   char host_name_holder[HOSTNAME_LENGTH + 1];
   LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
 
-  parse_user(definer, definerlen, user_name.str, &user_name.length,
-             host_name.str, &host_name.length);
+  if (parse_user(definer, definerlen, user_name.str, &user_name.length,
+                 host_name.str, &host_name.length) &&
+      user_name.length && !host_name.length)
+  {
+    // 'user@' -> 'user@%'
+    host_name= host_not_specified;
+  }
 
   set_definer(&user_name, &host_name);
 }
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 938f92220e1..eea2169f7d1 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -314,27 +314,19 @@ public:
 #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,
-                               const char *username, const char *hostname,
-                               char *buff, size_t buffsize);
-static bool show_role_grants(THD *thd, const char *username, const char *hostname,
-                             ACL_USER_BASE *acl_entry,
-                             char *buff, size_t buffsize);
-static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
-                                   bool handle_as_role,
-                                   char *buff, size_t buffsize);
-static bool show_database_privileges(THD *thd,
-                                     const char *username,
-                                     const char *hostname,
-                                     bool handle_as_role,
-                                     char *buff, size_t buffsize);
-static bool show_table_and_column_privileges(THD *thd,
-                                             const char *username,
-                                             const char *hostname,
-                                             bool handle_as_role,
-                                             char *buff, size_t buffsize);
+static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
+static bool show_proxy_grants (THD *, const char *, const char *,
+                               char *, size_t);
+static bool show_role_grants(THD *, const char *, const char *,
+                             ACL_USER_BASE *, char *, size_t);
+static bool show_global_privileges(THD *, ACL_USER_BASE *,
+                                   bool, char *, size_t);
+static bool show_database_privileges(THD *, const char *, const char *,
+                                     char *, size_t);
+static bool show_table_and_column_privileges(THD *, const char *, const char *,
+                                             char *, size_t);
+static int show_routine_grants(THD *, const char *, const char *, HASH *,
+                               const char *, int, char *, int);
 
 class ACL_PROXY_USER :public ACL_ACCESS
 {
@@ -1757,57 +1749,86 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
   sctx->db_access= 0;
   *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
 
-  /*
-     Find acl entry in user database.
-     This is specially tailored to suit the check we do for CALL of
-     a stored procedure; user is set to what is actually a
-     priv_user, which can be ''.
-  */
-  for (i=0 ; i < acl_users.elements ; i++)
+  if (host[0]) // User, not Role
   {
-    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
-    if ((!acl_user_tmp->user.str && !user[0]) ||
-        (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0))
+    /*
+       Find acl entry in user database.
+       This is specially tailored to suit the check we do for CALL of
+       a stored procedure; user is set to what is actually a
+       priv_user, which can be ''.
+    */
+    for (i=0 ; i < acl_users.elements ; i++)
     {
-      if (compare_hostname(&acl_user_tmp->host, host, ip))
+      ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
+      if ((!acl_user_tmp->user.str && !user[0]) ||
+          (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0))
       {
-        acl_user= acl_user_tmp;
-        res= 0;
-        break;
+        if (compare_hostname(&acl_user_tmp->host, host, ip))
+        {
+          acl_user= acl_user_tmp;
+          res= 0;
+          break;
+        }
       }
     }
+
+    if (acl_user)
+    {
+      for (i=0 ; i < acl_dbs.elements ; i++)
+      {
+        ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+        if (!acl_db->user ||
+            (user && user[0] && !strcmp(user, acl_db->user)))
+        {
+          if (compare_hostname(&acl_db->host, host, ip))
+          {
+            if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+            {
+              sctx->db_access= acl_db->access;
+              break;
+            }
+          }
+        }
+      }
+      sctx->master_access= acl_user->access;
+
+      if (acl_user->user.str)
+        strmake_buf(sctx->priv_user, user);
+
+      if (acl_user->host.hostname)
+        strmake_buf(sctx->priv_host, acl_user->host.hostname);
+    }
+  }
+  else // Role, not User
+  {
+    ACL_ROLE *acl_role= find_acl_role(user);
+    if (acl_role)
+    {
+      res= 0;
+      for (i=0 ; i < acl_dbs.elements ; i++)
+      {
+        ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+        if (!acl_db->user ||
+            (user && user[0] && !strcmp(user, acl_db->user)))
+        {
+          if (compare_hostname(&acl_db->host, "", ""))
+          {
+            if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+            {
+              sctx->db_access= acl_db->access;
+              break;
+            }
+          }
+        }
+      }
+      sctx->master_access= acl_role->access;
+
+      if (acl_role->user.str)
+        strmake_buf(sctx->priv_user, user);
+      sctx->priv_host[0]= 0;
+    }
   }
 
-  if (acl_user)
-  {
-    for (i=0 ; i < acl_dbs.elements ; i++)
-    {
-      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
-      if (!acl_db->user ||
-	  (user && user[0] && !strcmp(user, acl_db->user)))
-      {
-	if (compare_hostname(&acl_db->host, host, ip))
-	{
-	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
-	  {
-	    sctx->db_access= acl_db->access;
-	    break;
-	  }
-	}
-      }
-    }
-    sctx->master_access= acl_user->access;
-
-    if (acl_user->user.str)
-      strmake_buf(sctx->priv_user, user);
-    else
-      *sctx->priv_user= 0;
-
-    if (acl_user->host.hostname)
-      strmake_buf(sctx->priv_host, acl_user->host.hostname);
-    else
-      *sctx->priv_host= 0;
-  }
   mysql_mutex_unlock(&acl_cache->lock);
   DBUG_RETURN(res);
 }
@@ -3039,7 +3060,12 @@ bool is_acl_user(const char *host, const char *user)
     return TRUE;
 
   mysql_mutex_lock(&acl_cache->lock);
-  res= find_user_no_anon(host, user, TRUE) != NULL;
+
+  if (*host) // User
+    res= find_user_no_anon(host, user, TRUE) != NULL;
+  else // Role
+    res= find_acl_role(user) != NULL;
+
   mysql_mutex_unlock(&acl_cache->lock);
   return res;
 }
@@ -3345,13 +3371,13 @@ static bool test_if_create_new_users(THD *thd)
 
 static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
 			      ulong rights, bool revoke_grant,
-                              bool can_create_user, bool no_auto_create,
-                              bool handle_as_role)
+                              bool can_create_user, bool no_auto_create)
 {
   int error = -1;
   bool old_row_exists=0;
   char what= (revoke_grant) ? 'N' : 'Y';
   uchar user_key[MAX_KEY_LENGTH];
+  bool handle_as_role= combo.is_role();
   LEX *lex= thd->lex;
   DBUG_ENTER("replace_user_table");
 
@@ -4832,26 +4858,6 @@ table_error:
 }
 
 
-/*
-  A user name specified without a host can be either a
-  username@% (where '@%' is added automatically by the parser)
-  or a role name. Treat it as a role, if such a role exists.
-*/
-static ACL_ROLE *find_and_mark_as_role(LEX_USER *user)
-{
-  if (user->host.str == host_not_specified.str)
-  {
-    ACL_ROLE *role= find_acl_role(user->user.str);
-    if (role)
-    {
-      user->host= empty_lex_str;
-      return role;
-    }
-  }
-  return NULL;
-}
-
-
 /*
   Store table level and column level grants in the privilege tables
 
@@ -5018,21 +5024,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
   {
     int error;
     GRANT_TABLE *grant_table;
-    if (!(Str= get_current_user(thd, tmp_Str)))
+    if (!(Str= get_current_user(thd, tmp_Str, false)))
     {
       result= TRUE;
       continue;
     }
-
-    bool handle_as_role= FALSE;
-    if (find_and_mark_as_role(Str))
-      handle_as_role= TRUE;
-
     /* Create user if needed */
     error=replace_user_table(thd, tables[0].table, *Str,
 			     0, revoke_grant, create_new_users,
                              test(thd->variables.sql_mode &
-                                  MODE_NO_AUTO_CREATE_USER), handle_as_role);
+                                  MODE_NO_AUTO_CREATE_USER));
     if (error)
     {
       result= TRUE;				// Remember error
@@ -5227,21 +5228,16 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
   {
     int error;
     GRANT_NAME *grant_name;
-    if (!(Str= get_current_user(thd, tmp_Str)))
+    if (!(Str= get_current_user(thd, tmp_Str, false)))
     {
       result= TRUE;
       continue;
     }
-
-    bool handle_as_role= FALSE;
-    if (find_and_mark_as_role(Str))
-      handle_as_role=TRUE;
-
     /* Create user if needed */
     error=replace_user_table(thd, tables[0].table, *Str,
 			     0, revoke_grant, create_new_users,
                              test(thd->variables.sql_mode &
-                                  MODE_NO_AUTO_CREATE_USER), handle_as_role);
+                                  MODE_NO_AUTO_CREATE_USER));
     if (error)
     {
       result= TRUE;				// Remember error
@@ -5297,15 +5293,14 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
   DBUG_RETURN(result);
 }
 
-static void append_user(String *str, const char *u, const char *h,
-                        bool handle_as_role)
+static void append_user(String *str, const char *u, const char *h)
 {
   if (str->length())
     str->append(',');
   str->append('\'');
   str->append(u);
   /* hostname part is not relevant for roles, it is always empty */
-  if (!handle_as_role)
+  if (*h)
   {
     str->append(STRING_WITH_LEN("'@'"));
     str->append(h);
@@ -5381,13 +5376,13 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
       /* current_role is NONE */
       if (!thd->security_ctx->priv_role[0])
       {
-        append_user(&wrong_users, "NONE", "", TRUE);
+        append_user(&wrong_users, "NONE", "");
         result= 1;
         continue;
       }
       if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
       {
-        append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE);
+        append_user(&wrong_users, thd->security_ctx->priv_role, "");
         result= 1;
         continue;
       }
@@ -5395,18 +5390,26 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
       /* can not grant current_role to current_role */
       if (granted_role->user.str == current_role.str)
       {
-        append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE);
+        append_user(&wrong_users, thd->security_ctx->priv_role, "");
         result= 1;
         continue;
       }
       username= thd->security_ctx->priv_role;
-      hostname= (char *)"";
+      hostname= const_cast<char*>("");
+    }
+    else if (user->user.str == current_user.str)
+    {
+      role_as_user= NULL;
+      username= thd->security_ctx->priv_user;
+      hostname= thd->security_ctx->priv_host;
     }
     else
     {
-      role_as_user= find_and_mark_as_role(user);
+      if ((role_as_user= find_acl_role(user->user.str)))
+        hostname= const_cast<char*>("");
+      else
+        hostname= user->host.str ? user->host.str : host_not_specified.str;
       username= user->user.str;
-      hostname= user->host.str;
     }
 
     ROLE_GRANT_PAIR *mapping, *hash_entry;
@@ -5421,7 +5424,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
       /* role or user does not exist*/
       if (res == -1)
       {
-        append_user(&wrong_users, username, hostname, role_as_user != NULL);
+        append_user(&wrong_users, username, hostname);
         result= 1;
         continue;
       }
@@ -5434,7 +5437,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
           traverse_role_graph(role, NULL, NULL, NULL, role_explore_detect_cycle,
                               NULL) == 2)
       {
-        append_user(&wrong_users, username, hostname, TRUE);
+        append_user(&wrong_users, username, "");
         result= 1;
         /* need to rollback the mapping added previously */
         remove_role_user_mapping(mapping);
@@ -5450,7 +5453,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
       /* grant was already removed or never existed */
       if (!hash_entry)
       {
-        append_user(&wrong_users, username, hostname, role_as_user != NULL);
+        append_user(&wrong_users, username, hostname);
         result= 1;
         continue;
       }
@@ -5458,7 +5461,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
       int res= remove_role_user_mapping(mapping);
       if (res == -1)
       {
-        append_user(&wrong_users, username, hostname, role_as_user != NULL);
+        append_user(&wrong_users, username, hostname);
         result= 1;
         continue;
       }
@@ -5467,7 +5470,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
     /* write into the roles_mapping table */
     if (replace_roles_mapping_table(tables.table, mapping, revoke))
     {
-      append_user(&wrong_users, username, hostname, TRUE);
+      append_user(&wrong_users, username, "");
       result= 1;
       if (!revoke)
       {
@@ -5599,10 +5602,17 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
   mysql_mutex_lock(&acl_cache->lock);
   grant_version++;
 
+  if (proxied_user)
+  {
+    if (!(proxied_user= get_current_user(thd, proxied_user, false)))
+      DBUG_RETURN(TRUE);
+    DBUG_ASSERT(proxied_user->host.length); // not a Role
+  }
+
   int result=0;
   while ((tmp_Str = str_list++))
   {
-    if (!(Str= get_current_user(thd, tmp_Str)))
+    if (!(Str= get_current_user(thd, tmp_Str, false)))
     {
       result= TRUE;
       continue;
@@ -5615,14 +5625,10 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
     if (tmp_Str->user.str == current_user.str && tmp_Str->password.str)
       Str->password= tmp_Str->password;
 
-    bool handle_as_role= FALSE;
-    if (find_and_mark_as_role(Str))
-      handle_as_role= TRUE;
-
     if (replace_user_table(thd, tables[0].table, *Str,
                            (!db ? rights : 0), revoke_grant, create_new_users,
                            test(thd->variables.sql_mode &
-                                MODE_NO_AUTO_CREATE_USER), handle_as_role))
+                                MODE_NO_AUTO_CREATE_USER)))
       result= -1;
     else if (db)
     {
@@ -6870,14 +6876,6 @@ static uint command_lengths[]=
 };
 
 
-static int show_routine_grants(THD *thd,
-                               const char *username,
-                               const char *hostname,
-                               HASH *hash,
-                               const char *type, int typelen,
-                               bool handle_as_role,
-                               char *buff, int buffsize);
-
 bool print_grants_for_role(THD *thd, ACL_ROLE * role,
                            char *buff, size_t buffsize)
 {
@@ -6887,18 +6885,18 @@ bool print_grants_for_role(THD *thd, ACL_ROLE * role,
   if (show_global_privileges(thd, role, TRUE, buff, buffsize))
     return TRUE;
 
-  if (show_database_privileges(thd, role->user.str, "", TRUE, buff, buffsize))
+  if (show_database_privileges(thd, role->user.str, "", buff, buffsize))
     return TRUE;
 
-  if (show_table_and_column_privileges(thd, role->user.str, "", TRUE, buff, buffsize))
+  if (show_table_and_column_privileges(thd, role->user.str, "", buff, buffsize))
     return TRUE;
 
   if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash,
-                          STRING_WITH_LEN("PROCEDURE"), TRUE, buff, buffsize))
+                          STRING_WITH_LEN("PROCEDURE"), buff, buffsize))
     return TRUE;
 
   if (show_routine_grants(thd, role->user.str, "", &func_priv_hash,
-                          STRING_WITH_LEN("FUNCTION"), TRUE, buff, buffsize))
+                          STRING_WITH_LEN("FUNCTION"), buff, buffsize))
     return TRUE;
 
   return FALSE;
@@ -6951,7 +6949,8 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
   }
   else
   {
-    if (find_and_mark_as_role(lex_user))
+    lex_user= get_current_user(thd, lex_user, false);
+    if (lex_user->is_role())
     {
       rolename= lex_user->user.str;
     }
@@ -7010,28 +7009,28 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
     };
 
     /* Add database access */
-    if (show_database_privileges(thd, username, hostname, FALSE, buff, sizeof(buff)))
+    if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
     {
       error= -1;
       goto end;
     }
 
     /* Add table & column access */
-    if (show_table_and_column_privileges(thd, username, hostname, FALSE, buff, sizeof(buff)))
+    if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
     {
       error= -1;
       goto end;
     }
 
     if (show_routine_grants(thd, username, hostname, &proc_priv_hash,
-                            STRING_WITH_LEN("PROCEDURE"), FALSE, buff, sizeof(buff)))
+                            STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
     {
       error= -1;
       goto end;
     }
 
     if (show_routine_grants(thd, username, hostname, &func_priv_hash,
-                            STRING_WITH_LEN("FUNCTION"), FALSE, buff, sizeof(buff)))
+                            STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
     {
       error= -1;
       goto end;
@@ -7267,7 +7266,6 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
 static bool show_database_privileges(THD *thd,
                                      const char *username,
                                      const char *hostname,
-                                     bool handle_as_role,
                                      char *buff, size_t buffsize)
 {
   ACL_DB *acl_db;
@@ -7295,12 +7293,14 @@ static bool show_database_privileges(THD *thd,
     if (!strcmp(username, user) &&
         !my_strcasecmp(system_charset_info, hostname, host))
     {
-      /* do not print inherited access bits, the role bits present in the
-         table are what matters */
-      if (handle_as_role)
-        want_access=acl_db->initial_access;
-      else
+      /*
+        do not print inherited access bits for roles,
+        the role bits present in the table are what matters
+      */
+      if (*hostname) // User
         want_access=acl_db->access;
+      else // Role
+        want_access=acl_db->initial_access;
       if (want_access)
       {
         String db(buff,sizeof(buff),system_charset_info);
@@ -7331,7 +7331,7 @@ static bool show_database_privileges(THD *thd,
         db.append (STRING_WITH_LEN(".* TO '"));
         db.append(username, strlen(username),
                   system_charset_info);
-        if (!handle_as_role)
+        if (*hostname)
         {
           db.append (STRING_WITH_LEN("'@'"));
           // host and lex_user->host are equal except for case
@@ -7356,7 +7356,6 @@ static bool show_database_privileges(THD *thd,
 static bool show_table_and_column_privileges(THD *thd,
                                              const char *username,
                                              const char *hostname,
-                                             bool handle_as_role,
                                              char *buff, size_t buffsize)
 {
   uint counter, index;
@@ -7385,16 +7384,16 @@ static bool show_table_and_column_privileges(THD *thd,
     {
       ulong table_access;
       ulong cols_access;
-      if (handle_as_role)
-      {
-        table_access= grant_table->init_privs;
-        cols_access= grant_table->init_cols;
-      }
-      else
+      if (*hostname) // User
       {
         table_access= grant_table->privs;
         cols_access= grant_table->cols;
       }
+      else // Role
+      {
+        table_access= grant_table->init_privs;
+        cols_access= grant_table->init_cols;
+      }
 
       if ((table_access | cols_access) != 0)
       {
@@ -7435,8 +7434,8 @@ static bool show_table_and_column_privileges(THD *thd,
                 {
                   GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
                     my_hash_element(hash_columns,col_index);
-                  if ((!handle_as_role && (grant_column->rights & j)) ||
-                      (handle_as_role && (grant_column->init_rights & j)))
+                  if (j & (*hostname ? grant_column->rights         // User
+                                     : grant_column->init_rights))  // Role
                   {
                     if (!found_col)
                     {
@@ -7475,7 +7474,7 @@ static bool show_table_and_column_privileges(THD *thd,
         global.append(STRING_WITH_LEN(" TO '"));
         global.append(username, strlen(username),
                       system_charset_info);
-        if (!handle_as_role)
+        if (*hostname)
         {
           global.append(STRING_WITH_LEN("'@'"));
           // host and lex_user->host are equal except for case
@@ -7499,9 +7498,7 @@ static bool show_table_and_column_privileges(THD *thd,
 
 static int show_routine_grants(THD* thd,
                                const char *username, const char *hostname,
-                               HASH *hash,
-                               const char *type, int typelen,
-                               bool handle_as_role,
+                               HASH *hash, const char *type, int typelen,
                                char *buff, int buffsize)
 {
   uint counter, index;
@@ -7529,10 +7526,10 @@ static int show_routine_grants(THD* thd,
         !my_strcasecmp(system_charset_info, hostname, host))
     {
       ulong proc_access;
-      if (handle_as_role)
-        proc_access= grant_proc->init_privs;
-      else
+      if (*hostname) // User
         proc_access= grant_proc->privs;
+      else // Role
+        proc_access= grant_proc->init_privs;
 
       if (proc_access != 0)
       {
@@ -7572,7 +7569,7 @@ static int show_routine_grants(THD* thd,
 	global.append(STRING_WITH_LEN(" TO '"));
         global.append(username, strlen(username),
 		      system_charset_info);
-        if (!handle_as_role)
+        if (*hostname)
         {
           global.append(STRING_WITH_LEN("'@'"));
           // host and lex_user->host are equal except for case
@@ -8232,9 +8229,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
                         table->key_info->key_part[1].store_length);
     key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
 
-    if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
-                                                   user_key, (key_part_map)3,
-                                                   HA_READ_KEY_EXACT)))
+    error= table->file->ha_index_read_idx_map(table->record[0], 0,
+                                              user_key, (key_part_map)3,
+                                              HA_READ_KEY_EXACT);
+    if (!error && !*host_str)
+    { // verify that we got a role or a user, as needed
+      if (check_is_role(table) != user_from->is_role())
+        error= HA_ERR_KEY_NOT_FOUND;
+    }
+    if (error)
     {
       if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
       {
@@ -8371,7 +8374,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
     DBUG_RETURN(0);
 
   /* same. no roles in PROXY_USERS_ACL */
-  if (struct_no == PROXY_USERS_ACL && !user_from->host.length)
+  if (struct_no == PROXY_USERS_ACL && user_from->is_role())
     DBUG_RETURN(0);
 
   if (struct_no == ROLE_ACL) //no need to scan the structures in this case
@@ -8678,8 +8681,12 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
 {
   int result= 0;
   int found;
+  bool handle_as_role= user_from->is_role();
   DBUG_ENTER("handle_grant_data");
 
+  if (user_to)
+    DBUG_ASSERT(handle_as_role == user_to->is_role());
+
   /* Handle user table. */
   if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
   {
@@ -8688,20 +8695,32 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
   }
   else
   {
-    /* Handle user array. */
-    if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
+    if (handle_as_role)
     {
-      result= 1; /* At least one record/element found. */
-      /* If search is requested, we do not need to search further. */
-      if (! drop && ! user_to)
-        goto end;
+      if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found)
+      {
+        result= 1; /* At least one record/element found. */
+        /* If search is requested, we do not need to search further. */
+        if (! drop && ! user_to)
+          goto end;
+      }
+      else
+      if (find_user_no_anon(user_from->host.str, user_from->user.str, TRUE))
+        goto end; // looking for a role, found a user
     }
-    if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found)
+    else
     {
-      result= 1; /* At least one record/element found. */
-      /* If search is requested, we do not need to search further. */
-      if (! drop && ! user_to)
-        goto end;
+      /* Handle user array. */
+      if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
+      {
+        result= 1; /* At least one record/element found. */
+        /* If search is requested, we do not need to search further. */
+        if (! drop && ! user_to)
+          goto end;
+      }
+      else
+      if (find_acl_role(user_from->user.str))
+        goto end; // looking for a user, found a role
     }
   }
 
@@ -8821,14 +8840,14 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
   DBUG_RETURN(result);
 }
 
-static void append_user(String *str, LEX_USER *user, bool handle_as_role)
+static void append_user(String *str, LEX_USER *user)
 {
   if (str->length())
     str->append(',');
   str->append('\'');
   str->append(user->user.str);
   /* hostname part is not relevant for roles, it is always empty */
-  if (!handle_as_role)
+  if (!user->is_role())
   {
     str->append(STRING_WITH_LEN("'@'"));
     str->append(user->host.str);
@@ -8854,7 +8873,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
 {
   int result;
   String wrong_users;
-  LEX_USER *user_name, *tmp_user_name;
+  LEX_USER *user_name;
   List_iterator <LEX_USER> user_list(list);
   TABLE_LIST tables[GRANT_TABLES];
   bool some_users_created= FALSE;
@@ -8868,30 +8887,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
   mysql_rwlock_wrlock(&LOCK_grant);
   mysql_mutex_lock(&acl_cache->lock);
 
-  while ((tmp_user_name= user_list++))
+  while ((user_name= user_list++))
   {
-    if (handle_as_role)
-    {
-      user_name= tmp_user_name;
-      user_name->host.str= (char *)"";
-      user_name->host.length= 0;
-      /* role already exists */
-      if (find_acl_role(user_name->user.str))
-      {
-        append_user(&wrong_users, user_name, TRUE);
-        result = TRUE;
-        continue;
-      }
-    }
-    else
-    {
-      if (!(user_name= get_current_user(thd, tmp_user_name)))
-      {
-        result= TRUE;
-        continue;
-      }
-    }
-
     if (!user_name->host.str)
       user_name->host= host_not_specified;
 
@@ -8901,17 +8898,16 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
     */
     if (handle_grant_data(tables, 0, user_name, NULL))
     {
-      append_user(&wrong_users, user_name, handle_as_role);
+      append_user(&wrong_users, user_name);
 
       result= TRUE;
       continue;
     }
 
     some_users_created= TRUE;
-    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0,
-                           handle_as_role))
+    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
     {
-      append_user(&wrong_users, user_name, handle_as_role);
+      append_user(&wrong_users, user_name);
       result= TRUE;
     }
   }
@@ -8966,31 +8962,17 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
 
   while ((tmp_user_name= user_list++))
   {
-    if (handle_as_role)
+    user_name= get_current_user(thd, tmp_user_name, false);
+    if (!user_name || handle_as_role != user_name->is_role())
     {
-
-      user_name= tmp_user_name;
-      user_name->host.str= (char *)"";
-      user_name->host.length= 0;
-      if (!find_acl_role(user_name->user.str))
-      {
-        append_user(&wrong_users, user_name, TRUE);
-        result= TRUE;
-        continue;
-      }
-    }
-    else
-    {
-      if (!(user_name= get_current_user(thd, tmp_user_name)))
-      {
-        result= TRUE;
-        continue;
-      }
+      append_user(&wrong_users, tmp_user_name);
+      result= TRUE;
+      continue;
     }
 
     if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
     {
-      append_user(&wrong_users, user_name, handle_as_role);
+      append_user(&wrong_users, user_name);
       result= TRUE;
       continue;
     }
@@ -9058,18 +9040,21 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
 
   while ((tmp_user_from= user_list++))
   {
-    if (!(user_from= get_current_user(thd, tmp_user_from)))
-    {
-      result= TRUE;
-      continue;
-    }
     tmp_user_to= user_list++;
-    if (!(user_to= get_current_user(thd, tmp_user_to)))
+    if (!(user_from= get_current_user(thd, tmp_user_from, false)) ||
+        user_from->is_role())
     {
+      append_user(&wrong_users, user_from);
+      result= TRUE;
+      continue;
+    }
+    if (!(user_to= get_current_user(thd, tmp_user_to, false)) ||
+        user_to->is_role())
+    {
+      append_user(&wrong_users, user_to);
       result= TRUE;
       continue;
     }
-    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
 
     /*
       Search all in-memory structures and grant tables
@@ -9079,7 +9064,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
         handle_grant_data(tables, 0, user_from, user_to) <= 0)
     {
       /* NOTE TODO renaming roles is not yet implemented */
-      append_user(&wrong_users, user_from, FALSE);
+      append_user(&wrong_users, user_from);
       result= TRUE;
       continue;
     }
@@ -9139,7 +9124,7 @@ bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
   List_iterator <LEX_USER> user_list(list);
   while ((tmp_lex_user= user_list++))
   {
-    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
+    if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
     {
       result= -1;
       continue;
@@ -9150,8 +9135,7 @@ bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
       continue;
     }
 
-    if (replace_user_table(thd, tables[0].table,
-                           *lex_user, ~(ulong)0, 1, 0, 0, 0))
+    if (replace_user_table(thd, tables[0].table, *lex_user, ~(ulong)0, 1, 0, 0))
     {
       result= -1;
       continue;
@@ -10257,6 +10241,47 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
 
 #endif
 
+/**
+  Retuns information about user or current user.
+
+  @param[in] thd          thread handler
+  @param[in] user         user
+
+  @return
+    - On success, return a valid pointer to initialized
+    LEX_USER, which contains user information.
+    - On error, return 0.
+*/
+
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
+{
+  if (user->user.str == current_user.str)  // current_user
+    return create_default_definer(thd, false);
+
+  if (user->user.str == current_role.str)  // current_role
+    return create_default_definer(thd, true);
+
+  if (user->host.str == NULL) // Possibly a role
+  {
+    // to be reexecution friendly we have to make a copy
+    LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
+    if (!dup)
+      return 0;
+
+    if (lock)
+      mysql_mutex_lock(&acl_cache->lock);
+    if (find_acl_role(dup->user.str))
+      dup->host= empty_lex_str;
+    else
+      dup->host= host_not_specified;
+    if (lock)
+      mysql_mutex_unlock(&acl_cache->lock);
+    return dup;
+  }
+
+  return user;
+}
+
 struct ACL_internal_schema_registry_entry
 {
   const LEX_STRING *m_name;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index cec46a0a3a7..0f379ba7660 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -938,7 +938,7 @@ THD::THD()
   thr_lock_info_init(&lock_info); /* safety: will be reset after start */
 
   m_internal_handler= NULL;
-  m_binlog_invoker= FALSE;
+  m_binlog_invoker= INVOKER_NONE;
   arena_for_cached_items= 0;
   memset(&invoker_user, 0, sizeof(invoker_user));
   memset(&invoker_host, 0, sizeof(invoker_host));
@@ -1890,7 +1890,7 @@ void THD::cleanup_after_query()
   where= THD::DEFAULT_WHERE;
   /* reset table map for multi-table update */
   table_map_for_update= 0;
-  m_binlog_invoker= FALSE;
+  m_binlog_invoker= INVOKER_NONE;
 
 #ifndef EMBEDDED_LIBRARY
   if (rli_slave)
@@ -4341,9 +4341,9 @@ void THD::leave_locked_tables_mode()
   locked_tables_mode= LTM_NONE;
 }
 
-void THD::get_definer(LEX_USER *definer)
+void THD::get_definer(LEX_USER *definer, bool role)
 {
-  binlog_invoker();
+  binlog_invoker(role);
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
   if (slave_thread && has_invoker())
   {
@@ -4355,7 +4355,7 @@ void THD::get_definer(LEX_USER *definer)
   }
   else
 #endif
-    get_default_definer(this, definer);
+    get_default_definer(this, definer, role);
 }
 
 
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 64de6a63ded..13a3adf5909 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3153,9 +3153,11 @@ public:
   }
   void leave_locked_tables_mode();
   int decide_logging_format(TABLE_LIST *tables);
-  void binlog_invoker() { m_binlog_invoker= TRUE; }
-  bool need_binlog_invoker() { return m_binlog_invoker; }
-  void get_definer(LEX_USER *definer);
+
+  enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE};
+  void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
+  enum need_invoker need_binlog_invoker() { return m_binlog_invoker; }
+  void get_definer(LEX_USER *definer, bool role);
   void set_invoker(const LEX_STRING *user, const LEX_STRING *host)
   {
     invoker_user= *user;
@@ -3230,14 +3232,15 @@ private:
   Diagnostics_area main_da;
 
   /**
-    It will be set TURE if CURRENT_USER() is called in account management
-    statements or default definer is set in CREATE/ALTER SP, SF, Event,
-    TRIGGER or VIEW statements.
+    It will be set if CURRENT_USER() or CURRENT_ROLE() is called in account
+    management statements or default definer is set in CREATE/ALTER SP, SF,
+    Event, TRIGGER or VIEW statements.
 
-    Current user will be binlogged into Query_log_event if m_binlog_invoker
-    is TRUE; It will be stored into invoker_host and invoker_user by SQL thread.
+    Current user or role will be binlogged into Query_log_event if
+    m_binlog_invoker is not NONE; It will be stored into invoker_host and
+    invoker_user by SQL thread.
    */
-  bool m_binlog_invoker;
+  enum need_invoker m_binlog_invoker;
 
   /**
     It points to the invoker in the Query_log_event.
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3ac90ea2b7c..27162b1cac4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1840,7 +1840,7 @@ bool sp_process_definer(THD *thd)
     Query_arena original_arena;
     Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
 
-    lex->definer= create_default_definer(thd);
+    lex->definer= create_default_definer(thd, false);
 
     if (ps_arena)
       thd->restore_active_arena(ps_arena, &original_arena);
@@ -1854,20 +1854,24 @@ bool sp_process_definer(THD *thd)
   }
   else
   {
+    LEX_USER *d= lex->definer= get_current_user(thd, lex->definer);
+    if (!d)
+      DBUG_RETURN(TRUE);
+
     /*
-      If the specified definer differs from the current user, we
+      If the specified definer differs from the current user or role, we
       should check that the current user has SUPER privilege (in order
       to create a stored routine under another user one must have
       SUPER privilege).
     */
-    if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
-         my_strcasecmp(system_charset_info, lex->definer->host.str,
-                       thd->security_ctx->priv_host)) &&
-        check_global_access(thd, SUPER_ACL, true))
-    {
-      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+    bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user);
+    bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role);
+    bool curuserhost= curuser && d->host.str &&
+                  !my_strcasecmp(system_charset_info, d->host.str,
+                                 thd->security_ctx->priv_host);
+    if (!curuserhost && !currole &&
+        check_global_access(thd, SUPER_ACL, false))
       DBUG_RETURN(TRUE);
-    }
   }
 
   /* Check that the specified definer exists. Emit a warning if not. */
@@ -3783,7 +3787,7 @@ end_with_restore_list:
       goto error;
 
     /* Replicate current user as grantor */
-    thd->binlog_invoker();
+    thd->binlog_invoker(false);
 
     if (thd->security_ctx->user)              // If not replication
     {
@@ -7686,16 +7690,23 @@ Item *negate_expression(THD *thd, Item *expr)
   @param[out] definer   definer
 */
  
-void get_default_definer(THD *thd, LEX_USER *definer)
+void get_default_definer(THD *thd, LEX_USER *definer, bool role)
 {
   const Security_context *sctx= thd->security_ctx;
 
-  definer->user.str= (char *) sctx->priv_user;
+  if (role)
+  {
+    definer->user.str= const_cast<char*>(sctx->priv_role);
+    definer->host= empty_lex_str;
+  }
+  else
+  {
+    definer->user.str= const_cast<char*>(sctx->priv_user);
+    definer->host.str= const_cast<char*>(sctx->priv_host);
+    definer->host.length= strlen(definer->host.str);
+  }
   definer->user.length= strlen(definer->user.str);
 
-  definer->host.str= (char *) sctx->priv_host;
-  definer->host.length= strlen(definer->host.str);
-
   definer->password= null_lex_str;
   definer->plugin= empty_lex_str;
   definer->auth= empty_lex_str;
@@ -7713,16 +7724,22 @@ void get_default_definer(THD *thd, LEX_USER *definer)
     - On error, return 0.
 */
 
-LEX_USER *create_default_definer(THD *thd)
+LEX_USER *create_default_definer(THD *thd, bool role)
 {
   LEX_USER *definer;
 
   if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
     return 0;
 
-  thd->get_definer(definer);
+  thd->get_definer(definer, role);
 
-  return definer;
+  if (role && definer->user.length == 0)
+  {
+    my_error(ER_MALFORMED_DEFINER, MYF(0));
+    return 0;
+  }
+  else
+    return definer;
 }
 
 
@@ -7757,27 +7774,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
 }
 
 
-/**
-  Retuns information about user or current user.
-
-  @param[in] thd          thread handler
-  @param[in] user         user
-
-  @return
-    - On success, return a valid pointer to initialized
-    LEX_USER, which contains user information.
-    - On error, return 0.
-*/
-
-LEX_USER *get_current_user(THD *thd, LEX_USER *user)
-{
-  if (user->user.str == current_user.str)  // current_user
-    return create_default_definer(thd);
-
-  return user;
-}
-
-
 /**
   Check that byte length of a string does not exceed some limit.
 
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index c87e290119e..16b5280a7c7 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -63,10 +63,10 @@ Comp_creator *comp_ne_creator(bool invert);
 
 int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                          enum enum_schema_tables schema_table_idx);
-void get_default_definer(THD *thd, LEX_USER *definer);
-LEX_USER *create_default_definer(THD *thd);
+void get_default_definer(THD *thd, LEX_USER *definer, bool role);
+LEX_USER *create_default_definer(THD *thd, bool role);
 LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
-LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true);
 bool sp_process_definer(THD *thd);
 bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
                               uint max_byte_length);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 5fc5afcc0fd..b95d26bd51c 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2064,8 +2064,11 @@ void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
 {
   buffer->append(STRING_WITH_LEN("DEFINER="));
   append_identifier(thd, buffer, definer_user->str, definer_user->length);
-  buffer->append('@');
-  append_identifier(thd, buffer, definer_host->str, definer_host->length);
+  if (definer_host->str[0])
+  {
+    buffer->append('@');
+    append_identifier(thd, buffer, definer_host->str, definer_host->length);
+  }
   buffer->append(' ');
 }
 
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index e0898740047..c7d47487c6a 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -763,9 +763,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
     definer_user= lex->definer->user;
     definer_host= lex->definer->host;
 
-    trg_definer->str= trg_definer_holder;
-    trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
-                                 definer_host.str, NullS) - trg_definer->str;
+    lex->definer->set_lex_string(trg_definer, trg_definer_holder);
   }
   else
   {
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 65151d503a4..1a33cd97a48 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -822,7 +822,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
     goto err;   
   }
 
-  view->file_version= 1;
+  view->file_version= 2;
   view->calc_md5(md5);
   if (!(view->md5.str= (char*) thd->memdup(md5, 32)))
   {
@@ -1113,8 +1113,16 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
     push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                         ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER),
                         table->db, table->table_name);
-    get_default_definer(thd, &table->definer);
+    get_default_definer(thd, &table->definer, false);
   }
+  
+  /*
+    since 10.0.5 definer.host can never be "" for a User, but it's
+    always "" for a Role. Before 10.0.5 it could be "" for a User,
+    but roles didn't exist. file_version helps.
+  */
+  if (!table->definer.host.str[0] && table->file_version < 2)
+    table->definer.host= host_not_specified; // User, not Role
 
   /*
     Initialize view definition context by character set names loaded from
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index dd874caf4b0..332333da620 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13173,7 +13173,7 @@ user:
             if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
               MYSQL_YYABORT;
             $$->user = $1;
-            $$->host= host_not_specified;
+            $$->host= null_lex_str; // User or Role, see get_current_user()
             $$->password= null_lex_str; 
             $$->plugin= empty_lex_str;
             $$->auth= empty_lex_str;
@@ -14351,7 +14351,7 @@ grant_role:
             if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
               MYSQL_YYABORT;
             $$->user = $1;
-            $$->host= host_not_specified;
+            $$->host= empty_lex_str;
             $$->password= null_lex_str; 
             $$->plugin= empty_lex_str;
             $$->auth= empty_lex_str;
@@ -15044,9 +15044,9 @@ no_definer:
         ;
 
 definer:
-          DEFINER_SYM EQ user
+          DEFINER_SYM EQ user_or_role
           {
-            thd->lex->definer= get_current_user(thd, $3);
+            thd->lex->definer= $3;
           }
         ;
 
diff --git a/sql/structs.h b/sql/structs.h
index a3a54c524e6..f4b85433e3a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -190,6 +190,14 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
 
 typedef struct	st_lex_user {
   LEX_STRING user, host, password, plugin, auth;
+  bool is_role() { return user.str[0] && !host.str[0]; }
+  void set_lex_string(LEX_STRING *l, char *buf)
+  {
+    if (is_role())
+      *l= user;
+    else
+      l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf;
+  }
 } LEX_USER;
 
 /*