summaryrefslogtreecommitdiffstats
path: root/mysql-test/suite/roles
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-test/suite/roles')
-rw-r--r--mysql-test/suite/roles/acl_load_mutex-5170.result9
-rw-r--r--mysql-test/suite/roles/acl_load_mutex-5170.test25
-rw-r--r--mysql-test/suite/roles/acl_statistics.opt1
-rw-r--r--mysql-test/suite/roles/acl_statistics.result110
-rw-r--r--mysql-test/suite/roles/acl_statistics.test66
-rw-r--r--mysql-test/suite/roles/admin.result155
-rw-r--r--mysql-test/suite/roles/admin.test102
-rw-r--r--mysql-test/suite/roles/create_and_drop_current.result34
-rw-r--r--mysql-test/suite/roles/create_and_drop_current.test51
-rw-r--r--mysql-test/suite/roles/create_and_drop_role.result89
-rw-r--r--mysql-test/suite/roles/create_and_drop_role.test109
-rw-r--r--mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result30
-rw-r--r--mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test51
-rw-r--r--mysql-test/suite/roles/create_and_grant_role.result26
-rw-r--r--mysql-test/suite/roles/create_and_grant_role.test21
-rw-r--r--mysql-test/suite/roles/current_role_view-12666.result103
-rw-r--r--mysql-test/suite/roles/current_role_view-12666.test102
-rw-r--r--mysql-test/suite/roles/default_create_user_not_role.result8
-rw-r--r--mysql-test/suite/roles/default_create_user_not_role.test11
-rw-r--r--mysql-test/suite/roles/definer.result754
-rw-r--r--mysql-test/suite/roles/definer.test466
-rw-r--r--mysql-test/suite/roles/drop_current_role.result5
-rw-r--r--mysql-test/suite/roles/drop_current_role.test9
-rw-r--r--mysql-test/suite/roles/drop_current_user-5176.result11
-rw-r--r--mysql-test/suite/roles/drop_current_user-5176.test14
-rw-r--r--mysql-test/suite/roles/drop_routines.result81
-rw-r--r--mysql-test/suite/roles/drop_routines.test69
-rw-r--r--mysql-test/suite/roles/flush_roles-12366.result539
-rw-r--r--mysql-test/suite/roles/flush_roles-12366.test379
-rw-r--r--mysql-test/suite/roles/flush_roles-17898.result36
-rw-r--r--mysql-test/suite/roles/flush_roles-17898.test37
-rw-r--r--mysql-test/suite/roles/grant-5771.result36
-rw-r--r--mysql-test/suite/roles/grant-5771.test32
-rw-r--r--mysql-test/suite/roles/grant_empty.result16
-rw-r--r--mysql-test/suite/roles/grant_empty.test23
-rw-r--r--mysql-test/suite/roles/grant_proxy-5526.result9
-rw-r--r--mysql-test/suite/roles/grant_proxy-5526.test11
-rw-r--r--mysql-test/suite/roles/grant_revoke_current.result44
-rw-r--r--mysql-test/suite/roles/grant_revoke_current.test32
-rw-r--r--mysql-test/suite/roles/grant_role_auto_create_user.result83
-rw-r--r--mysql-test/suite/roles/grant_role_auto_create_user.test123
-rw-r--r--mysql-test/suite/roles/i_s_applicable_roles_is_default.result81
-rw-r--r--mysql-test/suite/roles/i_s_applicable_roles_is_default.test62
-rw-r--r--mysql-test/suite/roles/ip-6401.result15
-rw-r--r--mysql-test/suite/roles/ip-6401.test16
-rw-r--r--mysql-test/suite/roles/none_public.result72
-rw-r--r--mysql-test/suite/roles/none_public.test75
-rw-r--r--mysql-test/suite/roles/password.result35
-rw-r--r--mysql-test/suite/roles/password.test53
-rw-r--r--mysql-test/suite/roles/prepare_stmt_with_role.result107
-rw-r--r--mysql-test/suite/roles/prepare_stmt_with_role.test85
-rw-r--r--mysql-test/suite/roles/ps.result1
-rw-r--r--mysql-test/suite/roles/ps.test4
-rw-r--r--mysql-test/suite/roles/rebuild_role_grants.result333
-rw-r--r--mysql-test/suite/roles/rebuild_role_grants.test100
-rw-r--r--mysql-test/suite/roles/recursive.inc259
-rw-r--r--mysql-test/suite/roles/recursive.result366
-rw-r--r--mysql-test/suite/roles/recursive.test4
-rw-r--r--mysql-test/suite/roles/recursive_dbug.result486
-rw-r--r--mysql-test/suite/roles/recursive_dbug.test14
-rw-r--r--mysql-test/suite/roles/rename_user.result36
-rw-r--r--mysql-test/suite/roles/rename_user.test48
-rw-r--r--mysql-test/suite/roles/revoke_all.result183
-rw-r--r--mysql-test/suite/roles/revoke_all.test103
-rw-r--r--mysql-test/suite/roles/role_case_sensitive-10744.result60
-rw-r--r--mysql-test/suite/roles/role_case_sensitive-10744.test54
-rw-r--r--mysql-test/suite/roles/role_grant_propagate.result180
-rw-r--r--mysql-test/suite/roles/role_grant_propagate.test212
-rw-r--r--mysql-test/suite/roles/roles_tables_priv-29465.result38
-rw-r--r--mysql-test/suite/roles/roles_tables_priv-29465.test40
-rw-r--r--mysql-test/suite/roles/rpl_definer.result73
-rw-r--r--mysql-test/suite/roles/rpl_definer.test46
-rw-r--r--mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.result8
-rw-r--r--mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.test12
-rw-r--r--mysql-test/suite/roles/set_and_drop.result128
-rw-r--r--mysql-test/suite/roles/set_and_drop.test113
-rw-r--r--mysql-test/suite/roles/set_default_role_clear.result36
-rw-r--r--mysql-test/suite/roles/set_default_role_clear.test52
-rw-r--r--mysql-test/suite/roles/set_default_role_for.result60
-rw-r--r--mysql-test/suite/roles/set_default_role_for.test84
-rw-r--r--mysql-test/suite/roles/set_default_role_invalid.result130
-rw-r--r--mysql-test/suite/roles/set_default_role_invalid.test169
-rw-r--r--mysql-test/suite/roles/set_default_role_new_connection.result62
-rw-r--r--mysql-test/suite/roles/set_default_role_new_connection.test47
-rw-r--r--mysql-test/suite/roles/set_default_role_ps-6960.result10
-rw-r--r--mysql-test/suite/roles/set_default_role_ps-6960.test20
-rw-r--r--mysql-test/suite/roles/set_role-13655.result52
-rw-r--r--mysql-test/suite/roles/set_role-13655.test49
-rw-r--r--mysql-test/suite/roles/set_role-5232.result18
-rw-r--r--mysql-test/suite/roles/set_role-5232.test20
-rw-r--r--mysql-test/suite/roles/set_role-9614.result96
-rw-r--r--mysql-test/suite/roles/set_role-9614.test79
-rw-r--r--mysql-test/suite/roles/set_role-database-recursive.result82
-rw-r--r--mysql-test/suite/roles/set_role-database-recursive.test64
-rw-r--r--mysql-test/suite/roles/set_role-database-simple.result53
-rw-r--r--mysql-test/suite/roles/set_role-database-simple.test56
-rw-r--r--mysql-test/suite/roles/set_role-multiple-role.result147
-rw-r--r--mysql-test/suite/roles/set_role-multiple-role.test102
-rw-r--r--mysql-test/suite/roles/set_role-recursive.result119
-rw-r--r--mysql-test/suite/roles/set_role-recursive.test79
-rw-r--r--mysql-test/suite/roles/set_role-routine-simple.result104
-rw-r--r--mysql-test/suite/roles/set_role-routine-simple.test81
-rw-r--r--mysql-test/suite/roles/set_role-simple.result59
-rw-r--r--mysql-test/suite/roles/set_role-simple.test54
-rw-r--r--mysql-test/suite/roles/set_role-table-column-priv.result71
-rw-r--r--mysql-test/suite/roles/set_role-table-column-priv.test56
-rw-r--r--mysql-test/suite/roles/set_role-table-simple.result69
-rw-r--r--mysql-test/suite/roles/set_role-table-simple.test53
-rw-r--r--mysql-test/suite/roles/show_create_database-10463.result65
-rw-r--r--mysql-test/suite/roles/show_create_database-10463.test56
-rw-r--r--mysql-test/suite/roles/show_grants.result162
-rw-r--r--mysql-test/suite/roles/show_grants.test103
-rw-r--r--mysql-test/suite/roles/show_grants_replicated.result58
-rw-r--r--mysql-test/suite/roles/show_grants_replicated.test38
114 files changed, 10129 insertions, 0 deletions
diff --git a/mysql-test/suite/roles/acl_load_mutex-5170.result b/mysql-test/suite/roles/acl_load_mutex-5170.result
new file mode 100644
index 00000000..775541b9
--- /dev/null
+++ b/mysql-test/suite/roles/acl_load_mutex-5170.result
@@ -0,0 +1,9 @@
+create user user1@localhost;
+create role r1 with admin user1@localhost;
+grant all on test.* to r1;
+flush tables;
+select 1;
+1
+1
+drop role r1;
+drop user user1@localhost;
diff --git a/mysql-test/suite/roles/acl_load_mutex-5170.test b/mysql-test/suite/roles/acl_load_mutex-5170.test
new file mode 100644
index 00000000..76d817be
--- /dev/null
+++ b/mysql-test/suite/roles/acl_load_mutex-5170.test
@@ -0,0 +1,25 @@
+--source include/not_embedded.inc
+#
+# MDEV-5170 Assertion `(&(&acl_cache->lock)->m_mutex)->count > 0 && pthread_equal(pthread_self(), (&(&acl_cache->lock)->m_mutex)->thread)' fails after restarting server with a pre-created role grants
+#
+create user user1@localhost;
+create role r1 with admin user1@localhost;
+grant all on test.* to r1;
+flush tables;
+
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+--shutdown_server
+--source include/wait_until_disconnected.inc
+--enable_reconnect
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+--source include/wait_until_connected_again.inc
+
+select 1;
+
+drop role r1;
+drop user user1@localhost;
+
diff --git a/mysql-test/suite/roles/acl_statistics.opt b/mysql-test/suite/roles/acl_statistics.opt
new file mode 100644
index 00000000..24307596
--- /dev/null
+++ b/mysql-test/suite/roles/acl_statistics.opt
@@ -0,0 +1 @@
+--disable-skip-name-resolve
diff --git a/mysql-test/suite/roles/acl_statistics.result b/mysql-test/suite/roles/acl_statistics.result
new file mode 100644
index 00000000..003bc263
--- /dev/null
+++ b/mysql-test/suite/roles/acl_statistics.result
@@ -0,0 +1,110 @@
+SHOW STATUS LIKE 'Acl%';
+Variable_name Value
+Acl_column_grants 0
+Acl_database_grants 0
+Acl_function_grants 0
+Acl_procedure_grants 0
+Acl_package_spec_grants 0
+Acl_package_body_grants 0
+Acl_proxy_users 4
+Acl_role_grants 0
+Acl_roles 0
+Acl_table_grants 1
+Acl_users 5
+SELECT count(*) COLUMN_GRANTS from mysql.columns_priv;
+COLUMN_GRANTS
+0
+SELECT count(*) DATABASE_GRANTS from mysql.db;
+DATABASE_GRANTS
+0
+SELECT count(*) FUNCTION_GRANTS from mysql.procs_priv where routine_type='FUNCTION';
+FUNCTION_GRANTS
+0
+SELECT count(*) PROCEDURE_GRANTS from mysql.procs_priv where routine_type='PROCEDURE';
+PROCEDURE_GRANTS
+0
+SELECT count(*) PROXY_USERS from mysql.proxies_priv;
+PROXY_USERS
+4
+SELECT count(*) ROLE_GRANTS from mysql.roles_mapping;
+ROLE_GRANTS
+0
+SELECT count(*) ROLES from mysql.user where is_role='Y';
+ROLES
+0
+SELECT count(*) TABLE_GRANTS from mysql.tables_priv;
+TABLE_GRANTS
+1
+SELECT count(*) USERS from mysql.user where is_role='N';
+USERS
+5
+CREATE USER u1;
+CREATE ROLE r1;
+CREATE ROLE r2;
+GRANT PROXY ON root TO u1;
+GRANT SELECT ON *.* to u1;
+GRANT SELECT ON *.* to r1;
+GRANT DELETE ON mysql.* to u1;
+GRANT DELETE ON mysql.* to r1;
+GRANT INSERT ON mysql.user to u1;
+GRANT INSERT ON mysql.user to r1;
+GRANT UPDATE (host) ON mysql.user to u1;
+GRANT UPDATE (host) ON mysql.user to r1;
+GRANT r1 to u1;
+GRANT r2 to r1;
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+GRANT EXECUTE ON PROCEDURE mysql.test_proc TO r1;
+GRANT EXECUTE ON PROCEDURE mysql.test_proc TO u1;
+CREATE FUNCTION mysql.test_func (param INT) RETURNS INT
+RETURN (SELECT COUNT(*) FROM mysql.user);
+GRANT EXECUTE ON FUNCTION mysql.test_func TO r1;
+GRANT EXECUTE ON FUNCTION mysql.test_func TO u1;
+GRANT EXECUTE ON FUNCTION mysql.test_func TO r2;
+SHOW STATUS LIKE 'Acl%';
+Variable_name Value
+Acl_column_grants 2
+Acl_database_grants 2
+Acl_function_grants 3
+Acl_procedure_grants 2
+Acl_package_spec_grants 0
+Acl_package_body_grants 0
+Acl_proxy_users 5
+Acl_role_grants 4
+Acl_roles 2
+Acl_table_grants 3
+Acl_users 6
+SELECT count(*) COLUMN_GRANTS from mysql.columns_priv;
+COLUMN_GRANTS
+2
+SELECT count(*) DATABASE_GRANTS from mysql.db;
+DATABASE_GRANTS
+2
+SELECT count(*) FUNCTION_GRANTS from mysql.procs_priv where routine_type='FUNCTION';
+FUNCTION_GRANTS
+3
+SELECT count(*) PROCEDURE_GRANTS from mysql.procs_priv where routine_type='PROCEDURE';
+PROCEDURE_GRANTS
+2
+SELECT count(*) PROXY_USERS from mysql.proxies_priv;
+PROXY_USERS
+5
+SELECT count(*) ROLE_GRANTS from mysql.roles_mapping;
+ROLE_GRANTS
+4
+SELECT count(*) ROLES from mysql.user where is_role='Y';
+ROLES
+2
+SELECT count(*) TABLE_GRANTS from mysql.tables_priv;
+TABLE_GRANTS
+3
+SELECT count(*) USERS from mysql.user where is_role='N';
+USERS
+6
+DROP PROCEDURE mysql.test_proc;
+DROP FUNCTION mysql.test_func;
+DROP ROLE r2;
+DROP ROLE r1;
+DROP USER u1;
diff --git a/mysql-test/suite/roles/acl_statistics.test b/mysql-test/suite/roles/acl_statistics.test
new file mode 100644
index 00000000..c76d3760
--- /dev/null
+++ b/mysql-test/suite/roles/acl_statistics.test
@@ -0,0 +1,66 @@
+# Test case for validating acl statistics for the feedback plugin.
+--source include/not_embedded.inc
+
+# First get a baseline of the initial statistics.
+SHOW STATUS LIKE 'Acl%';
+SELECT count(*) COLUMN_GRANTS from mysql.columns_priv;
+SELECT count(*) DATABASE_GRANTS from mysql.db;
+SELECT count(*) FUNCTION_GRANTS from mysql.procs_priv where routine_type='FUNCTION';
+SELECT count(*) PROCEDURE_GRANTS from mysql.procs_priv where routine_type='PROCEDURE';
+SELECT count(*) PROXY_USERS from mysql.proxies_priv;
+SELECT count(*) ROLE_GRANTS from mysql.roles_mapping;
+SELECT count(*) ROLES from mysql.user where is_role='Y';
+SELECT count(*) TABLE_GRANTS from mysql.tables_priv;
+SELECT count(*) USERS from mysql.user where is_role='N';
+
+# Next add some users, roles and privileges to them.
+CREATE USER u1;
+CREATE ROLE r1;
+CREATE ROLE r2;
+GRANT PROXY ON root TO u1;
+GRANT SELECT ON *.* to u1;
+GRANT SELECT ON *.* to r1;
+GRANT DELETE ON mysql.* to u1;
+GRANT DELETE ON mysql.* to r1;
+GRANT INSERT ON mysql.user to u1;
+GRANT INSERT ON mysql.user to r1;
+GRANT UPDATE (host) ON mysql.user to u1;
+GRANT UPDATE (host) ON mysql.user to r1;
+
+GRANT r1 to u1;
+GRANT r2 to r1;
+
+delimiter |;
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+ select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+delimiter ;|
+GRANT EXECUTE ON PROCEDURE mysql.test_proc TO r1;
+GRANT EXECUTE ON PROCEDURE mysql.test_proc TO u1;
+
+CREATE FUNCTION mysql.test_func (param INT) RETURNS INT
+ RETURN (SELECT COUNT(*) FROM mysql.user);
+GRANT EXECUTE ON FUNCTION mysql.test_func TO r1;
+GRANT EXECUTE ON FUNCTION mysql.test_func TO u1;
+# Extra grant to differentiate procedure from function grants.
+GRANT EXECUTE ON FUNCTION mysql.test_func TO r2;
+
+# Recheck how statistics are updated. Make sure that both the information
+# schema and the actualy physical rows are the same.
+SHOW STATUS LIKE 'Acl%';
+SELECT count(*) COLUMN_GRANTS from mysql.columns_priv;
+SELECT count(*) DATABASE_GRANTS from mysql.db;
+SELECT count(*) FUNCTION_GRANTS from mysql.procs_priv where routine_type='FUNCTION';
+SELECT count(*) PROCEDURE_GRANTS from mysql.procs_priv where routine_type='PROCEDURE';
+SELECT count(*) PROXY_USERS from mysql.proxies_priv;
+SELECT count(*) ROLE_GRANTS from mysql.roles_mapping;
+SELECT count(*) ROLES from mysql.user where is_role='Y';
+SELECT count(*) TABLE_GRANTS from mysql.tables_priv;
+SELECT count(*) USERS from mysql.user where is_role='N';
+
+DROP PROCEDURE mysql.test_proc;
+DROP FUNCTION mysql.test_func;
+DROP ROLE r2;
+DROP ROLE r1;
+DROP USER u1;
diff --git a/mysql-test/suite/roles/admin.result b/mysql-test/suite/roles/admin.result
new file mode 100644
index 00000000..2ecbfae4
--- /dev/null
+++ b/mysql-test/suite/roles/admin.result
@@ -0,0 +1,155 @@
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+create role role1;
+create role role2 with admin current_user;
+create role role3 with admin current_role;
+ERROR 0L000: Invalid definer
+create role role3 with admin role1;
+create role role4 with admin root@localhost;
+connect c1, localhost, foo,,;
+create role role5 with admin root@localhost;
+ERROR 42000: Access denied; you need (at least one of) the SUPER, SET USER privilege(s) for this operation
+create role role5 with admin role3;
+ERROR 42000: Access denied; you need (at least one of) the SUPER, SET USER privilege(s) for this operation
+create role role5 with admin foo@localhost;
+connection default;
+call mtr.add_suppression("Invalid roles_mapping table entry user:'foo@bar', rolename:'role6'");
+create role role6 with admin foo@bar;
+Warnings:
+Note 1449 The user specified as a definer ('foo'@'bar') does not exist
+create user bar with admin current_user;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'admin current_user' at line 1
+grant role1 to foo@localhost with admin option;
+grant role2 to foo@localhost;
+grant role2 to role1;
+grant role4 to role3 with admin option;
+grant select on *.* to foo@localhost with admin option;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'admin option' at line 1
+show grants for foo@localhost;
+Grants for foo@localhost
+GRANT CREATE USER ON *.* TO `foo`@`localhost`
+GRANT `role1` TO `foo`@`localhost` WITH ADMIN OPTION
+GRANT `role2` TO `foo`@`localhost`
+GRANT `role5` TO `foo`@`localhost` WITH ADMIN OPTION
+show grants for role1;
+Grants for role1
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT `role2` TO `role1`
+GRANT `role3` TO `role1` WITH ADMIN OPTION
+GRANT `role4` TO `role3` WITH ADMIN OPTION
+show grants for role4;
+Grants for role4
+GRANT USAGE ON *.* TO `role4`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ role1 role2 N
+ role1 role3 Y
+ role3 role4 Y
+bar foo role6 Y
+localhost foo role1 Y
+localhost foo role2 N
+localhost foo role5 Y
+localhost root role1 Y
+localhost root role2 Y
+localhost root role4 Y
+flush privileges;
+show grants for foo@localhost;
+Grants for foo@localhost
+GRANT CREATE USER ON *.* TO `foo`@`localhost`
+GRANT `role1` TO `foo`@`localhost` WITH ADMIN OPTION
+GRANT `role2` TO `foo`@`localhost`
+GRANT `role5` TO `foo`@`localhost` WITH ADMIN OPTION
+show grants for role1;
+Grants for role1
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT `role2` TO `role1`
+GRANT `role3` TO `role1` WITH ADMIN OPTION
+GRANT `role4` TO `role3` WITH ADMIN OPTION
+show grants for role4;
+Grants for role4
+GRANT USAGE ON *.* TO `role4`
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+role1 role3 YES NULL
+role3 role4 YES NULL
+root@localhost role1 YES NO
+root@localhost role2 YES NO
+root@localhost role4 YES NO
+grant role2 to role1 with admin option;
+revoke role1 from foo@localhost;
+revoke admin option for role4 from role3;
+revoke admin option for role2 from foo@localhost;
+revoke admin option for role1 from root@localhost;
+show grants for foo@localhost;
+Grants for foo@localhost
+GRANT CREATE USER ON *.* TO `foo`@`localhost`
+GRANT `role2` TO `foo`@`localhost`
+GRANT `role5` TO `foo`@`localhost` WITH ADMIN OPTION
+show grants for role1;
+Grants for role1
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT `role2` TO `role1` WITH ADMIN OPTION
+GRANT `role3` TO `role1` WITH ADMIN OPTION
+GRANT `role4` TO `role3`
+show grants for role4;
+Grants for role4
+GRANT USAGE ON *.* TO `role4`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ role1 role2 Y
+ role1 role3 Y
+ role3 role4 N
+bar foo role6 Y
+localhost foo role2 N
+localhost foo role5 Y
+localhost root role1 N
+localhost root role2 Y
+localhost root role4 Y
+flush privileges;
+show grants for foo@localhost;
+Grants for foo@localhost
+GRANT CREATE USER ON *.* TO `foo`@`localhost`
+GRANT `role2` TO `foo`@`localhost`
+GRANT `role5` TO `foo`@`localhost` WITH ADMIN OPTION
+show grants for role1;
+Grants for role1
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT `role2` TO `role1` WITH ADMIN OPTION
+GRANT `role3` TO `role1` WITH ADMIN OPTION
+GRANT `role4` TO `role3`
+show grants for role4;
+Grants for role4
+GRANT USAGE ON *.* TO `role4`
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 YES NULL
+role1 role3 YES NULL
+role3 role4 NO NULL
+root@localhost role1 NO NO
+root@localhost role2 YES NO
+root@localhost role4 YES NO
+grant role1 to role4;
+ERROR 28000: Access denied for user 'root'@'localhost'
+grant role1 to role4 with admin option;
+ERROR 28000: Access denied for user 'root'@'localhost'
+grant role3 to role2;
+revoke role3 from role2;
+grant role4 to role2 with admin option;
+revoke role2 from current_user;
+revoke role4 from current_user;
+grant role4 to current_user;
+drop role role1, role2, role3, role4, role5, role6;
+drop user foo@localhost;
diff --git a/mysql-test/suite/roles/admin.test b/mysql-test/suite/roles/admin.test
new file mode 100644
index 00000000..242518eb
--- /dev/null
+++ b/mysql-test/suite/roles/admin.test
@@ -0,0 +1,102 @@
+source include/not_embedded.inc;
+
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+
+########################################
+# syntax tests
+########################################
+
+create role role1;
+create role role2 with admin current_user;
+--error ER_MALFORMED_DEFINER
+create role role3 with admin current_role;
+create role role3 with admin role1;
+create role role4 with admin root@localhost;
+
+# privilege checks, one needs SUPER to specify an arbitrary admin
+connect (c1, localhost, foo,,);
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+create role role5 with admin root@localhost;
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+create role role5 with admin role3;
+create role role5 with admin foo@localhost;
+
+connection default;
+# non-existing admin. works. warning. error in the log on acl_load.
+call mtr.add_suppression("Invalid roles_mapping table entry user:'foo@bar', rolename:'role6'");
+create role role6 with admin foo@bar;
+
+--error ER_PARSE_ERROR
+create user bar with admin current_user;
+
+grant role1 to foo@localhost with admin option;
+grant role2 to foo@localhost;
+grant role2 to role1;
+grant role4 to role3 with admin option;
+--error ER_PARSE_ERROR
+grant select on *.* to foo@localhost with admin option;
+
+--sorted_result
+show grants for foo@localhost;
+--sorted_result
+show grants for role1;
+--sorted_result
+show grants for role4;
+--sorted_result
+select * from mysql.roles_mapping;
+flush privileges;
+--sorted_result
+show grants for foo@localhost;
+--sorted_result
+show grants for role1;
+--sorted_result
+show grants for role4;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+grant role2 to role1 with admin option;
+revoke role1 from foo@localhost;
+revoke admin option for role4 from role3;
+revoke admin option for role2 from foo@localhost;
+revoke admin option for role1 from root@localhost;
+
+--sorted_result
+show grants for foo@localhost;
+--sorted_result
+show grants for role1;
+--sorted_result
+show grants for role4;
+--sorted_result
+select * from mysql.roles_mapping;
+flush privileges;
+--sorted_result
+show grants for foo@localhost;
+--sorted_result
+show grants for role1;
+--sorted_result
+show grants for role4;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+# Now, root@localhost don't have admin option for role1:
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant role1 to role4;
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant role1 to role4 with admin option;
+# but role3 is grantable
+grant role3 to role2;
+revoke role3 from role2;
+
+# now, a diamond
+grant role4 to role2 with admin option;
+revoke role2 from current_user;
+revoke role4 from current_user;
+grant role4 to current_user;
+
+
+########################################
+# cleanup
+########################################
+drop role role1, role2, role3, role4, role5, role6;
+drop user foo@localhost;
diff --git a/mysql-test/suite/roles/create_and_drop_current.result b/mysql-test/suite/roles/create_and_drop_current.result
new file mode 100644
index 00000000..7e847677
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_current.result
@@ -0,0 +1,34 @@
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+create user current_user;
+ERROR HY000: Operation CREATE USER failed for CURRENT_USER
+create user current_role;
+ERROR HY000: Operation CREATE USER failed for CURRENT_ROLE
+create role current_user;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'current_user' at line 1
+create role current_role;
+ERROR HY000: Operation CREATE ROLE failed for CURRENT_ROLE
+drop user current_user;
+drop user current_role;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'current_role' at line 1
+drop role current_user;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'current_user' at line 1
+drop role current_role;
+ERROR HY000: Operation DROP ROLE failed for CURRENT_ROLE
+show warnings;
+Level Code Message
+Error 1446 Invalid definer
+Error 1396 Operation DROP ROLE failed for CURRENT_ROLE
+create role r1;
+grant r1 to current_user;
+set role r1;
+select current_role();
+current_role()
+r1
+create user current_role;
+ERROR HY000: Operation CREATE USER failed for CURRENT_ROLE
+create role current_role;
+ERROR HY000: Operation CREATE ROLE failed for CURRENT_ROLE
+drop user current_role;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'current_role' at line 1
+drop role current_role;
diff --git a/mysql-test/suite/roles/create_and_drop_current.test b/mysql-test/suite/roles/create_and_drop_current.test
new file mode 100644
index 00000000..7ca8161a
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_current.test
@@ -0,0 +1,51 @@
+#
+# MDEV-5225 Server crashes on CREATE USER|ROLE CURRENT_ROLE or DROP ROLE CURRENT_ROLE
+#
+
+# Where CURRENT_USER/CURRENT_ROLE is explicitly allowed by the grammar
+# the error (if any) should be ER_CANNOT_USER
+#
+# Where it's not explicitly allowed, the error is ER_PARSE_ERROR,
+# because CURRENT_USER/CURRENT_ROLE are reserved words and cannot be
+# accepted as an identifier.
+#
+
+--source include/not_embedded.inc
+
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+--change_user foo
+
+--error ER_CANNOT_USER
+create user current_user;
+--error ER_CANNOT_USER
+create user current_role;
+--error ER_PARSE_ERROR
+create role current_user;
+--error ER_CANNOT_USER
+create role current_role;
+# this works
+drop user current_user;
+--error ER_PARSE_ERROR
+drop user current_role;
+--error ER_PARSE_ERROR
+drop role current_user;
+--error ER_CANNOT_USER
+drop role current_role;
+show warnings;
+
+--change_user root
+
+create role r1;
+grant r1 to current_user;
+set role r1;
+select current_role();
+
+--error ER_CANNOT_USER
+create user current_role;
+--error ER_CANNOT_USER
+create role current_role;
+--error ER_PARSE_ERROR
+drop user current_role;
+drop role current_role;
+
diff --git a/mysql-test/suite/roles/create_and_drop_role.result b/mysql-test/suite/roles/create_and_drop_role.result
new file mode 100644
index 00000000..90bf1506
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role.result
@@ -0,0 +1,89 @@
+connect mysql, localhost, root,,;
+use mysql;
+create role test_role1@host1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@host1' at line 1
+create role test_role2@host2, test_role1@host1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@host2, test_role1@host1' at line 1
+create role test_role1;
+create role test_role2, test_role3;
+select user, host, is_role from user where user like 'test%';
+User Host is_role
+test_role1 Y
+test_role2 Y
+test_role3 Y
+drop role test_role1;
+drop role test_role2, test_role3;
+create role test_role1;
+create role test_role1;
+ERROR HY000: Operation CREATE ROLE failed for 'test_role1'
+create role test_role1, test_role2;
+ERROR HY000: Operation CREATE ROLE failed for 'test_role1'
+select user, host, is_role from user where user like 'test%';
+User Host is_role
+test_role1 Y
+test_role2 Y
+drop role test_role1;
+drop role test_role1;
+ERROR HY000: Operation DROP ROLE failed for 'test_role1'
+drop role test_role1, test_role2;
+ERROR HY000: Operation DROP ROLE failed for 'test_role1'
+drop role root;
+ERROR HY000: Operation DROP ROLE failed for 'root'
+create user dummy@'';
+drop role dummy;
+ERROR HY000: Operation DROP ROLE failed for 'dummy'
+drop user dummy@'';
+select user, host, is_role from user where user like 'test%';
+User Host is_role
+disconnect mysql;
+connection default;
+create role '';
+ERROR OP000: Invalid role specification ``
+create role ' ';
+ERROR OP000: Invalid role specification ``
+create role 'foo ';
+drop role foo;
+create role r1;
+drop user r1;
+ERROR HY000: Operation DROP USER failed for 'r1'@'%'
+drop role r1;
+create role r1 with admin u1;
+Warnings:
+Note 1449 The user specified as a definer ('u1'@'%') does not exist
+create user foo@bar;
+drop user foo@bar;
+drop role r1;
+CREATE USER u1;
+CREATE ROLE r1;
+CREATE USER r1@localhost;
+CREATE ROLE r2;
+GRANT r2 to r1;
+GRANT r2 to r1@localhost;
+DROP ROLE r1;
+SELECT * FROM mysql.roles_mapping;
+Host User Role Admin_option
+localhost r1 r2 N
+localhost root r2 Y
+SHOW GRANTS FOR r1@localhost;
+Grants for r1@localhost
+GRANT `r2` TO `r1`@`localhost`
+GRANT USAGE ON *.* TO `r1`@`localhost`
+DROP USER u1;
+DROP ROLE r2;
+DROP USER r1@localhost;
+create role 'foo ';
+select concat(user, '__'), is_role from mysql.user where user like 'foo%';
+concat(user, '__') is_role
+foo__ Y
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root foo Y
+drop role foo;
+select concat(user, '__'), is_role from mysql.user where user like 'foo%';
+concat(user, '__') is_role
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+show grants;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
diff --git a/mysql-test/suite/roles/create_and_drop_role.test b/mysql-test/suite/roles/create_and_drop_role.test
new file mode 100644
index 00000000..b6e5bd2b
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role.test
@@ -0,0 +1,109 @@
+source include/not_embedded.inc;
+
+connect (mysql, localhost, root,,);
+use mysql;
+
+#test valid syntax
+--error ER_PARSE_ERROR
+create role test_role1@host1;
+--error ER_PARSE_ERROR
+create role test_role2@host2, test_role1@host1;
+
+create role test_role1;
+create role test_role2, test_role3;
+
+--sorted_result
+select user, host, is_role from user where user like 'test%';
+
+drop role test_role1;
+drop role test_role2, test_role3;
+
+create role test_role1;
+--error ER_CANNOT_USER
+create role test_role1;
+--error ER_CANNOT_USER
+create role test_role1, test_role2;
+
+--sorted_result
+select user, host, is_role from user where user like 'test%';
+
+drop role test_role1;
+--error ER_CANNOT_USER
+drop role test_role1;
+--error ER_CANNOT_USER
+drop role test_role1, test_role2;
+
+#test that we can not drop users when calling drop role
+--error ER_CANNOT_USER
+drop role root;
+create user dummy@'';
+--error ER_CANNOT_USER
+drop role dummy;
+drop user dummy@'';
+
+--sorted_result
+select user, host, is_role from user where user like 'test%';
+disconnect mysql;
+connection default;
+
+#
+# MDEV-5520 Connection lost on wrong CREATE ROLE
+#
+--error ER_INVALID_ROLE
+create role '';
+
+#
+# MDEV-8609 Server crashes in is_invalid_role_name on reloading ACL with a blank role name
+#
+--error ER_INVALID_ROLE
+create role ' ';
+create role 'foo ';
+drop role foo;
+
+#
+# MDEV-5523 Server crashes on DROP USER <rolename>
+#
+create role r1;
+--error ER_CANNOT_USER
+drop user r1;
+drop role r1;
+
+#
+# MDEV-5525 Assertion `status == 0' fails on creating user after granting it role admin option
+#
+create role r1 with admin u1;
+create user foo@bar;
+drop user foo@bar;
+drop role r1;
+
+#
+# MDEV-7774 Assertion `status == 0' fails when dropping in this order:
+#
+CREATE USER u1;
+CREATE ROLE r1;
+CREATE USER r1@localhost;
+CREATE ROLE r2;
+GRANT r2 to r1;
+GRANT r2 to r1@localhost;
+# MDEV-7774: Dropping in this order caused the crash.
+DROP ROLE r1;
+--sorted_result
+SELECT * FROM mysql.roles_mapping;
+SHOW GRANTS FOR r1@localhost; # Related to MDEV-7774, also caused a crash, by
+ # not updating the internal acl_roles_mapping
+ # data structure correctly;
+DROP USER u1;
+DROP ROLE r2;
+DROP USER r1@localhost;
+
+#
+# MDEV-11533: Roles with trailing white spaces are not cleared correctly
+#
+create role 'foo ';
+select concat(user, '__'), is_role from mysql.user where user like 'foo%';
+select * from mysql.roles_mapping;
+drop role foo;
+select concat(user, '__'), is_role from mysql.user where user like 'foo%';
+select * from mysql.roles_mapping;
+--sorted_result
+show grants;
diff --git a/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result
new file mode 100644
index 00000000..dfad83e4
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result
@@ -0,0 +1,30 @@
+# switching from mysql.global_priv to mysql.user
+connect mysql, localhost, root,,;
+use mysql;
+alter table user drop column is_role;
+alter table user drop column default_role;
+alter table user drop column max_statement_time;
+flush privileges;
+create role test_role;
+ERROR HY000: Column count of mysql.user is wrong. Expected 45, found 44. Created with MariaDB MYSQL_VERSION_ID, now running MYSQL_VERSION_ID. Please use mariadb-upgrade to fix this error
+drop role test_role;
+ERROR HY000: Operation DROP ROLE failed for 'test_role'
+alter table user add column is_role enum('N', 'Y') default 'N' not null
+COLLATE utf8_general_ci
+after password_expired;
+create role test_role;
+create user test_user@localhost;
+grant test_role to test_user@localhost;
+set default role test_role for root@localhost;
+drop role test_role;
+drop user test_user@localhost;
+alter table user add column default_role char(80) default '' not null
+COLLATE utf8_general_ci
+after is_role;
+alter table user add max_statement_time decimal(12,6) default 0 not null
+after default_role;
+update user set is_role='N';
+flush privileges;
+create role test_role;
+drop role test_role;
+# switching back from mysql.user to mysql.global_priv
diff --git a/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test
new file mode 100644
index 00000000..dac6eab2
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test
@@ -0,0 +1,51 @@
+#
+# Test that SET DEFAULT ROLE doesn't work on old privilege tables
+# that don't have 'default_role' column
+#
+source include/not_embedded.inc;
+source include/switch_to_mysql_user.inc;
+
+connect (mysql, localhost, root,,);
+use mysql;
+
+#
+# downgrade the table to pre-default-role structure
+#
+alter table user drop column is_role;
+alter table user drop column default_role;
+alter table user drop column max_statement_time;
+
+flush privileges;
+
+--replace_regex /10\d\d\d\d/MYSQL_VERSION_ID/
+--error ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
+create role test_role;
+--error ER_CANNOT_USER
+drop role test_role;
+alter table user add column is_role enum('N', 'Y') default 'N' not null
+ COLLATE utf8_general_ci
+after password_expired;
+
+# Test default role column
+create role test_role;
+create user test_user@localhost;
+grant test_role to test_user@localhost;
+#--replace_regex /10\d\d\d\d/MYSQL_VERSION_ID/
+#--error ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
+set default role test_role for root@localhost;
+drop role test_role;
+drop user test_user@localhost;
+
+alter table user add column default_role char(80) default '' not null
+ COLLATE utf8_general_ci
+after is_role;
+alter table user add max_statement_time decimal(12,6) default 0 not null
+after default_role;
+
+update user set is_role='N';
+
+flush privileges;
+create role test_role;
+drop role test_role;
+
+source include/switch_to_mysql_global_priv.inc;
diff --git a/mysql-test/suite/roles/create_and_grant_role.result b/mysql-test/suite/roles/create_and_grant_role.result
new file mode 100644
index 00000000..2a676115
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_grant_role.result
@@ -0,0 +1,26 @@
+create role r1;
+grant r1 to root@localhost;
+create user u1;
+set role r1;
+grant r1 to u1;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+% u1 r1 N
+localhost root r1 Y
+drop user u1;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root r1 Y
+show grants;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `r1`
+GRANT `r1` TO `root`@`localhost` WITH ADMIN OPTION
+drop role r1;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+show grants;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
diff --git a/mysql-test/suite/roles/create_and_grant_role.test b/mysql-test/suite/roles/create_and_grant_role.test
new file mode 100644
index 00000000..5b321503
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_grant_role.test
@@ -0,0 +1,21 @@
+source include/not_embedded.inc;
+
+create role r1;
+grant r1 to root@localhost;
+create user u1;
+set role r1;
+
+grant r1 to u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+drop user u1;
+--sorted_result
+select * from mysql.roles_mapping;
+--sorted_result
+show grants;
+drop role r1;
+--sorted_result
+select * from mysql.roles_mapping;
+--sorted_result
+show grants;
diff --git a/mysql-test/suite/roles/current_role_view-12666.result b/mysql-test/suite/roles/current_role_view-12666.result
new file mode 100644
index 00000000..1d7a8b05
--- /dev/null
+++ b/mysql-test/suite/roles/current_role_view-12666.result
@@ -0,0 +1,103 @@
+CREATE USER has_role@'localhost';
+GRANT ALL PRIVILEGES ON *.* TO has_role@'localhost';
+CREATE ROLE test_role;
+GRANT test_role TO has_role@'localhost';
+CREATE USER no_role@'localhost';
+GRANT ALL PRIVILEGES ON *.* TO no_role@'localhost';
+CREATE TABLE view_role_test (
+id int primary key,
+role_name varchar(50)
+);
+INSERT INTO view_role_test VALUES (1, 'test_role');
+#
+# Use the same logic for stored procedures.
+#
+PREPARE prepared_no_current_role FROM "SELECT * from view_role_test WHERE role_name = CURRENT_ROLE()";
+#
+# Creating a view with no CURRENT_ROLE() set and one with CURRENT_ROLE()
+# set. Both should produce the same SHOW CREATE VIEW output.
+#
+CREATE
+DEFINER = no_role@localhost
+SQL SECURITY INVOKER
+VIEW v_view_role_test_no_current_role
+AS
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+SHOW CREATE VIEW v_view_role_test_no_current_role;
+View Create View character_set_client collation_connection
+v_view_role_test_no_current_role CREATE ALGORITHM=UNDEFINED DEFINER=`no_role`@`localhost` SQL SECURITY INVOKER VIEW `v_view_role_test_no_current_role` AS select `view_role_test`.`id` AS `id`,`view_role_test`.`role_name` AS `role_name` from `view_role_test` where `view_role_test`.`role_name` = current_role() latin1 latin1_swedish_ci
+#
+# No values should be returned
+#
+EXECUTE prepared_no_current_role;
+id role_name
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+id role_name
+SELECT * FROM v_view_role_test_no_current_role;
+id role_name
+#
+# Now let's set the role. Create identical views as before. See if
+# their behaviour is different. It should not be.
+#
+SET ROLE test_role;
+SELECT CURRENT_USER();
+CURRENT_USER()
+root@localhost
+SELECT CURRENT_ROLE();
+CURRENT_ROLE()
+test_role
+#
+# Create the VIEW and prepared Statement with a CURRENT_ROLE() set.
+#
+CREATE
+DEFINER = no_role@localhost
+SQL SECURITY INVOKER
+VIEW v_view_role_test_with_current_role
+AS
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+PREPARE prepared_with_current_role FROM "SELECT * from view_role_test WHERE role_name = CURRENT_ROLE()";
+SHOW CREATE VIEW v_view_role_test_with_current_role;
+View Create View character_set_client collation_connection
+v_view_role_test_with_current_role CREATE ALGORITHM=UNDEFINED DEFINER=`no_role`@`localhost` SQL SECURITY INVOKER VIEW `v_view_role_test_with_current_role` AS select `view_role_test`.`id` AS `id`,`view_role_test`.`role_name` AS `role_name` from `view_role_test` where `view_role_test`.`role_name` = current_role() latin1 latin1_swedish_ci
+#
+# Values should be returned for all select statements as we do have
+# a CURRENT_ROLE() active;
+#
+EXECUTE prepared_no_current_role;
+id role_name
+1 test_role
+EXECUTE prepared_with_current_role;
+id role_name
+1 test_role
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+id role_name
+1 test_role
+SELECT * FROM v_view_role_test_no_current_role;
+id role_name
+1 test_role
+SELECT * FROM v_view_role_test_with_current_role;
+id role_name
+1 test_role
+SET ROLE NONE;
+#
+# No values should be returned for all select statements as we do not have
+# a CURRENT_ROLE() active;
+#
+EXECUTE prepared_no_current_role;
+id role_name
+EXECUTE prepared_with_current_role;
+id role_name
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+id role_name
+SELECT * FROM v_view_role_test_no_current_role;
+id role_name
+SELECT * FROM v_view_role_test_with_current_role;
+id role_name
+DROP USER has_role@'localhost';
+DROP USER no_role@'localhost';
+DROP ROLE test_role;
+DROP table view_role_test;
+DROP VIEW v_view_role_test_no_current_role;
+DROP VIEW v_view_role_test_with_current_role;
+DROP PREPARE prepared_no_current_role;
+DROP PREPARE prepared_with_current_role;
diff --git a/mysql-test/suite/roles/current_role_view-12666.test b/mysql-test/suite/roles/current_role_view-12666.test
new file mode 100644
index 00000000..32039ffe
--- /dev/null
+++ b/mysql-test/suite/roles/current_role_view-12666.test
@@ -0,0 +1,102 @@
+#
+# MDEV-12666 CURRENT_ROLE() does not work in a view
+#
+--source include/not_embedded.inc
+
+CREATE USER has_role@'localhost';
+GRANT ALL PRIVILEGES ON *.* TO has_role@'localhost';
+
+CREATE ROLE test_role;
+GRANT test_role TO has_role@'localhost';
+
+CREATE USER no_role@'localhost';
+GRANT ALL PRIVILEGES ON *.* TO no_role@'localhost';
+
+CREATE TABLE view_role_test (
+ id int primary key,
+ role_name varchar(50)
+ );
+
+INSERT INTO view_role_test VALUES (1, 'test_role');
+
+--echo #
+--echo # Use the same logic for stored procedures.
+--echo #
+PREPARE prepared_no_current_role FROM "SELECT * from view_role_test WHERE role_name = CURRENT_ROLE()";
+
+--echo #
+--echo # Creating a view with no CURRENT_ROLE() set and one with CURRENT_ROLE()
+--echo # set. Both should produce the same SHOW CREATE VIEW output.
+--echo #
+CREATE
+DEFINER = no_role@localhost
+SQL SECURITY INVOKER
+VIEW v_view_role_test_no_current_role
+AS
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+
+SHOW CREATE VIEW v_view_role_test_no_current_role;
+
+
+--echo #
+--echo # No values should be returned
+--echo #
+EXECUTE prepared_no_current_role;
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+SELECT * FROM v_view_role_test_no_current_role;
+
+--echo #
+--echo # Now let's set the role. Create identical views as before. See if
+--echo # their behaviour is different. It should not be.
+--echo #
+SET ROLE test_role;
+
+SELECT CURRENT_USER();
+SELECT CURRENT_ROLE();
+
+--echo #
+--echo # Create the VIEW and prepared Statement with a CURRENT_ROLE() set.
+--echo #
+CREATE
+DEFINER = no_role@localhost
+SQL SECURITY INVOKER
+VIEW v_view_role_test_with_current_role
+AS
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+
+PREPARE prepared_with_current_role FROM "SELECT * from view_role_test WHERE role_name = CURRENT_ROLE()";
+
+SHOW CREATE VIEW v_view_role_test_with_current_role;
+
+
+--echo #
+--echo # Values should be returned for all select statements as we do have
+--echo # a CURRENT_ROLE() active;
+--echo #
+EXECUTE prepared_no_current_role;
+EXECUTE prepared_with_current_role;
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+SELECT * FROM v_view_role_test_no_current_role;
+SELECT * FROM v_view_role_test_with_current_role;
+
+SET ROLE NONE;
+--echo #
+--echo # No values should be returned for all select statements as we do not have
+--echo # a CURRENT_ROLE() active;
+--echo #
+EXECUTE prepared_no_current_role;
+EXECUTE prepared_with_current_role;
+SELECT * FROM view_role_test WHERE role_name = CURRENT_ROLE();
+SELECT * FROM v_view_role_test_no_current_role;
+SELECT * FROM v_view_role_test_with_current_role;
+
+
+DROP USER has_role@'localhost';
+DROP USER no_role@'localhost';
+DROP ROLE test_role;
+
+DROP table view_role_test;
+DROP VIEW v_view_role_test_no_current_role;
+DROP VIEW v_view_role_test_with_current_role;
+DROP PREPARE prepared_no_current_role;
+DROP PREPARE prepared_with_current_role;
diff --git a/mysql-test/suite/roles/default_create_user_not_role.result b/mysql-test/suite/roles/default_create_user_not_role.result
new file mode 100644
index 00000000..3f32329b
--- /dev/null
+++ b/mysql-test/suite/roles/default_create_user_not_role.result
@@ -0,0 +1,8 @@
+connect mysql, localhost, root,,;
+use mysql;
+create user 'test'@'localhost';
+select user, host, is_role from user where user='test' and host='localhost';
+User Host is_role
+test localhost N
+drop user 'test'@'localhost';
+disconnect mysql;
diff --git a/mysql-test/suite/roles/default_create_user_not_role.test b/mysql-test/suite/roles/default_create_user_not_role.test
new file mode 100644
index 00000000..d3a43289
--- /dev/null
+++ b/mysql-test/suite/roles/default_create_user_not_role.test
@@ -0,0 +1,11 @@
+source include/not_embedded.inc;
+
+connect (mysql, localhost, root,,);
+use mysql;
+create user 'test'@'localhost';
+
+#check to see if a created user is not a role by default
+select user, host, is_role from user where user='test' and host='localhost';
+
+drop user 'test'@'localhost';
+disconnect mysql;
diff --git a/mysql-test/suite/roles/definer.result b/mysql-test/suite/roles/definer.result
new file mode 100644
index 00000000..091ba255
--- /dev/null
+++ b/mysql-test/suite/roles/definer.result
@@ -0,0 +1,754 @@
+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 select on test.* 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 select on test.* to role2;
+create user foo@localhost;
+grant create view on mysqltest1.* to foo@localhost;
+grant select, create view on test.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+grant select on test.* to role4;
+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,current_role() 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`,current_role() AS `current_role()` 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
+connect c1, localhost, foo,,mysqltest1;
+connection c1;
+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`
+GRANT SELECT, CREATE VIEW ON `test`.* 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 current_role()
+11 100 role2
+22 200 role2
+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 `mysqltest1`.`t1`
+select * from t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`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
+connection default;
+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
+create user role4;
+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
+flush tables;
+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 Created
+tr1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 ''@'' for table `mysqltest1`.`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 Created
+tr2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 Created
+tr3 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 Created
+tr2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 ''@'' for table `mysqltest1`.`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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 ''@'' 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 COLLATE latin1_swedish_ci */;
+
+USE `test`;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE VIEW `v1` AS SELECT
+ 1 AS `a+b`,
+ 1 AS `c` */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE VIEW `v2` AS SELECT
+ 1 AS `a+b`,
+ 1 AS `c`,
+ 1 AS `current_role()` */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE VIEW `v3` AS SELECT
+ 1 AS `a+b`,
+ 1 AS `c` */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE VIEW `v4` AS SELECT
+ 1 AS `a+b`,
+ 1 AS `c` */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE VIEW `v5` AS SELECT
+ 1 AS `a+b`,
+ 1 AS `c` */;
+SET character_set_client = @saved_cs_client;
+
+CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */;
+
+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 COLLATE=latin1_swedish_ci;
+/*!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 COLLATE=latin1_swedish_ci;
+/*!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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;;
+/*!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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;;
+/*!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 = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;;
+/*!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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
+/*!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 */ ;
+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 VIEW 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 VIEW 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`,current_role() AS `current_role()` 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 VIEW 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 VIEW 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 VIEW 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;
+#
+# MDEV-13676: Field "create Procedure" is NULL, even if the the user
+# has role which is the definer. (SHOW CREATE PROCEDURE)
+#
+create database rtest;
+create role r1;
+create role r2;
+create role r3;
+grant all privileges on rtest.* to r1;
+create user user1;
+grant r1 to user1;
+grant r1 to r2;
+grant r2 to user1;
+grant r3 to user1;
+connect user1, localhost,user1,,"*NO-ONE*",,,;
+set role r2;
+use rtest;
+CREATE DEFINER=current_role() PROCEDURE user1_proc() SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END;//
+set role r2;
+show create procedure user1_proc;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+user1_proc STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`r2` PROCEDURE `user1_proc`()
+ SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# Currently one can not use as definer any role except CURRENT_ROLE
+#
+CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END;//
+ERROR 42000: Access denied; you need (at least one of) the SUPER, SET USER privilege(s) for this operation
+set role r1;
+CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END;//
+show create procedure user1_proc2;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+user1_proc2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`r1` PROCEDURE `user1_proc2`()
+ SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# Test to see if the user can still see the procedure code if the
+# role that owns it is granted to him indirectly.
+#
+set role r2;
+show create procedure user1_proc2;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+user1_proc2 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`r1` PROCEDURE `user1_proc2`()
+ SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END latin1 latin1_swedish_ci latin1_swedish_ci
+#
+# One should not be able to see the procedure code if the role that owns
+# the procedure is not set by the user or is not in the subgraph of the
+# currently active role.
+#
+set role r3;
+show create procedure user1_proc2;
+ERROR 42000: PROCEDURE user1_proc2 does not exist
+connection default;
+use rtest;
+#
+# Try a few edge cases, with usernames identical to role name;
+#
+create user user_like_role;
+create user foo;
+create role user_like_role;
+grant select on rtest.* to user_like_role;
+grant select on rtest.* to foo;
+grant select on rtest.* to user_like_role@'%';
+grant user_like_role to foo;
+#
+# Here we have a procedure that is owned by user_like_role USER
+# We don't want user_like_role ROLE to have access to its code.
+#
+CREATE DEFINER=`user_like_role`@`%` PROCEDURE sensitive_proc() SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END;//
+connect user_like_role, localhost, user_like_role,,"*NO-ONE*",,,;
+use rtest;
+show create procedure sensitive_proc;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+sensitive_proc STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user_like_role`@`%` PROCEDURE `sensitive_proc`()
+ SQL SECURITY INVOKER
+BEGIN
+SELECT NOW(), VERSION();
+END latin1 latin1_swedish_ci latin1_swedish_ci
+connect foo, localhost, foo,,"*NO-ONE*",,,;
+set role user_like_role;
+use rtest;
+#
+# Foo has the set rolename identical to the procedure's definer's username.
+# Foo should not have access to this procedure.
+#
+show create procedure sensitive_proc;
+ERROR 42000: PROCEDURE sensitive_proc does not exist
+connection default;
+drop role r1;
+drop role r2;
+drop role r3;
+drop role user_like_role;
+drop user user1;
+drop user foo;
+drop user user_like_role;
+drop procedure user1_proc;
+drop procedure user1_proc2;
+drop procedure sensitive_proc;
+drop database rtest;
diff --git a/mysql-test/suite/roles/definer.test b/mysql-test/suite/roles/definer.test
new file mode 100644
index 00000000..4cd42d59
--- /dev/null
+++ b/mysql-test/suite/roles/definer.test
@@ -0,0 +1,466 @@
+# create view
+# create trigger
+# create procedure
+# create event
+# mysqldump dumping the definer
+
+--source include/not_embedded.inc
+--source include/default_charset.inc
+
+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 select on test.* 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;
+grant select on test.* to role2;
+
+# create a non-priv user and a priv role granted to him
+create user foo@localhost;
+grant create view on mysqltest1.* to foo@localhost;
+grant select, create view on test.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+grant select on test.* to role4;
+
+##################################################
+# 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,current_role() 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;
+
+create user role4;
+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
+
+flush tables;
+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);
+--replace_column 7 #
+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);
+--replace_column 7 #
+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);
+--replace_column 7 #
+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
+
+--replace_column 7 #
+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;
+
+--echo #
+--echo # MDEV-13676: Field "create Procedure" is NULL, even if the the user
+--echo # has role which is the definer. (SHOW CREATE PROCEDURE)
+--echo #
+
+create database rtest;
+create role r1;
+create role r2;
+create role r3;
+grant all privileges on rtest.* to r1;
+
+create user user1;
+grant r1 to user1;
+grant r1 to r2;
+grant r2 to user1;
+grant r3 to user1;
+
+connect (user1, localhost,user1,,"*NO-ONE*",,,);
+set role r2;
+use rtest;
+
+DELIMITER //;
+CREATE DEFINER=current_role() PROCEDURE user1_proc() SQL SECURITY INVOKER
+ BEGIN
+ SELECT NOW(), VERSION();
+ END;//
+DELIMITER ;//
+
+set role r2;
+show create procedure user1_proc;
+
+--echo #
+--echo # Currently one can not use as definer any role except CURRENT_ROLE
+--echo #
+DELIMITER //;
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
+ BEGIN
+ SELECT NOW(), VERSION();
+ END;//
+DELIMITER ;//
+
+set role r1;
+DELIMITER //;
+CREATE DEFINER='r1' PROCEDURE user1_proc2() SQL SECURITY INVOKER
+ BEGIN
+ SELECT NOW(), VERSION();
+ END;//
+DELIMITER ;//
+
+show create procedure user1_proc2;
+--echo #
+--echo # Test to see if the user can still see the procedure code if the
+--echo # role that owns it is granted to him indirectly.
+--echo #
+set role r2;
+show create procedure user1_proc2;
+
+--echo #
+--echo # One should not be able to see the procedure code if the role that owns
+--echo # the procedure is not set by the user or is not in the subgraph of the
+--echo # currently active role.
+--echo #
+set role r3;
+--error ER_SP_DOES_NOT_EXIST
+show create procedure user1_proc2;
+
+connection default;
+
+use rtest;
+
+--echo #
+--echo # Try a few edge cases, with usernames identical to role name;
+--echo #
+
+create user user_like_role;
+create user foo;
+create role user_like_role;
+grant select on rtest.* to user_like_role;
+grant select on rtest.* to foo;
+grant select on rtest.* to user_like_role@'%';
+
+grant user_like_role to foo;
+
+--echo #
+--echo # Here we have a procedure that is owned by user_like_role USER
+--echo # We don't want user_like_role ROLE to have access to its code.
+--echo #
+DELIMITER //;
+CREATE DEFINER=`user_like_role`@`%` PROCEDURE sensitive_proc() SQL SECURITY INVOKER
+ BEGIN
+ SELECT NOW(), VERSION();
+ END;//
+DELIMITER ;//
+
+connect (user_like_role, localhost, user_like_role,,"*NO-ONE*",,,);
+use rtest;
+show create procedure sensitive_proc;
+
+connect (foo, localhost, foo,,"*NO-ONE*",,,);
+set role user_like_role;
+use rtest;
+
+--echo #
+--echo # Foo has the set rolename identical to the procedure's definer's username.
+--echo # Foo should not have access to this procedure.
+--echo #
+--error ER_SP_DOES_NOT_EXIST
+show create procedure sensitive_proc;
+
+connection default;
+drop role r1;
+drop role r2;
+drop role r3;
+drop role user_like_role;
+drop user user1;
+drop user foo;
+drop user user_like_role;
+drop procedure user1_proc;
+drop procedure user1_proc2;
+drop procedure sensitive_proc;
+drop database rtest;
diff --git a/mysql-test/suite/roles/drop_current_role.result b/mysql-test/suite/roles/drop_current_role.result
new file mode 100644
index 00000000..b6de0304
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_role.result
@@ -0,0 +1,5 @@
+create role r;
+set role r;
+drop role r;
+revoke all on *.* from current_role;
+ERROR OP000: Invalid role specification `r`
diff --git a/mysql-test/suite/roles/drop_current_role.test b/mysql-test/suite/roles/drop_current_role.test
new file mode 100644
index 00000000..c8d6fc5d
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_role.test
@@ -0,0 +1,9 @@
+--source include/not_embedded.inc
+#
+# MDEV-22521 Server crashes in traverse_role_graph_up or Assertion `user' fails in traverse_role_graph_impl
+#
+create role r;
+set role r;
+drop role r;
+error ER_INVALID_ROLE;
+revoke all on *.* from current_role;
diff --git a/mysql-test/suite/roles/drop_current_user-5176.result b/mysql-test/suite/roles/drop_current_user-5176.result
new file mode 100644
index 00000000..9c4041a0
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_user-5176.result
@@ -0,0 +1,11 @@
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+connect foo,localhost,foo,,;
+drop user foo@localhost;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+show grants;
+ERROR 42000: There is no such grant defined for user 'foo' on host 'localhost'
+select current_user();
+current_user()
+foo@localhost
diff --git a/mysql-test/suite/roles/drop_current_user-5176.test b/mysql-test/suite/roles/drop_current_user-5176.test
new file mode 100644
index 00000000..27051345
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_user-5176.test
@@ -0,0 +1,14 @@
+#
+# MDEV-5176 Server crashes in fill_schema_applicable_roles on select from APPLICABLE_ROLES after a suicide
+#
+--source include/not_embedded.inc
+
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+--connect (foo,localhost,foo,,)
+drop user foo@localhost;
+select * from information_schema.applicable_roles;
+--error ER_NONEXISTING_GRANT
+show grants;
+select current_user();
+
diff --git a/mysql-test/suite/roles/drop_routines.result b/mysql-test/suite/roles/drop_routines.result
new file mode 100644
index 00000000..7facb9fd
--- /dev/null
+++ b/mysql-test/suite/roles/drop_routines.result
@@ -0,0 +1,81 @@
+create role r1;
+create role r2;
+create role r3;
+create user u1;
+grant r2 to r1;
+grant r3 to r2;
+grant r1 to u1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+GRANT `r1` TO `u1`@`%`
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+grant SELECT on *.* to u1;
+grant INSERT on mysql.* to r1;
+grant DELETE on mysql.roles_mapping to r2;
+grant UPDATE on mysql.user to r3;
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+grant execute on function mysql.test_func to r2;
+grant execute on procedure mysql.test_proc to r3;
+revoke execute on procedure mysql.test_proc from r2;
+ERROR 42000: There is no such grant defined for user 'r2' on host '' on routine 'test_proc'
+show grants for r1;
+Grants for r1
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT INSERT ON `mysql`.* TO `r1`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+show grants for r2;
+Grants for r2
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT `r3` TO `r2`
+show grants for r3;
+Grants for r3
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r3`
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+show grants for r2;
+Grants for r2
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT `r3` TO `r2`
+connect u1,localhost,u1,,;
+select mysql.test_func("none");
+ERROR 42000: execute command denied to user 'u1'@'%' for routine 'mysql.test_func'
+set role r1;
+select mysql.test_func("r1");
+ERROR 42000: execute command denied to user 'u1'@'%' for routine 'mysql.test_func'
+connection default;
+drop function mysql.test_func;
+drop role r1, r2, r3;
+drop user u1;
diff --git a/mysql-test/suite/roles/drop_routines.test b/mysql-test/suite/roles/drop_routines.test
new file mode 100644
index 00000000..b5972d8d
--- /dev/null
+++ b/mysql-test/suite/roles/drop_routines.test
@@ -0,0 +1,69 @@
+source include/not_embedded.inc;
+
+create role r1;
+create role r2;
+create role r3;
+create user u1;
+
+#CREATE A CHAIN OF ROLES
+grant r2 to r1;
+grant r3 to r2;
+grant r1 to u1;
+
+--sorted_result
+show grants for u1;
+--sorted_result
+show grants for r1;
+
+grant SELECT on *.* to u1;
+grant INSERT on mysql.* to r1;
+grant DELETE on mysql.roles_mapping to r2;
+grant UPDATE on mysql.user to r3;
+
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+
+delimiter |;
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+ select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+delimiter ;|
+
+grant execute on function mysql.test_func to r2;
+grant execute on procedure mysql.test_proc to r3;
+
+--error ER_NONEXISTING_PROC_GRANT
+revoke execute on procedure mysql.test_proc from r2;
+
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+
+--sorted_result
+show grants for r2;
+
+--connect (u1,localhost,u1,,)
+
+--error ER_PROCACCESS_DENIED_ERROR
+select mysql.test_func("none");
+set role r1;
+--error ER_PROCACCESS_DENIED_ERROR
+select mysql.test_func("r1");
+
+connection default;
+
+drop function mysql.test_func;
+drop role r1, r2, r3;
+drop user u1;
diff --git a/mysql-test/suite/roles/flush_roles-12366.result b/mysql-test/suite/roles/flush_roles-12366.result
new file mode 100644
index 00000000..043f79f8
--- /dev/null
+++ b/mysql-test/suite/roles/flush_roles-12366.result
@@ -0,0 +1,539 @@
+#
+# MDEV-12366: FLUSH PRIVILEGES can break hierarchy of roles
+#
+# This testcase contains a user, who is granted a master role
+# operations_cluster. operations_cluster is granted 8 different roles
+# who in turn each have 4 different roles granted to them.
+#
+# Only the leaf roles contain privileges to access databases.
+# Make sure the user has access to all databases if the master role
+# is granted to him.
+#
+CREATE USER u;
+CREATE ROLE operations_cluster;
+GRANT operations_cluster TO u;
+CREATE DATABASE bob_live_sg;
+CREATE TABLE bob_live_sg.a (i INT(10));
+CREATE TABLE bob_live_sg.b (i INT(10));
+CREATE TABLE bob_live_sg.c (i INT(10));
+CREATE TABLE bob_live_sg.d (i INT(10));
+CREATE DATABASE oms_live_sg;
+CREATE TABLE oms_live_sg.a (i INT(10));
+CREATE TABLE oms_live_sg.b (i INT(10));
+CREATE TABLE oms_live_sg.c (i INT(10));
+CREATE TABLE oms_live_sg.d (i INT(10));
+CREATE DATABASE bob_live_ph;
+CREATE TABLE bob_live_ph.a (i INT(10));
+CREATE TABLE bob_live_ph.b (i INT(10));
+CREATE TABLE bob_live_ph.c (i INT(10));
+CREATE TABLE bob_live_ph.d (i INT(10));
+CREATE DATABASE oms_live_ph;
+CREATE TABLE oms_live_ph.a (i INT(10));
+CREATE TABLE oms_live_ph.b (i INT(10));
+CREATE TABLE oms_live_ph.c (i INT(10));
+CREATE TABLE oms_live_ph.d (i INT(10));
+CREATE DATABASE bob_live_id;
+CREATE TABLE bob_live_id.a (i INT(10));
+CREATE TABLE bob_live_id.b (i INT(10));
+CREATE TABLE bob_live_id.c (i INT(10));
+CREATE TABLE bob_live_id.d (i INT(10));
+CREATE DATABASE oms_live_id;
+CREATE TABLE oms_live_id.a (i INT(10));
+CREATE TABLE oms_live_id.b (i INT(10));
+CREATE TABLE oms_live_id.c (i INT(10));
+CREATE TABLE oms_live_id.d (i INT(10));
+CREATE DATABASE bob_live_hk;
+CREATE TABLE bob_live_hk.a (i INT(10));
+CREATE TABLE bob_live_hk.b (i INT(10));
+CREATE TABLE bob_live_hk.c (i INT(10));
+CREATE TABLE bob_live_hk.d (i INT(10));
+CREATE DATABASE oms_live_hk;
+CREATE TABLE oms_live_hk.a (i INT(10));
+CREATE TABLE oms_live_hk.b (i INT(10));
+CREATE TABLE oms_live_hk.c (i INT(10));
+CREATE TABLE oms_live_hk.d (i INT(10));
+CREATE DATABASE bob_live_vn;
+CREATE TABLE bob_live_vn.a (i INT(10));
+CREATE TABLE bob_live_vn.b (i INT(10));
+CREATE TABLE bob_live_vn.c (i INT(10));
+CREATE TABLE bob_live_vn.d (i INT(10));
+CREATE DATABASE oms_live_vn;
+CREATE TABLE oms_live_vn.a (i INT(10));
+CREATE TABLE oms_live_vn.b (i INT(10));
+CREATE TABLE oms_live_vn.c (i INT(10));
+CREATE TABLE oms_live_vn.d (i INT(10));
+CREATE DATABASE bob_live_tw;
+CREATE TABLE bob_live_tw.a (i INT(10));
+CREATE TABLE bob_live_tw.b (i INT(10));
+CREATE TABLE bob_live_tw.c (i INT(10));
+CREATE TABLE bob_live_tw.d (i INT(10));
+CREATE DATABASE oms_live_tw;
+CREATE TABLE oms_live_tw.a (i INT(10));
+CREATE TABLE oms_live_tw.b (i INT(10));
+CREATE TABLE oms_live_tw.c (i INT(10));
+CREATE TABLE oms_live_tw.d (i INT(10));
+CREATE DATABASE bob_live_my;
+CREATE TABLE bob_live_my.a (i INT(10));
+CREATE TABLE bob_live_my.b (i INT(10));
+CREATE TABLE bob_live_my.c (i INT(10));
+CREATE TABLE bob_live_my.d (i INT(10));
+CREATE DATABASE oms_live_my;
+CREATE TABLE oms_live_my.a (i INT(10));
+CREATE TABLE oms_live_my.b (i INT(10));
+CREATE TABLE oms_live_my.c (i INT(10));
+CREATE TABLE oms_live_my.d (i INT(10));
+CREATE DATABASE bob_live_th;
+CREATE TABLE bob_live_th.a (i INT(10));
+CREATE TABLE bob_live_th.b (i INT(10));
+CREATE TABLE bob_live_th.c (i INT(10));
+CREATE TABLE bob_live_th.d (i INT(10));
+CREATE DATABASE oms_live_th;
+CREATE TABLE oms_live_th.a (i INT(10));
+CREATE TABLE oms_live_th.b (i INT(10));
+CREATE TABLE oms_live_th.c (i INT(10));
+CREATE TABLE oms_live_th.d (i INT(10));
+CREATE ROLE a_sg;
+CREATE ROLE b_sg;
+CREATE ROLE c_sg;
+CREATE ROLE d_sg;
+CREATE ROLE operations_sg;
+GRANT a_sg TO operations_sg;
+GRANT b_sg TO operations_sg;
+GRANT c_sg TO operations_sg;
+GRANT d_sg TO operations_sg;
+GRANT SELECT ON bob_live_sg.a TO a_sg;
+GRANT SELECT ON bob_live_sg.b TO b_sg;
+GRANT SELECT ON bob_live_sg.c TO c_sg;
+GRANT SELECT ON bob_live_sg.d TO d_sg;
+GRANT SELECT ON oms_live_sg.a TO a_sg;
+GRANT SELECT ON oms_live_sg.b TO b_sg;
+GRANT SELECT ON oms_live_sg.c TO c_sg;
+GRANT SELECT ON oms_live_sg.d TO d_sg;
+CREATE ROLE a_ph;
+CREATE ROLE b_ph;
+CREATE ROLE c_ph;
+CREATE ROLE d_ph;
+CREATE ROLE operations_ph;
+GRANT a_ph TO operations_ph;
+GRANT b_ph TO operations_ph;
+GRANT c_ph TO operations_ph;
+GRANT d_ph TO operations_ph;
+GRANT SELECT ON bob_live_ph.a TO a_ph;
+GRANT SELECT ON bob_live_ph.b TO b_ph;
+GRANT SELECT ON bob_live_ph.c TO c_ph;
+GRANT SELECT ON bob_live_ph.d TO d_ph;
+GRANT SELECT ON oms_live_ph.a TO a_ph;
+GRANT SELECT ON oms_live_ph.b TO b_ph;
+GRANT SELECT ON oms_live_ph.c TO c_ph;
+GRANT SELECT ON oms_live_ph.d TO d_ph;
+CREATE ROLE a_id;
+CREATE ROLE b_id;
+CREATE ROLE c_id;
+CREATE ROLE d_id;
+CREATE ROLE operations_id;
+GRANT a_id TO operations_id;
+GRANT b_id TO operations_id;
+GRANT c_id TO operations_id;
+GRANT d_id TO operations_id;
+GRANT SELECT ON bob_live_id.a TO a_id;
+GRANT SELECT ON bob_live_id.b TO b_id;
+GRANT SELECT ON bob_live_id.c TO c_id;
+GRANT SELECT ON bob_live_id.d TO d_id;
+GRANT SELECT ON oms_live_id.a TO a_id;
+GRANT SELECT ON oms_live_id.b TO b_id;
+GRANT SELECT ON oms_live_id.c TO c_id;
+GRANT SELECT ON oms_live_id.d TO d_id;
+CREATE ROLE a_hk;
+CREATE ROLE b_hk;
+CREATE ROLE c_hk;
+CREATE ROLE d_hk;
+CREATE ROLE operations_hk;
+GRANT a_hk TO operations_hk;
+GRANT b_hk TO operations_hk;
+GRANT c_hk TO operations_hk;
+GRANT d_hk TO operations_hk;
+GRANT SELECT ON bob_live_hk.a TO a_hk;
+GRANT SELECT ON bob_live_hk.b TO b_hk;
+GRANT SELECT ON bob_live_hk.c TO c_hk;
+GRANT SELECT ON bob_live_hk.d TO d_hk;
+GRANT SELECT ON oms_live_hk.a TO a_hk;
+GRANT SELECT ON oms_live_hk.b TO b_hk;
+GRANT SELECT ON oms_live_hk.c TO c_hk;
+GRANT SELECT ON oms_live_hk.d TO d_hk;
+CREATE ROLE a_vn;
+CREATE ROLE b_vn;
+CREATE ROLE c_vn;
+CREATE ROLE d_vn;
+CREATE ROLE operations_vn;
+GRANT a_vn TO operations_vn;
+GRANT b_vn TO operations_vn;
+GRANT c_vn TO operations_vn;
+GRANT d_vn TO operations_vn;
+GRANT SELECT ON bob_live_vn.a TO a_vn;
+GRANT SELECT ON bob_live_vn.b TO b_vn;
+GRANT SELECT ON bob_live_vn.c TO c_vn;
+GRANT SELECT ON bob_live_vn.d TO d_vn;
+GRANT SELECT ON oms_live_vn.a TO a_vn;
+GRANT SELECT ON oms_live_vn.b TO b_vn;
+GRANT SELECT ON oms_live_vn.c TO c_vn;
+GRANT SELECT ON oms_live_vn.d TO d_vn;
+CREATE ROLE a_tw;
+CREATE ROLE b_tw;
+CREATE ROLE c_tw;
+CREATE ROLE d_tw;
+CREATE ROLE operations_tw;
+GRANT a_tw TO operations_tw;
+GRANT b_tw TO operations_tw;
+GRANT c_tw TO operations_tw;
+GRANT d_tw TO operations_tw;
+GRANT SELECT ON bob_live_tw.a TO a_tw;
+GRANT SELECT ON bob_live_tw.b TO b_tw;
+GRANT SELECT ON bob_live_tw.c TO c_tw;
+GRANT SELECT ON bob_live_tw.d TO d_tw;
+GRANT SELECT ON oms_live_tw.a TO a_tw;
+GRANT SELECT ON oms_live_tw.b TO b_tw;
+GRANT SELECT ON oms_live_tw.c TO c_tw;
+GRANT SELECT ON oms_live_tw.d TO d_tw;
+CREATE ROLE a_my;
+CREATE ROLE b_my;
+CREATE ROLE c_my;
+CREATE ROLE d_my;
+CREATE ROLE operations_my;
+GRANT a_my TO operations_my;
+GRANT b_my TO operations_my;
+GRANT c_my TO operations_my;
+GRANT d_my TO operations_my;
+GRANT SELECT ON bob_live_my.a TO a_my;
+GRANT SELECT ON bob_live_my.b TO b_my;
+GRANT SELECT ON bob_live_my.c TO c_my;
+GRANT SELECT ON bob_live_my.d TO d_my;
+GRANT SELECT ON oms_live_my.a TO a_my;
+GRANT SELECT ON oms_live_my.b TO b_my;
+GRANT SELECT ON oms_live_my.c TO c_my;
+GRANT SELECT ON oms_live_my.d TO d_my;
+CREATE ROLE a_th;
+CREATE ROLE b_th;
+CREATE ROLE c_th;
+CREATE ROLE d_th;
+CREATE ROLE operations_th;
+GRANT a_th TO operations_th;
+GRANT b_th TO operations_th;
+GRANT c_th TO operations_th;
+GRANT d_th TO operations_th;
+GRANT SELECT ON bob_live_th.a TO a_th;
+GRANT SELECT ON bob_live_th.b TO b_th;
+GRANT SELECT ON bob_live_th.c TO c_th;
+GRANT SELECT ON bob_live_th.d TO d_th;
+GRANT SELECT ON oms_live_th.a TO a_th;
+GRANT SELECT ON oms_live_th.b TO b_th;
+GRANT SELECT ON oms_live_th.c TO c_th;
+GRANT SELECT ON oms_live_th.d TO d_th;
+GRANT operations_sg TO operations_cluster;
+GRANT operations_ph TO operations_cluster;
+GRANT operations_id TO operations_cluster;
+GRANT operations_hk TO operations_cluster;
+GRANT operations_vn TO operations_cluster;
+GRANT operations_tw TO operations_cluster;
+GRANT operations_my TO operations_cluster;
+GRANT operations_th TO operations_cluster;
+connect con1,localhost,u,,;
+SHOW DATABASES;
+Database
+information_schema
+SET ROLE operations_cluster;
+SHOW DATABASES;
+Database
+bob_live_hk
+bob_live_id
+bob_live_my
+bob_live_ph
+bob_live_sg
+bob_live_th
+bob_live_tw
+bob_live_vn
+information_schema
+oms_live_hk
+oms_live_id
+oms_live_my
+oms_live_ph
+oms_live_sg
+oms_live_th
+oms_live_tw
+oms_live_vn
+SELECT COUNT(1) FROM oms_live_sg.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.d;
+COUNT(1)
+0
+connect con2,localhost,root,,;
+FLUSH PRIVILEGES;
+connect con3,localhost,u,,;
+SHOW DATABASES;
+Database
+information_schema
+SET ROLE operations_cluster;
+SHOW DATABASES;
+Database
+bob_live_hk
+bob_live_id
+bob_live_my
+bob_live_ph
+bob_live_sg
+bob_live_th
+bob_live_tw
+bob_live_vn
+information_schema
+oms_live_hk
+oms_live_id
+oms_live_my
+oms_live_ph
+oms_live_sg
+oms_live_th
+oms_live_tw
+oms_live_vn
+SELECT COUNT(1) FROM oms_live_sg.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_sg.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_ph.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_id.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_hk.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_vn.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_tw.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_my.d;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.a;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.b;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.c;
+COUNT(1)
+0
+SELECT COUNT(1) FROM oms_live_th.d;
+COUNT(1)
+0
+connect con4,localhost,root,,;
+DROP DATABASE bob_live_sg;
+DROP DATABASE oms_live_sg;
+DROP DATABASE bob_live_ph;
+DROP DATABASE oms_live_ph;
+DROP DATABASE bob_live_id;
+DROP DATABASE oms_live_id;
+DROP DATABASE bob_live_hk;
+DROP DATABASE oms_live_hk;
+DROP DATABASE bob_live_vn;
+DROP DATABASE oms_live_vn;
+DROP DATABASE bob_live_tw;
+DROP DATABASE oms_live_tw;
+DROP DATABASE bob_live_my;
+DROP DATABASE oms_live_my;
+DROP DATABASE bob_live_th;
+DROP DATABASE oms_live_th;
+DROP ROLE operations_sg;
+DROP ROLE a_sg;
+DROP ROLE b_sg;
+DROP ROLE c_sg;
+DROP ROLE d_sg;
+DROP ROLE operations_ph;
+DROP ROLE a_ph;
+DROP ROLE b_ph;
+DROP ROLE c_ph;
+DROP ROLE d_ph;
+DROP ROLE operations_id;
+DROP ROLE a_id;
+DROP ROLE b_id;
+DROP ROLE c_id;
+DROP ROLE d_id;
+DROP ROLE operations_hk;
+DROP ROLE a_hk;
+DROP ROLE b_hk;
+DROP ROLE c_hk;
+DROP ROLE d_hk;
+DROP ROLE operations_vn;
+DROP ROLE a_vn;
+DROP ROLE b_vn;
+DROP ROLE c_vn;
+DROP ROLE d_vn;
+DROP ROLE operations_tw;
+DROP ROLE a_tw;
+DROP ROLE b_tw;
+DROP ROLE c_tw;
+DROP ROLE d_tw;
+DROP ROLE operations_my;
+DROP ROLE a_my;
+DROP ROLE b_my;
+DROP ROLE c_my;
+DROP ROLE d_my;
+DROP ROLE operations_th;
+DROP ROLE a_th;
+DROP ROLE b_th;
+DROP ROLE c_th;
+DROP ROLE d_th;
+DROP USER u;
+DROP ROLE operations_cluster;
diff --git a/mysql-test/suite/roles/flush_roles-12366.test b/mysql-test/suite/roles/flush_roles-12366.test
new file mode 100644
index 00000000..343ac4ab
--- /dev/null
+++ b/mysql-test/suite/roles/flush_roles-12366.test
@@ -0,0 +1,379 @@
+--source include/not_embedded.inc
+--echo #
+--echo # MDEV-12366: FLUSH PRIVILEGES can break hierarchy of roles
+--echo #
+--echo # This testcase contains a user, who is granted a master role
+--echo # operations_cluster. operations_cluster is granted 8 different roles
+--echo # who in turn each have 4 different roles granted to them.
+--echo #
+--echo # Only the leaf roles contain privileges to access databases.
+--echo # Make sure the user has access to all databases if the master role
+--echo # is granted to him.
+--echo #
+CREATE USER u;
+CREATE ROLE operations_cluster;
+GRANT operations_cluster TO u;
+CREATE DATABASE bob_live_sg;
+CREATE TABLE bob_live_sg.a (i INT(10));
+CREATE TABLE bob_live_sg.b (i INT(10));
+CREATE TABLE bob_live_sg.c (i INT(10));
+CREATE TABLE bob_live_sg.d (i INT(10));
+CREATE DATABASE oms_live_sg;
+CREATE TABLE oms_live_sg.a (i INT(10));
+CREATE TABLE oms_live_sg.b (i INT(10));
+CREATE TABLE oms_live_sg.c (i INT(10));
+CREATE TABLE oms_live_sg.d (i INT(10));
+CREATE DATABASE bob_live_ph;
+CREATE TABLE bob_live_ph.a (i INT(10));
+CREATE TABLE bob_live_ph.b (i INT(10));
+CREATE TABLE bob_live_ph.c (i INT(10));
+CREATE TABLE bob_live_ph.d (i INT(10));
+CREATE DATABASE oms_live_ph;
+CREATE TABLE oms_live_ph.a (i INT(10));
+CREATE TABLE oms_live_ph.b (i INT(10));
+CREATE TABLE oms_live_ph.c (i INT(10));
+CREATE TABLE oms_live_ph.d (i INT(10));
+CREATE DATABASE bob_live_id;
+CREATE TABLE bob_live_id.a (i INT(10));
+CREATE TABLE bob_live_id.b (i INT(10));
+CREATE TABLE bob_live_id.c (i INT(10));
+CREATE TABLE bob_live_id.d (i INT(10));
+CREATE DATABASE oms_live_id;
+CREATE TABLE oms_live_id.a (i INT(10));
+CREATE TABLE oms_live_id.b (i INT(10));
+CREATE TABLE oms_live_id.c (i INT(10));
+CREATE TABLE oms_live_id.d (i INT(10));
+CREATE DATABASE bob_live_hk;
+CREATE TABLE bob_live_hk.a (i INT(10));
+CREATE TABLE bob_live_hk.b (i INT(10));
+CREATE TABLE bob_live_hk.c (i INT(10));
+CREATE TABLE bob_live_hk.d (i INT(10));
+CREATE DATABASE oms_live_hk;
+CREATE TABLE oms_live_hk.a (i INT(10));
+CREATE TABLE oms_live_hk.b (i INT(10));
+CREATE TABLE oms_live_hk.c (i INT(10));
+CREATE TABLE oms_live_hk.d (i INT(10));
+CREATE DATABASE bob_live_vn;
+CREATE TABLE bob_live_vn.a (i INT(10));
+CREATE TABLE bob_live_vn.b (i INT(10));
+CREATE TABLE bob_live_vn.c (i INT(10));
+CREATE TABLE bob_live_vn.d (i INT(10));
+CREATE DATABASE oms_live_vn;
+CREATE TABLE oms_live_vn.a (i INT(10));
+CREATE TABLE oms_live_vn.b (i INT(10));
+CREATE TABLE oms_live_vn.c (i INT(10));
+CREATE TABLE oms_live_vn.d (i INT(10));
+CREATE DATABASE bob_live_tw;
+CREATE TABLE bob_live_tw.a (i INT(10));
+CREATE TABLE bob_live_tw.b (i INT(10));
+CREATE TABLE bob_live_tw.c (i INT(10));
+CREATE TABLE bob_live_tw.d (i INT(10));
+CREATE DATABASE oms_live_tw;
+CREATE TABLE oms_live_tw.a (i INT(10));
+CREATE TABLE oms_live_tw.b (i INT(10));
+CREATE TABLE oms_live_tw.c (i INT(10));
+CREATE TABLE oms_live_tw.d (i INT(10));
+CREATE DATABASE bob_live_my;
+CREATE TABLE bob_live_my.a (i INT(10));
+CREATE TABLE bob_live_my.b (i INT(10));
+CREATE TABLE bob_live_my.c (i INT(10));
+CREATE TABLE bob_live_my.d (i INT(10));
+CREATE DATABASE oms_live_my;
+CREATE TABLE oms_live_my.a (i INT(10));
+CREATE TABLE oms_live_my.b (i INT(10));
+CREATE TABLE oms_live_my.c (i INT(10));
+CREATE TABLE oms_live_my.d (i INT(10));
+CREATE DATABASE bob_live_th;
+CREATE TABLE bob_live_th.a (i INT(10));
+CREATE TABLE bob_live_th.b (i INT(10));
+CREATE TABLE bob_live_th.c (i INT(10));
+CREATE TABLE bob_live_th.d (i INT(10));
+CREATE DATABASE oms_live_th;
+CREATE TABLE oms_live_th.a (i INT(10));
+CREATE TABLE oms_live_th.b (i INT(10));
+CREATE TABLE oms_live_th.c (i INT(10));
+CREATE TABLE oms_live_th.d (i INT(10));
+CREATE ROLE a_sg;
+CREATE ROLE b_sg;
+CREATE ROLE c_sg;
+CREATE ROLE d_sg;
+CREATE ROLE operations_sg;
+GRANT a_sg TO operations_sg;
+GRANT b_sg TO operations_sg;
+GRANT c_sg TO operations_sg;
+GRANT d_sg TO operations_sg;
+GRANT SELECT ON bob_live_sg.a TO a_sg;
+GRANT SELECT ON bob_live_sg.b TO b_sg;
+GRANT SELECT ON bob_live_sg.c TO c_sg;
+GRANT SELECT ON bob_live_sg.d TO d_sg;
+GRANT SELECT ON oms_live_sg.a TO a_sg;
+GRANT SELECT ON oms_live_sg.b TO b_sg;
+GRANT SELECT ON oms_live_sg.c TO c_sg;
+GRANT SELECT ON oms_live_sg.d TO d_sg;
+CREATE ROLE a_ph;
+CREATE ROLE b_ph;
+CREATE ROLE c_ph;
+CREATE ROLE d_ph;
+CREATE ROLE operations_ph;
+GRANT a_ph TO operations_ph;
+GRANT b_ph TO operations_ph;
+GRANT c_ph TO operations_ph;
+GRANT d_ph TO operations_ph;
+GRANT SELECT ON bob_live_ph.a TO a_ph;
+GRANT SELECT ON bob_live_ph.b TO b_ph;
+GRANT SELECT ON bob_live_ph.c TO c_ph;
+GRANT SELECT ON bob_live_ph.d TO d_ph;
+GRANT SELECT ON oms_live_ph.a TO a_ph;
+GRANT SELECT ON oms_live_ph.b TO b_ph;
+GRANT SELECT ON oms_live_ph.c TO c_ph;
+GRANT SELECT ON oms_live_ph.d TO d_ph;
+CREATE ROLE a_id;
+CREATE ROLE b_id;
+CREATE ROLE c_id;
+CREATE ROLE d_id;
+CREATE ROLE operations_id;
+GRANT a_id TO operations_id;
+GRANT b_id TO operations_id;
+GRANT c_id TO operations_id;
+GRANT d_id TO operations_id;
+GRANT SELECT ON bob_live_id.a TO a_id;
+GRANT SELECT ON bob_live_id.b TO b_id;
+GRANT SELECT ON bob_live_id.c TO c_id;
+GRANT SELECT ON bob_live_id.d TO d_id;
+GRANT SELECT ON oms_live_id.a TO a_id;
+GRANT SELECT ON oms_live_id.b TO b_id;
+GRANT SELECT ON oms_live_id.c TO c_id;
+GRANT SELECT ON oms_live_id.d TO d_id;
+CREATE ROLE a_hk;
+CREATE ROLE b_hk;
+CREATE ROLE c_hk;
+CREATE ROLE d_hk;
+CREATE ROLE operations_hk;
+GRANT a_hk TO operations_hk;
+GRANT b_hk TO operations_hk;
+GRANT c_hk TO operations_hk;
+GRANT d_hk TO operations_hk;
+GRANT SELECT ON bob_live_hk.a TO a_hk;
+GRANT SELECT ON bob_live_hk.b TO b_hk;
+GRANT SELECT ON bob_live_hk.c TO c_hk;
+GRANT SELECT ON bob_live_hk.d TO d_hk;
+GRANT SELECT ON oms_live_hk.a TO a_hk;
+GRANT SELECT ON oms_live_hk.b TO b_hk;
+GRANT SELECT ON oms_live_hk.c TO c_hk;
+GRANT SELECT ON oms_live_hk.d TO d_hk;
+CREATE ROLE a_vn;
+CREATE ROLE b_vn;
+CREATE ROLE c_vn;
+CREATE ROLE d_vn;
+CREATE ROLE operations_vn;
+GRANT a_vn TO operations_vn;
+GRANT b_vn TO operations_vn;
+GRANT c_vn TO operations_vn;
+GRANT d_vn TO operations_vn;
+GRANT SELECT ON bob_live_vn.a TO a_vn;
+GRANT SELECT ON bob_live_vn.b TO b_vn;
+GRANT SELECT ON bob_live_vn.c TO c_vn;
+GRANT SELECT ON bob_live_vn.d TO d_vn;
+GRANT SELECT ON oms_live_vn.a TO a_vn;
+GRANT SELECT ON oms_live_vn.b TO b_vn;
+GRANT SELECT ON oms_live_vn.c TO c_vn;
+GRANT SELECT ON oms_live_vn.d TO d_vn;
+CREATE ROLE a_tw;
+CREATE ROLE b_tw;
+CREATE ROLE c_tw;
+CREATE ROLE d_tw;
+CREATE ROLE operations_tw;
+GRANT a_tw TO operations_tw;
+GRANT b_tw TO operations_tw;
+GRANT c_tw TO operations_tw;
+GRANT d_tw TO operations_tw;
+GRANT SELECT ON bob_live_tw.a TO a_tw;
+GRANT SELECT ON bob_live_tw.b TO b_tw;
+GRANT SELECT ON bob_live_tw.c TO c_tw;
+GRANT SELECT ON bob_live_tw.d TO d_tw;
+GRANT SELECT ON oms_live_tw.a TO a_tw;
+GRANT SELECT ON oms_live_tw.b TO b_tw;
+GRANT SELECT ON oms_live_tw.c TO c_tw;
+GRANT SELECT ON oms_live_tw.d TO d_tw;
+CREATE ROLE a_my;
+CREATE ROLE b_my;
+CREATE ROLE c_my;
+CREATE ROLE d_my;
+CREATE ROLE operations_my;
+GRANT a_my TO operations_my;
+GRANT b_my TO operations_my;
+GRANT c_my TO operations_my;
+GRANT d_my TO operations_my;
+GRANT SELECT ON bob_live_my.a TO a_my;
+GRANT SELECT ON bob_live_my.b TO b_my;
+GRANT SELECT ON bob_live_my.c TO c_my;
+GRANT SELECT ON bob_live_my.d TO d_my;
+GRANT SELECT ON oms_live_my.a TO a_my;
+GRANT SELECT ON oms_live_my.b TO b_my;
+GRANT SELECT ON oms_live_my.c TO c_my;
+GRANT SELECT ON oms_live_my.d TO d_my;
+CREATE ROLE a_th;
+CREATE ROLE b_th;
+CREATE ROLE c_th;
+CREATE ROLE d_th;
+CREATE ROLE operations_th;
+GRANT a_th TO operations_th;
+GRANT b_th TO operations_th;
+GRANT c_th TO operations_th;
+GRANT d_th TO operations_th;
+GRANT SELECT ON bob_live_th.a TO a_th;
+GRANT SELECT ON bob_live_th.b TO b_th;
+GRANT SELECT ON bob_live_th.c TO c_th;
+GRANT SELECT ON bob_live_th.d TO d_th;
+GRANT SELECT ON oms_live_th.a TO a_th;
+GRANT SELECT ON oms_live_th.b TO b_th;
+GRANT SELECT ON oms_live_th.c TO c_th;
+GRANT SELECT ON oms_live_th.d TO d_th;
+GRANT operations_sg TO operations_cluster;
+GRANT operations_ph TO operations_cluster;
+GRANT operations_id TO operations_cluster;
+GRANT operations_hk TO operations_cluster;
+GRANT operations_vn TO operations_cluster;
+GRANT operations_tw TO operations_cluster;
+GRANT operations_my TO operations_cluster;
+GRANT operations_th TO operations_cluster;
+
+connect(con1,localhost,u,,);
+SHOW DATABASES;
+SET ROLE operations_cluster;
+SHOW DATABASES;
+SELECT COUNT(1) FROM oms_live_sg.a;
+SELECT COUNT(1) FROM oms_live_sg.b;
+SELECT COUNT(1) FROM oms_live_sg.c;
+SELECT COUNT(1) FROM oms_live_sg.d;
+SELECT COUNT(1) FROM oms_live_ph.a;
+SELECT COUNT(1) FROM oms_live_ph.b;
+SELECT COUNT(1) FROM oms_live_ph.c;
+SELECT COUNT(1) FROM oms_live_ph.d;
+SELECT COUNT(1) FROM oms_live_id.a;
+SELECT COUNT(1) FROM oms_live_id.b;
+SELECT COUNT(1) FROM oms_live_id.c;
+SELECT COUNT(1) FROM oms_live_id.d;
+SELECT COUNT(1) FROM oms_live_hk.a;
+SELECT COUNT(1) FROM oms_live_hk.b;
+SELECT COUNT(1) FROM oms_live_hk.c;
+SELECT COUNT(1) FROM oms_live_hk.d;
+SELECT COUNT(1) FROM oms_live_vn.a;
+SELECT COUNT(1) FROM oms_live_vn.b;
+SELECT COUNT(1) FROM oms_live_vn.c;
+SELECT COUNT(1) FROM oms_live_vn.d;
+SELECT COUNT(1) FROM oms_live_tw.a;
+SELECT COUNT(1) FROM oms_live_tw.b;
+SELECT COUNT(1) FROM oms_live_tw.c;
+SELECT COUNT(1) FROM oms_live_tw.d;
+SELECT COUNT(1) FROM oms_live_my.a;
+SELECT COUNT(1) FROM oms_live_my.b;
+SELECT COUNT(1) FROM oms_live_my.c;
+SELECT COUNT(1) FROM oms_live_my.d;
+SELECT COUNT(1) FROM oms_live_th.a;
+SELECT COUNT(1) FROM oms_live_th.b;
+SELECT COUNT(1) FROM oms_live_th.c;
+SELECT COUNT(1) FROM oms_live_th.d;
+
+
+connect(con2,localhost,root,,);
+FLUSH PRIVILEGES;
+
+connect(con3,localhost,u,,);
+SHOW DATABASES;
+SET ROLE operations_cluster;
+SHOW DATABASES;
+SELECT COUNT(1) FROM oms_live_sg.a;
+SELECT COUNT(1) FROM oms_live_sg.b;
+SELECT COUNT(1) FROM oms_live_sg.c;
+SELECT COUNT(1) FROM oms_live_sg.d;
+SELECT COUNT(1) FROM oms_live_ph.a;
+SELECT COUNT(1) FROM oms_live_ph.b;
+SELECT COUNT(1) FROM oms_live_ph.c;
+SELECT COUNT(1) FROM oms_live_ph.d;
+SELECT COUNT(1) FROM oms_live_id.a;
+SELECT COUNT(1) FROM oms_live_id.b;
+SELECT COUNT(1) FROM oms_live_id.c;
+SELECT COUNT(1) FROM oms_live_id.d;
+SELECT COUNT(1) FROM oms_live_hk.a;
+SELECT COUNT(1) FROM oms_live_hk.b;
+SELECT COUNT(1) FROM oms_live_hk.c;
+SELECT COUNT(1) FROM oms_live_hk.d;
+SELECT COUNT(1) FROM oms_live_vn.a;
+SELECT COUNT(1) FROM oms_live_vn.b;
+SELECT COUNT(1) FROM oms_live_vn.c;
+SELECT COUNT(1) FROM oms_live_vn.d;
+SELECT COUNT(1) FROM oms_live_tw.a;
+SELECT COUNT(1) FROM oms_live_tw.b;
+SELECT COUNT(1) FROM oms_live_tw.c;
+SELECT COUNT(1) FROM oms_live_tw.d;
+SELECT COUNT(1) FROM oms_live_my.a;
+SELECT COUNT(1) FROM oms_live_my.b;
+SELECT COUNT(1) FROM oms_live_my.c;
+SELECT COUNT(1) FROM oms_live_my.d;
+SELECT COUNT(1) FROM oms_live_th.a;
+SELECT COUNT(1) FROM oms_live_th.b;
+SELECT COUNT(1) FROM oms_live_th.c;
+SELECT COUNT(1) FROM oms_live_th.d;
+
+
+connect(con4,localhost,root,,);
+
+DROP DATABASE bob_live_sg;
+DROP DATABASE oms_live_sg;
+DROP DATABASE bob_live_ph;
+DROP DATABASE oms_live_ph;
+DROP DATABASE bob_live_id;
+DROP DATABASE oms_live_id;
+DROP DATABASE bob_live_hk;
+DROP DATABASE oms_live_hk;
+DROP DATABASE bob_live_vn;
+DROP DATABASE oms_live_vn;
+DROP DATABASE bob_live_tw;
+DROP DATABASE oms_live_tw;
+DROP DATABASE bob_live_my;
+DROP DATABASE oms_live_my;
+DROP DATABASE bob_live_th;
+DROP DATABASE oms_live_th;
+DROP ROLE operations_sg;
+DROP ROLE a_sg;
+DROP ROLE b_sg;
+DROP ROLE c_sg;
+DROP ROLE d_sg;
+DROP ROLE operations_ph;
+DROP ROLE a_ph;
+DROP ROLE b_ph;
+DROP ROLE c_ph;
+DROP ROLE d_ph;
+DROP ROLE operations_id;
+DROP ROLE a_id;
+DROP ROLE b_id;
+DROP ROLE c_id;
+DROP ROLE d_id;
+DROP ROLE operations_hk;
+DROP ROLE a_hk;
+DROP ROLE b_hk;
+DROP ROLE c_hk;
+DROP ROLE d_hk;
+DROP ROLE operations_vn;
+DROP ROLE a_vn;
+DROP ROLE b_vn;
+DROP ROLE c_vn;
+DROP ROLE d_vn;
+DROP ROLE operations_tw;
+DROP ROLE a_tw;
+DROP ROLE b_tw;
+DROP ROLE c_tw;
+DROP ROLE d_tw;
+DROP ROLE operations_my;
+DROP ROLE a_my;
+DROP ROLE b_my;
+DROP ROLE c_my;
+DROP ROLE d_my;
+DROP ROLE operations_th;
+DROP ROLE a_th;
+DROP ROLE b_th;
+DROP ROLE c_th;
+DROP ROLE d_th;
+DROP USER u;
+DROP ROLE operations_cluster;
diff --git a/mysql-test/suite/roles/flush_roles-17898.result b/mysql-test/suite/roles/flush_roles-17898.result
new file mode 100644
index 00000000..85aeca36
--- /dev/null
+++ b/mysql-test/suite/roles/flush_roles-17898.result
@@ -0,0 +1,36 @@
+use mysql;
+insert db (db,user,select_priv) values ('foo','dwr_foo','Y'), ('bar','dwr_bar','Y');
+insert roles_mapping (user,role) values ('dwr_qux_dev','dwr_foo'),('dwr_qux_dev','dwr_bar');
+insert global_priv values ('','dwr_foo','{"is_role":true}'), ('','dwr_bar','{"is_role":true}'),
+('','dwr_qux_dev','{"access":16384,"is_role":true}');
+flush privileges;
+drop role dwr_foo;
+drop role dwr_bar;
+drop role dwr_qux_dev;
+use test;
+create table db_copy as select * from mysql.db;
+delete from mysql.db;
+flush privileges;
+create user u1@localhost;
+create role r1;
+create role r2;
+grant r1 to u1@localhost;
+grant select on test.* to r2;
+grant select on m_.* to r2;
+grant r2 to r1;
+show grants for u1@localhost;
+Grants for u1@localhost
+GRANT `r1` TO `u1`@`localhost`
+GRANT USAGE ON *.* TO `u1`@`localhost`
+show grants for r1;
+Grants for r1
+GRANT `r2` TO `r1`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT SELECT ON `test`.* TO `r2`
+GRANT SELECT ON `m_`.* TO `r2`
+drop user u1@localhost;
+drop role r1, r2;
+insert mysql.db select * from db_copy;
+flush privileges;
+drop table db_copy;
diff --git a/mysql-test/suite/roles/flush_roles-17898.test b/mysql-test/suite/roles/flush_roles-17898.test
new file mode 100644
index 00000000..8dc55a87
--- /dev/null
+++ b/mysql-test/suite/roles/flush_roles-17898.test
@@ -0,0 +1,37 @@
+source include/not_embedded.inc;
+#
+# MDEV-17898 FLUSH PRIVILEGES crashes server with segfault
+#
+use mysql;
+insert db (db,user,select_priv) values ('foo','dwr_foo','Y'), ('bar','dwr_bar','Y');
+insert roles_mapping (user,role) values ('dwr_qux_dev','dwr_foo'),('dwr_qux_dev','dwr_bar');
+insert global_priv values ('','dwr_foo','{"is_role":true}'), ('','dwr_bar','{"is_role":true}'),
+ ('','dwr_qux_dev','{"access":16384,"is_role":true}');
+flush privileges;
+drop role dwr_foo;
+drop role dwr_bar;
+drop role dwr_qux_dev;
+use test;
+
+#
+# MDEV-18298 Crashes server with segfault during role grants
+#
+create table db_copy as select * from mysql.db;
+delete from mysql.db;
+flush privileges;
+
+create user u1@localhost;
+create role r1;
+create role r2;
+grant r1 to u1@localhost;
+grant select on test.* to r2;
+grant select on m_.* to r2;
+grant r2 to r1;
+show grants for u1@localhost;
+show grants for r1;
+drop user u1@localhost;
+drop role r1, r2;
+
+insert mysql.db select * from db_copy;
+flush privileges;
+drop table db_copy;
diff --git a/mysql-test/suite/roles/grant-5771.result b/mysql-test/suite/roles/grant-5771.result
new file mode 100644
index 00000000..14e033f4
--- /dev/null
+++ b/mysql-test/suite/roles/grant-5771.result
@@ -0,0 +1,36 @@
+create database mysqltest1;
+create database mysqltest2;
+create user foo@localhost;
+create role r1, r2;
+grant all on mysqltest1.* to r1;
+grant all on mysqltest2.* to r2;
+grant r1 to r2;
+grant r2 to foo@localhost;
+connect foo,localhost,foo,,;
+select current_user;
+current_user
+foo@localhost
+show tables in mysqltest1;
+ERROR 42000: Access denied for user 'foo'@'localhost' to database 'mysqltest1'
+show tables in mysqltest2;
+ERROR 42000: Access denied for user 'foo'@'localhost' to database 'mysqltest2'
+set role r2;
+show tables in mysqltest1;
+Tables_in_mysqltest1
+show tables in mysqltest2;
+Tables_in_mysqltest2
+show grants;
+Grants for foo@localhost
+GRANT `r2` TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT `r1` TO `r2`
+GRANT USAGE ON *.* TO `r2`
+GRANT ALL PRIVILEGES ON `mysqltest2`.* TO `r2`
+GRANT USAGE ON *.* TO `r1`
+GRANT ALL PRIVILEGES ON `mysqltest1`.* TO `r1`
+connection default;
+drop user foo@localhost;
+drop role r1;
+drop role r2;
+drop database mysqltest1;
+drop database mysqltest2;
diff --git a/mysql-test/suite/roles/grant-5771.test b/mysql-test/suite/roles/grant-5771.test
new file mode 100644
index 00000000..3c8f5d2f
--- /dev/null
+++ b/mysql-test/suite/roles/grant-5771.test
@@ -0,0 +1,32 @@
+#
+# MDEV-5771 Privileges acquired via roles depend on the order of granting
+#
+--source include/not_embedded.inc
+
+create database mysqltest1;
+create database mysqltest2;
+
+create user foo@localhost;
+create role r1, r2;
+grant all on mysqltest1.* to r1;
+grant all on mysqltest2.* to r2;
+grant r1 to r2;
+grant r2 to foo@localhost;
+
+--connect (foo,localhost,foo,,)
+select current_user;
+--error ER_DBACCESS_DENIED_ERROR
+show tables in mysqltest1;
+--error ER_DBACCESS_DENIED_ERROR
+show tables in mysqltest2;
+set role r2;
+show tables in mysqltest1;
+show tables in mysqltest2;
+show grants;
+
+connection default;
+drop user foo@localhost;
+drop role r1;
+drop role r2;
+drop database mysqltest1;
+drop database mysqltest2;
diff --git a/mysql-test/suite/roles/grant_empty.result b/mysql-test/suite/roles/grant_empty.result
new file mode 100644
index 00000000..2e454299
--- /dev/null
+++ b/mysql-test/suite/roles/grant_empty.result
@@ -0,0 +1,16 @@
+grant '' to foo@localhost;
+ERROR OP000: Invalid role specification ``
+create user ''@localhost;
+create role r1;
+grant r1 to ''@localhost;
+connect con1,localhost,nonexisting_user,,;
+select current_user;
+current_user
+@localhost
+show grants;
+Grants for @localhost
+GRANT `r1` TO ``@`localhost`
+GRANT USAGE ON *.* TO ``@`localhost`
+connection default;
+drop role r1;
+drop user ''@localhost;
diff --git a/mysql-test/suite/roles/grant_empty.test b/mysql-test/suite/roles/grant_empty.test
new file mode 100644
index 00000000..e419fffa
--- /dev/null
+++ b/mysql-test/suite/roles/grant_empty.test
@@ -0,0 +1,23 @@
+#
+# MDEV-5668 Assertion `granted_role->is_role()' fails on granting role with empty name
+#
+--error ER_INVALID_ROLE
+grant '' to foo@localhost;
+
+#
+# MDEV-5238 Server crashes in find_role_grant_pair on SHOW GRANTS for an anonymous user
+#
+--source include/not_embedded.inc
+
+create user ''@localhost;
+create role r1;
+grant r1 to ''@localhost;
+
+--connect (con1,localhost,nonexisting_user,,)
+select current_user;
+show grants;
+
+connection default;
+drop role r1;
+drop user ''@localhost;
+
diff --git a/mysql-test/suite/roles/grant_proxy-5526.result b/mysql-test/suite/roles/grant_proxy-5526.result
new file mode 100644
index 00000000..8bfa8e39
--- /dev/null
+++ b/mysql-test/suite/roles/grant_proxy-5526.result
@@ -0,0 +1,9 @@
+create role r1;
+create user user;
+grant proxy on r1 to user;
+show grants for user;
+Grants for user@%
+GRANT USAGE ON *.* TO `user`@`%`
+GRANT PROXY ON 'r1'@'%' TO 'user'@'%'
+drop user user;
+drop role r1;
diff --git a/mysql-test/suite/roles/grant_proxy-5526.test b/mysql-test/suite/roles/grant_proxy-5526.test
new file mode 100644
index 00000000..eadc763b
--- /dev/null
+++ b/mysql-test/suite/roles/grant_proxy-5526.test
@@ -0,0 +1,11 @@
+--source include/not_embedded.inc
+#
+# MDEV-5526 Assertion `proxied_user->host.length' fails on GRANT PROXY ON <role>
+#
+create role r1;
+create user user;
+grant proxy on r1 to user;
+show grants for user;
+drop user user;
+drop role r1;
+
diff --git a/mysql-test/suite/roles/grant_revoke_current.result b/mysql-test/suite/roles/grant_revoke_current.result
new file mode 100644
index 00000000..329cab64
--- /dev/null
+++ b/mysql-test/suite/roles/grant_revoke_current.result
@@ -0,0 +1,44 @@
+select priv into @root_priv from mysql.global_priv where user='root' and host='localhost';
+grant select on *.* to current_role;
+ERROR 0L000: Invalid definer
+revoke select on *.* from current_role;
+ERROR 0L000: Invalid definer
+revoke all, grant option from current_role;
+ERROR 0L000: Invalid definer
+create role r1;
+grant insert on test.* to r1;
+grant r1 to current_user;
+set role r1;
+select current_role();
+current_role()
+r1
+grant select on *.* to current_role;
+show grants for current_role;
+Grants for r1
+GRANT SELECT ON *.* TO `r1`
+GRANT INSERT ON `test`.* TO `r1`
+revoke insert on test.* from current_role;
+show grants for current_role;
+Grants for r1
+GRANT SELECT ON *.* TO `r1`
+revoke all, grant option from current_role;
+show grants for current_role;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+set password=password('foobar');
+show grants;
+Grants for root@localhost
+GRANT `r1` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` IDENTIFIED BY PASSWORD '*9B500343BC52E2911172EB52AE5CF4847604C6E5' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `r1`
+grant r1 to current_user() identified by 'barfoo';
+show grants;
+Grants for root@localhost
+GRANT `r1` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` IDENTIFIED BY PASSWORD '*343915A8181B5728EADBDC73E1F7E6B0C3998483' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `r1`
+set password='';
+drop role r1;
+update mysql.global_priv set priv=@root_priv where user='root' and host='localhost';
diff --git a/mysql-test/suite/roles/grant_revoke_current.test b/mysql-test/suite/roles/grant_revoke_current.test
new file mode 100644
index 00000000..fda55358
--- /dev/null
+++ b/mysql-test/suite/roles/grant_revoke_current.test
@@ -0,0 +1,32 @@
+--source include/not_embedded.inc
+select priv into @root_priv from mysql.global_priv where user='root' and host='localhost';
+
+--error ER_MALFORMED_DEFINER
+grant select on *.* to current_role;
+--error ER_MALFORMED_DEFINER
+revoke select on *.* from current_role;
+--error ER_MALFORMED_DEFINER
+revoke all, grant option from current_role;
+
+create role r1;
+grant insert on test.* to r1;
+grant r1 to current_user;
+set role r1;
+select current_role();
+
+grant select on *.* to current_role;
+show grants for current_role;
+revoke insert on test.* from current_role;
+show grants for current_role;
+revoke all, grant option from current_role;
+show grants for current_role;
+
+set password=password('foobar');
+show grants;
+grant r1 to current_user() identified by 'barfoo';
+show grants;
+set password='';
+
+#cleanup
+drop role r1;
+update mysql.global_priv set priv=@root_priv where user='root' and host='localhost';
diff --git a/mysql-test/suite/roles/grant_role_auto_create_user.result b/mysql-test/suite/roles/grant_role_auto_create_user.result
new file mode 100644
index 00000000..61ce0359
--- /dev/null
+++ b/mysql-test/suite/roles/grant_role_auto_create_user.result
@@ -0,0 +1,83 @@
+create database db;
+create role auto_create;
+create user auto_create;
+grant all on db.* to auto_create;
+create user foo@localhost;
+grant auto_create to foo@localhost;
+create user bar@localhost identified by 'baz';
+grant auto_create to bar@localhost;
+connect con1,localhost,foo,,;
+set role 'auto_create';
+use db;
+create table t1 (i int);
+disconnect con1;
+connect con1,localhost,bar,baz,;
+set role auto_create;
+use db;
+insert into t1 values (1);
+disconnect con1;
+connection default;
+drop user foo@localhost, bar@localhost;
+set sql_mode = 'no_auto_create_user';
+grant auto_create to foo@localhost;
+ERROR 28000: Can't find any matching row in the user table
+grant auto_create to bar@localhost identified by 'baz';
+select user, host from mysql.user where user = 'bar';
+User Host
+bar localhost
+set sql_mode = '';
+connect con1,localhost,bar,baz,;
+set role auto_create;
+use db;
+drop table t1;
+disconnect con1;
+connection default;
+create user foo@localhost;
+connect con1, localhost, foo,,;
+set sql_mode = '';
+grant auto_create to bar2@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+grant auto_create to foo2@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+set sql_mode = 'no_auto_create_user';
+grant auto_create to bar2@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+grant auto_create to foo2@localhost identified by 'pass';
+ERROR 28000: Access denied for user 'foo'@'localhost'
+disconnect con1;
+connection default;
+grant auto_create to foo@localhost;
+connect con1, localhost, foo,,;
+set sql_mode = '';
+grant auto_create to bar@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+grant auto_create to bar2@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+grant auto_create to foo2@localhost identified by 'pass';
+ERROR 28000: Access denied for user 'foo'@'localhost'
+set sql_mode = 'no_auto_create_user';
+grant auto_create to bar2@localhost;
+ERROR 28000: Access denied for user 'foo'@'localhost'
+grant auto_create to foo2@localhost identified by 'pass';
+ERROR 28000: Access denied for user 'foo'@'localhost'
+connection default;
+grant auto_create to foo@localhost with admin option;
+disconnect con1;
+connect con1, localhost, foo,,;
+set sql_mode = '';
+grant auto_create to bar@localhost;
+grant auto_create to bar2@localhost;
+ERROR 42000: You are not allowed to create a user with GRANT
+grant auto_create to foo2@localhost identified by 'pass';
+ERROR 42000: You are not allowed to create a user with GRANT
+set sql_mode = 'no_auto_create_user';
+grant auto_create to bar2@localhost;
+ERROR 28000: Can't find any matching row in the user table
+grant auto_create to foo2@localhost identified by 'pass';
+ERROR 42000: You are not allowed to create a user with GRANT
+connection default;
+drop user foo@localhost;
+drop user bar@localhost;
+drop role auto_create;
+drop user auto_create;
+drop database db;
diff --git a/mysql-test/suite/roles/grant_role_auto_create_user.test b/mysql-test/suite/roles/grant_role_auto_create_user.test
new file mode 100644
index 00000000..e6739347
--- /dev/null
+++ b/mysql-test/suite/roles/grant_role_auto_create_user.test
@@ -0,0 +1,123 @@
+#
+# MDEV-5221 User auto-creation does not work upon GRANT <role>
+#
+--source include/not_embedded.inc
+
+create database db;
+create role auto_create;
+create user auto_create;
+grant all on db.* to auto_create;
+create user foo@localhost;
+grant auto_create to foo@localhost;
+create user bar@localhost identified by 'baz';
+grant auto_create to bar@localhost;
+
+# Test if the users have been created and the role has been granted to them
+--connect (con1,localhost,foo,,)
+set role 'auto_create';
+use db;
+create table t1 (i int);
+--disconnect con1
+
+--connect (con1,localhost,bar,baz,)
+set role auto_create;
+use db;
+insert into t1 values (1);
+--disconnect con1
+
+--connection default
+drop user foo@localhost, bar@localhost;
+
+set sql_mode = 'no_auto_create_user';
+--error ER_PASSWORD_NO_MATCH
+grant auto_create to foo@localhost;
+grant auto_create to bar@localhost identified by 'baz';
+select user, host from mysql.user where user = 'bar';
+set sql_mode = '';
+
+--connect (con1,localhost,bar,baz,)
+set role auto_create;
+use db;
+drop table t1;
+--disconnect con1
+
+--connection default
+
+create user foo@localhost;
+
+# test all possible cases with a user who has no rights to grant the role
+--connect (con1, localhost, foo,,)
+
+set sql_mode = '';
+#try and grant roles, no rights however
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to bar2@localhost;
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to foo2@localhost;
+
+set sql_mode = 'no_auto_create_user';
+#try and grant roles, no rights however
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to bar2@localhost;
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to foo2@localhost identified by 'pass';
+--disconnect con1
+--connection default
+grant auto_create to foo@localhost;
+
+--connect (con1, localhost, foo,,)
+
+#we now have the role granted to us, but we don't have insert privileges,
+#we should not be able to create a new user
+
+set sql_mode = '';
+#also test that we can not grant a role without admin option
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to bar@localhost;
+
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to bar2@localhost;
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to foo2@localhost identified by 'pass';
+
+set sql_mode = 'no_auto_create_user';
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to bar2@localhost;
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+grant auto_create to foo2@localhost identified by 'pass';
+
+#test that we can grant a role with admin option to an existing user, but not
+#create one
+
+--connection default
+grant auto_create to foo@localhost with admin option;
+
+--disconnect con1
+--connect (con1, localhost, foo,,)
+
+#we now have the role granted to us, but we don't have insert privileges,
+#we should not be able to create a new user
+
+set sql_mode = '';
+#also test that we can grant a role with admin option
+grant auto_create to bar@localhost;
+
+#test that we don't create users if we don't have insert privileges
+--error ER_CANT_CREATE_USER_WITH_GRANT
+grant auto_create to bar2@localhost;
+--error ER_CANT_CREATE_USER_WITH_GRANT
+grant auto_create to foo2@localhost identified by 'pass';
+
+set sql_mode = 'no_auto_create_user';
+--error ER_PASSWORD_NO_MATCH
+grant auto_create to bar2@localhost;
+--error ER_CANT_CREATE_USER_WITH_GRANT
+grant auto_create to foo2@localhost identified by 'pass';
+
+
+--connection default
+drop user foo@localhost;
+drop user bar@localhost;
+drop role auto_create;
+drop user auto_create;
+drop database db;
diff --git a/mysql-test/suite/roles/i_s_applicable_roles_is_default.result b/mysql-test/suite/roles/i_s_applicable_roles_is_default.result
new file mode 100644
index 00000000..ee7d17f3
--- /dev/null
+++ b/mysql-test/suite/roles/i_s_applicable_roles_is_default.result
@@ -0,0 +1,81 @@
+create user foo;
+create role role1;
+create role role2;
+create role role3;
+grant role1 to foo;
+grant role2 to role1;
+grant role3 to foo;
+connect foo, localhost, foo;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@% role1 NO NO
+foo@% role3 NO NO
+role1 role2 NO NULL
+set default role role3;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@% role1 NO NO
+foo@% role3 NO YES
+role1 role2 NO NULL
+set default role role1;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@% role1 NO YES
+foo@% role3 NO NO
+role1 role2 NO NULL
+disconnect foo;
+connection default;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES NO
+root@localhost role2 YES NO
+root@localhost role3 YES NO
+set default role none for foo;
+connect foo, localhost, foo;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@% role1 NO NO
+foo@% role3 NO NO
+role1 role2 NO NULL
+disconnect foo;
+connection default;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES NO
+root@localhost role2 YES NO
+root@localhost role3 YES NO
+set default role role1;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES YES
+root@localhost role2 YES NO
+root@localhost role3 YES NO
+set default role role2;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES NO
+root@localhost role2 YES YES
+root@localhost role3 YES NO
+set default role role3;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES NO
+root@localhost role2 YES NO
+root@localhost role3 YES YES
+set default role none;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+role1 role2 NO NULL
+root@localhost role1 YES NO
+root@localhost role2 YES NO
+root@localhost role3 YES NO
+drop role role3;
+drop role role2;
+drop role role1;
+drop user foo;
+update mysql.global_priv set priv=json_compact(json_remove(priv, '$.default_role'));
diff --git a/mysql-test/suite/roles/i_s_applicable_roles_is_default.test b/mysql-test/suite/roles/i_s_applicable_roles_is_default.test
new file mode 100644
index 00000000..0e643692
--- /dev/null
+++ b/mysql-test/suite/roles/i_s_applicable_roles_is_default.test
@@ -0,0 +1,62 @@
+--source include/not_embedded.inc
+create user foo;
+create role role1;
+create role role2;
+create role role3;
+
+grant role1 to foo;
+grant role2 to role1;
+grant role3 to foo;
+
+
+connect (foo, localhost, foo);
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role role3;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role role1;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+
+disconnect foo;
+connection default;
+
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role none for foo;
+connect (foo, localhost, foo);
+--sorted_result
+select * from information_schema.applicable_roles;
+
+disconnect foo;
+connection default;
+
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role role1;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role role2;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role role3;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+set default role none;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+drop role role3;
+drop role role2;
+drop role role1;
+drop user foo;
+update mysql.global_priv set priv=json_compact(json_remove(priv, '$.default_role'));
diff --git a/mysql-test/suite/roles/ip-6401.result b/mysql-test/suite/roles/ip-6401.result
new file mode 100644
index 00000000..723916f9
--- /dev/null
+++ b/mysql-test/suite/roles/ip-6401.result
@@ -0,0 +1,15 @@
+create role r1;
+create user foo@'127.0.0.1';
+grant r1 to foo@'127.0.0.1';
+connect con1,127.0.0.1,foo,,;
+show grants;
+Grants for foo@127.0.0.1
+GRANT `r1` TO `foo`@`127.0.0.1`
+GRANT USAGE ON *.* TO `foo`@`127.0.0.1`
+set role r1;
+select * from information_schema.enabled_roles;
+ROLE_NAME
+r1
+connection default;
+drop user foo@'127.0.0.1';
+drop role r1;
diff --git a/mysql-test/suite/roles/ip-6401.test b/mysql-test/suite/roles/ip-6401.test
new file mode 100644
index 00000000..b7d4b168
--- /dev/null
+++ b/mysql-test/suite/roles/ip-6401.test
@@ -0,0 +1,16 @@
+#
+# MDEV-6401 SET ROLE returning ERROR 1959 Invalid role specification for valid role
+#
+--source include/not_embedded.inc
+create role r1;
+create user foo@'127.0.0.1';
+grant r1 to foo@'127.0.0.1';
+
+--connect (con1,127.0.0.1,foo,,)
+show grants;
+set role r1;
+select * from information_schema.enabled_roles;
+
+connection default;
+drop user foo@'127.0.0.1';
+drop role r1;
diff --git a/mysql-test/suite/roles/none_public.result b/mysql-test/suite/roles/none_public.result
new file mode 100644
index 00000000..a6c896c9
--- /dev/null
+++ b/mysql-test/suite/roles/none_public.result
@@ -0,0 +1,72 @@
+create role role1;
+create role none;
+ERROR OP000: Invalid role specification `NONE`
+create role public;
+ERROR OP000: Invalid role specification `PUBLIC`
+drop role none;
+ERROR HY000: Operation DROP ROLE failed for 'NONE'
+drop role public;
+ERROR HY000: Operation DROP ROLE failed for PUBLIC
+grant none to role1;
+ERROR OP000: Invalid role specification `NONE`
+grant role1 to none;
+ERROR OP000: Invalid role specification `none`
+grant select on *.* to none;
+ERROR OP000: Invalid role specification `none`
+grant public to role1;
+ERROR OP000: Invalid role specification `PUBLIC`
+grant role1 to public;
+grant select on *.* to public;
+grant role1 to current_role;
+ERROR OP000: Invalid role specification `NONE`
+revoke none from role1;
+ERROR OP000: Invalid role specification `NONE`
+revoke role1 from none;
+ERROR OP000: Invalid role specification `none`
+revoke select on *.* from none;
+ERROR OP000: Invalid role specification `none`
+revoke public from role1;
+ERROR OP000: Invalid role specification `PUBLIC`
+revoke role1 from public;
+revoke select on *.* from public;
+show grants for none;
+ERROR OP000: Invalid role specification `none`
+show grants for public;
+Grants for PUBLIC
+create definer=none view test.v1 as select 1;
+ERROR OP000: Invalid role specification `none`
+create definer=public view test.v1 as select 1;
+ERROR OP000: Invalid role specification `public`
+drop role role1;
+create user foo@localhost;
+connect foo,localhost,foo;
+set default role public;
+ERROR OP000: Invalid role specification `PUBLIC`
+disconnect foo;
+connection default;
+update mysql.global_priv set priv=json_insert(priv, '$.default_role', 'none') where user='foo';
+connect foo,localhost,foo;
+show grants;
+Grants for foo@localhost
+GRANT USAGE ON *.* TO `foo`@`localhost`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+NULL
+disconnect foo;
+connection default;
+update mysql.global_priv set priv=json_insert(priv, '$.default_role', 'public') where user='foo';
+connect foo,localhost,foo;
+show grants;
+Grants for foo@localhost
+GRANT USAGE ON *.* TO `foo`@`localhost`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+NULL
+disconnect foo;
+connection default;
+drop user foo@localhost;
+insert mysql.global_priv values ('', 'none', '{"is_role":true}');
+flush privileges;
+Warnings:
+Error 1959 Invalid role specification `none`
+delete from mysql.global_priv where host='';
diff --git a/mysql-test/suite/roles/none_public.test b/mysql-test/suite/roles/none_public.test
new file mode 100644
index 00000000..e88ed45c
--- /dev/null
+++ b/mysql-test/suite/roles/none_public.test
@@ -0,0 +1,75 @@
+source include/not_embedded.inc;
+
+create role role1;
+
+--error ER_INVALID_ROLE
+create role none;
+--error ER_INVALID_ROLE
+create role public;
+--error ER_CANNOT_USER
+drop role none;
+--error ER_CANNOT_USER
+drop role public;
+
+--error ER_INVALID_ROLE
+grant none to role1;
+--error ER_INVALID_ROLE
+grant role1 to none;
+--error ER_INVALID_ROLE
+grant select on *.* to none;
+--error ER_INVALID_ROLE
+grant public to role1;
+grant role1 to public;
+grant select on *.* to public;
+
+--error ER_INVALID_ROLE
+grant role1 to current_role;
+
+--error ER_INVALID_ROLE
+revoke none from role1;
+--error ER_INVALID_ROLE
+revoke role1 from none;
+--error ER_INVALID_ROLE
+revoke select on *.* from none;
+--error ER_INVALID_ROLE
+revoke public from role1;
+revoke role1 from public;
+revoke select on *.* from public;
+
+--error ER_INVALID_ROLE
+show grants for none;
+show grants for public;
+
+--error ER_INVALID_ROLE
+create definer=none view test.v1 as select 1;
+--error ER_INVALID_ROLE
+create definer=public view test.v1 as select 1;
+
+drop role role1;
+
+create user foo@localhost;
+connect foo,localhost,foo;
+--error ER_INVALID_ROLE
+set default role public;
+disconnect foo;
+
+connection default;
+update mysql.global_priv set priv=json_insert(priv, '$.default_role', 'none') where user='foo';
+connect foo,localhost,foo;
+show grants;
+select * from information_schema.enabled_roles;
+disconnect foo;
+
+connection default;
+update mysql.global_priv set priv=json_insert(priv, '$.default_role', 'public') where user='foo';
+connect foo,localhost,foo;
+show grants;
+select * from information_schema.enabled_roles;
+disconnect foo;
+
+connection default;
+drop user foo@localhost;
+
+insert mysql.global_priv values ('', 'none', '{"is_role":true}');
+flush privileges;
+delete from mysql.global_priv where host='';
diff --git a/mysql-test/suite/roles/password.result b/mysql-test/suite/roles/password.result
new file mode 100644
index 00000000..2d54db2c
--- /dev/null
+++ b/mysql-test/suite/roles/password.result
@@ -0,0 +1,35 @@
+set sql_mode='';
+create role r1;
+grant select on *.* to r1 identified by 'foobar';
+drop user r1;
+grant select on *.* to r1 identified by '';
+drop user r1;
+grant select on mysql.user to r1 identified by password '00000000000000000000000000000000000000000';
+drop user r1;
+grant select on *.* to r1 identified via plugin;
+ERROR HY000: Plugin 'plugin' is not loaded
+grant select on mysql.user to r1 identified via plugin using 'param';
+ERROR HY000: Plugin 'plugin' is not loaded
+grant select on *.* to r1 require subject 'foobar';
+drop user r1;
+grant select on mysql.user to r1 require issuer 'foobar';
+drop user r1;
+grant select on *.* to r1 require cipher 'foobar';
+drop user r1;
+grant select on mysql.user to r1 require ssl;
+drop user r1;
+grant select on *.* to r1 require x509;
+drop user r1;
+grant select on mysql.user to r1 require none;
+drop user r1;
+grant select on *.* to r1 with max_queries_per_hour 10;
+drop user r1;
+grant select on mysql.user to r1 with max_updates_per_hour 10;
+drop user r1;
+grant select on *.* to r1 with max_connections_per_hour 10;
+drop user r1;
+grant select on mysql.user to r1 with max_user_connections 10;
+drop user r1;
+set password for r1 = '00000000000000000000000000000000000000000';
+ERROR 28000: Can't find any matching row in the user table
+drop role r1;
diff --git a/mysql-test/suite/roles/password.test b/mysql-test/suite/roles/password.test
new file mode 100644
index 00000000..e5fff01d
--- /dev/null
+++ b/mysql-test/suite/roles/password.test
@@ -0,0 +1,53 @@
+#
+# setting authentication for roles
+#
+
+--source include/not_embedded.inc
+
+#identified by [password]...
+#identified with ... [using ...]
+#require [subject][issuer][cipher][ssl][x509]
+# max_queries_per_hour | max_updates_per_hour | max_connections_per_hour | max_user_connections
+#set password for ... = ...
+
+set sql_mode='';
+create role r1;
+
+# IDENTIFIED does not apply to roles, using it forces username context
+grant select on *.* to r1 identified by 'foobar';
+drop user r1;
+grant select on *.* to r1 identified by '';
+drop user r1;
+grant select on mysql.user to r1 identified by password '00000000000000000000000000000000000000000';
+drop user r1;
+--error ER_PLUGIN_IS_NOT_LOADED
+grant select on *.* to r1 identified via plugin;
+--error ER_PLUGIN_IS_NOT_LOADED
+grant select on mysql.user to r1 identified via plugin using 'param';
+
+# same for REQUIRE and mqh
+grant select on *.* to r1 require subject 'foobar';
+drop user r1;
+grant select on mysql.user to r1 require issuer 'foobar';
+drop user r1;
+grant select on *.* to r1 require cipher 'foobar';
+drop user r1;
+grant select on mysql.user to r1 require ssl;
+drop user r1;
+grant select on *.* to r1 require x509;
+drop user r1;
+grant select on mysql.user to r1 require none;
+drop user r1;
+grant select on *.* to r1 with max_queries_per_hour 10;
+drop user r1;
+grant select on mysql.user to r1 with max_updates_per_hour 10;
+drop user r1;
+grant select on *.* to r1 with max_connections_per_hour 10;
+drop user r1;
+grant select on mysql.user to r1 with max_user_connections 10;
+drop user r1;
+
+--error ER_PASSWORD_NO_MATCH
+set password for r1 = '00000000000000000000000000000000000000000';
+
+drop role r1;
diff --git a/mysql-test/suite/roles/prepare_stmt_with_role.result b/mysql-test/suite/roles/prepare_stmt_with_role.result
new file mode 100644
index 00000000..10387936
--- /dev/null
+++ b/mysql-test/suite/roles/prepare_stmt_with_role.result
@@ -0,0 +1,107 @@
+#
+# Test user to check if we can grant the created role to it.
+#
+create user test_user;
+#
+# First create the role.
+#
+SET @createRole = 'CREATE ROLE developers';
+PREPARE stmtCreateRole FROM @createRole;
+EXECUTE stmtCreateRole;
+#
+# Test to see if the role is created.
+#
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+User Host is_role
+developers Y
+SHOW GRANTS;
+Grants for root@localhost
+GRANT `developers` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+# Test reexecution.
+EXECUTE stmtCreateRole;
+ERROR HY000: Operation CREATE ROLE failed for 'developers'
+#
+# Now grant the role to the test user.
+#
+SET @grantRole = 'GRANT developers to test_user';
+PREPARE stmtGrantRole FROM @grantRole;
+EXECUTE stmtGrantRole;
+# Test reexecution.
+EXECUTE stmtGrantRole;
+#
+# We should see 2 entries in the roles_mapping table.
+#
+SELECT * FROM mysql.roles_mapping;
+Host User Role Admin_option
+% test_user developers N
+localhost root developers Y
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT `developers` TO `test_user`@`%`
+GRANT USAGE ON *.* TO `test_user`@`%`
+#
+# Test revoking a role.
+#
+SET @revokeRole = 'REVOKE developers FROM test_user';
+PREPARE stmtRevokeRole FROM @revokeRole;
+EXECUTE stmtRevokeRole;
+EXECUTE stmtRevokeRole;
+ERROR HY000: Cannot revoke role 'developers' from: 'test_user'@'%'
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT USAGE ON *.* TO `test_user`@`%`
+EXECUTE stmtGrantRole;
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT `developers` TO `test_user`@`%`
+GRANT USAGE ON *.* TO `test_user`@`%`
+EXECUTE stmtRevokeRole;
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT USAGE ON *.* TO `test_user`@`%`
+#
+# Now drop the role.
+#
+SET @dropRole = 'DROP ROLE developers';
+PREPARE stmtDropRole FROM @dropRole;
+EXECUTE stmtDropRole;
+#
+# Check both user and roles_mapping table for traces of our role.
+#
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+User Host is_role
+SELECT * FROM mysql.roles_mapping;
+Host User Role Admin_option
+SHOW GRANTS;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT USAGE ON *.* TO `test_user`@`%`
+#
+# Test reexecution.
+#
+EXECUTE stmtCreateRole;
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+User Host is_role
+developers Y
+SELECT * FROM mysql.roles_mapping;
+Host User Role Admin_option
+localhost root developers Y
+SHOW GRANTS;
+Grants for root@localhost
+GRANT `developers` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+SHOW GRANTS FOR test_user;
+Grants for test_user@%
+GRANT USAGE ON *.* TO `test_user`@`%`
+EXECUTE stmtDropRole;
+# Cleanup.
+DROP USER test_user;
diff --git a/mysql-test/suite/roles/prepare_stmt_with_role.test b/mysql-test/suite/roles/prepare_stmt_with_role.test
new file mode 100644
index 00000000..516e9dda
--- /dev/null
+++ b/mysql-test/suite/roles/prepare_stmt_with_role.test
@@ -0,0 +1,85 @@
+--source include/not_embedded.inc
+
+
+--echo #
+--echo # Test user to check if we can grant the created role to it.
+--echo #
+create user test_user;
+--echo #
+--echo # First create the role.
+--echo #
+SET @createRole = 'CREATE ROLE developers';
+PREPARE stmtCreateRole FROM @createRole;
+EXECUTE stmtCreateRole;
+--echo #
+--echo # Test to see if the role is created.
+--echo #
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+SHOW GRANTS;
+
+--echo # Test reexecution.
+--error ER_CANNOT_USER
+EXECUTE stmtCreateRole;
+
+--echo #
+--echo # Now grant the role to the test user.
+--echo #
+SET @grantRole = 'GRANT developers to test_user';
+PREPARE stmtGrantRole FROM @grantRole;
+EXECUTE stmtGrantRole;
+--echo # Test reexecution.
+EXECUTE stmtGrantRole;
+
+--echo #
+--echo # We should see 2 entries in the roles_mapping table.
+--echo #
+--sorted_result
+SELECT * FROM mysql.roles_mapping;
+SHOW GRANTS FOR test_user;
+
+--echo #
+--echo # Test revoking a role.
+--echo #
+SET @revokeRole = 'REVOKE developers FROM test_user';
+PREPARE stmtRevokeRole FROM @revokeRole;
+EXECUTE stmtRevokeRole;
+--error ER_CANNOT_REVOKE_ROLE
+EXECUTE stmtRevokeRole;
+SHOW GRANTS FOR test_user;
+
+EXECUTE stmtGrantRole;
+SHOW GRANTS FOR test_user;
+EXECUTE stmtRevokeRole;
+SHOW GRANTS FOR test_user;
+
+--echo #
+--echo # Now drop the role.
+--echo #
+SET @dropRole = 'DROP ROLE developers';
+PREPARE stmtDropRole FROM @dropRole;
+EXECUTE stmtDropRole;
+
+--echo #
+--echo # Check both user and roles_mapping table for traces of our role.
+--echo #
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+SELECT * FROM mysql.roles_mapping;
+SHOW GRANTS;
+SHOW GRANTS FOR test_user;
+
+--echo #
+--echo # Test reexecution.
+--echo #
+EXECUTE stmtCreateRole;
+SELECT user, host,is_role FROM mysql.user
+WHERE user = 'developers';
+SELECT * FROM mysql.roles_mapping;
+
+SHOW GRANTS;
+SHOW GRANTS FOR test_user;
+EXECUTE stmtDropRole;
+
+--echo # Cleanup.
+DROP USER test_user;
diff --git a/mysql-test/suite/roles/ps.result b/mysql-test/suite/roles/ps.result
new file mode 100644
index 00000000..694dcb94
--- /dev/null
+++ b/mysql-test/suite/roles/ps.result
@@ -0,0 +1 @@
+PREPARE stmt FROM 'SET ROLE NONE';
diff --git a/mysql-test/suite/roles/ps.test b/mysql-test/suite/roles/ps.test
new file mode 100644
index 00000000..f9bb6aaf
--- /dev/null
+++ b/mysql-test/suite/roles/ps.test
@@ -0,0 +1,4 @@
+#
+# MDEV-5521 SET ROLE as prepared statement crashes the server
+#
+PREPARE stmt FROM 'SET ROLE NONE';
diff --git a/mysql-test/suite/roles/rebuild_role_grants.result b/mysql-test/suite/roles/rebuild_role_grants.result
new file mode 100644
index 00000000..b8d74753
--- /dev/null
+++ b/mysql-test/suite/roles/rebuild_role_grants.result
@@ -0,0 +1,333 @@
+create role r1;
+create user u1;
+grant r1 to u1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+GRANT `r1` TO `u1`@`%`
+create user u2;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+GRANT `r1` TO `u1`@`%`
+show grants for u2;
+Grants for u2@%
+GRANT USAGE ON *.* TO `u2`@`%`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+% u1 r1 N
+localhost root r1 Y
+revoke r1 from u1;
+revoke r1 from u1;
+ERROR HY000: Cannot revoke role 'r1' from: 'u1'@'%'
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root r1 Y
+grant r1 to u1;
+grant r1 to u1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+GRANT `r1` TO `u1`@`%`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+% u1 r1 N
+localhost root r1 Y
+drop role r1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+create role r1;
+grant r1 to u1;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+% u1 r1 N
+localhost root r1 Y
+drop user u1;
+show grants for u1;
+ERROR 42000: There is no such grant defined for user 'u1' on host '%'
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root r1 Y
+drop role r1;
+drop user u2;
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+connect con1, localhost, foo,,;
+create role look, isp, xxx, ppp;
+rename user current_user to nnnn@'%';
+drop role look, isp, xxx, ppp;
+connection default;
+disconnect con1;
+drop user nnnn@'%';
+CREATE USER u@localhost;
+CREATE ROLE r1;
+CREATE ROLE r2;
+CREATE ROLE r3;
+CREATE ROLE r4;
+CREATE ROLE r5;
+CREATE ROLE r6;
+CREATE ROLE r7;
+CREATE ROLE r8;
+CREATE ROLE r9;
+CREATE ROLE r10;
+CREATE ROLE r11;
+CREATE ROLE r12;
+CREATE ROLE r13;
+CREATE ROLE r14;
+CREATE ROLE r15;
+CREATE ROLE r16;
+CREATE ROLE r17;
+CREATE ROLE r18;
+CREATE ROLE r19;
+CREATE ROLE r20;
+CREATE ROLE r21;
+CREATE ROLE r22;
+CREATE ROLE r23;
+CREATE ROLE r24;
+CREATE ROLE r25;
+CREATE ROLE r26;
+CREATE ROLE r27;
+CREATE ROLE r28;
+CREATE ROLE r29;
+CREATE ROLE r30;
+CREATE ROLE r31;
+CREATE ROLE r32;
+CREATE ROLE r33;
+CREATE ROLE r34;
+CREATE ROLE r35;
+CREATE ROLE r36;
+CREATE ROLE r37;
+CREATE ROLE r38;
+CREATE ROLE r39;
+CREATE ROLE r40;
+CREATE ROLE r41;
+CREATE ROLE r42;
+CREATE ROLE r43;
+CREATE ROLE r44;
+CREATE ROLE r45;
+CREATE ROLE r46;
+CREATE ROLE r47;
+CREATE ROLE r48;
+CREATE ROLE r49;
+CREATE ROLE r50;
+CREATE ROLE r51;
+CREATE ROLE r52;
+CREATE ROLE r53;
+CREATE ROLE r54;
+CREATE ROLE r55;
+CREATE ROLE r56;
+CREATE ROLE r57;
+CREATE ROLE r58;
+CREATE ROLE r59;
+CREATE ROLE r60;
+CREATE ROLE r61;
+CREATE ROLE r62;
+CREATE ROLE r63;
+CREATE ROLE r64;
+CREATE ROLE r65;
+CREATE ROLE r66;
+CREATE ROLE r67;
+CREATE ROLE r68;
+CREATE ROLE r69;
+CREATE ROLE r70;
+CREATE ROLE r71;
+CREATE ROLE r72;
+CREATE ROLE r73;
+CREATE ROLE r74;
+CREATE ROLE r75;
+CREATE ROLE r76;
+CREATE ROLE r77;
+CREATE ROLE r78;
+CREATE ROLE r79;
+CREATE ROLE r80;
+CREATE ROLE r81;
+CREATE ROLE r82;
+CREATE ROLE r83;
+CREATE ROLE r84;
+CREATE ROLE r85;
+CREATE ROLE r86;
+CREATE ROLE r87;
+CREATE ROLE r88;
+CREATE ROLE r89;
+CREATE ROLE r90;
+CREATE ROLE r91;
+CREATE ROLE r92;
+CREATE ROLE r93;
+CREATE ROLE r94;
+CREATE ROLE r95;
+CREATE ROLE r96;
+CREATE ROLE r97;
+CREATE ROLE r98;
+CREATE ROLE r99;
+CREATE ROLE r100;
+CREATE ROLE r101;
+CREATE ROLE r102;
+CREATE ROLE r103;
+CREATE ROLE r104;
+CREATE ROLE r105;
+CREATE ROLE r106;
+CREATE ROLE r107;
+CREATE ROLE r108;
+CREATE ROLE r109;
+CREATE ROLE r110;
+CREATE ROLE r111;
+CREATE ROLE r112;
+CREATE ROLE r113;
+CREATE ROLE r114;
+CREATE ROLE r115;
+CREATE ROLE r116;
+CREATE ROLE r117;
+CREATE ROLE r118;
+CREATE ROLE r119;
+CREATE ROLE r120;
+CREATE ROLE r121;
+CREATE ROLE r122;
+CREATE ROLE r123;
+CREATE ROLE r124;
+CREATE ROLE r125;
+CREATE ROLE r126;
+CREATE ROLE r127;
+CREATE ROLE r128;
+CREATE ROLE n;
+CREATE ROLE d WITH ADMIN n;
+CREATE ROLE '%' WITH ADMIN u@localhost;
+DROP ROLE n;
+CREATE USER 't';
+DROP ROLE r1;
+DROP ROLE r2;
+DROP ROLE r3;
+DROP ROLE r4;
+DROP ROLE r5;
+DROP ROLE r6;
+DROP ROLE r7;
+DROP ROLE r8;
+DROP ROLE r9;
+DROP ROLE r10;
+DROP ROLE r11;
+DROP ROLE r12;
+DROP ROLE r13;
+DROP ROLE r14;
+DROP ROLE r15;
+DROP ROLE r16;
+DROP ROLE r17;
+DROP ROLE r18;
+DROP ROLE r19;
+DROP ROLE r20;
+DROP ROLE r21;
+DROP ROLE r22;
+DROP ROLE r23;
+DROP ROLE r24;
+DROP ROLE r25;
+DROP ROLE r26;
+DROP ROLE r27;
+DROP ROLE r28;
+DROP ROLE r29;
+DROP ROLE r30;
+DROP ROLE r31;
+DROP ROLE r32;
+DROP ROLE r33;
+DROP ROLE r34;
+DROP ROLE r35;
+DROP ROLE r36;
+DROP ROLE r37;
+DROP ROLE r38;
+DROP ROLE r39;
+DROP ROLE r40;
+DROP ROLE r41;
+DROP ROLE r42;
+DROP ROLE r43;
+DROP ROLE r44;
+DROP ROLE r45;
+DROP ROLE r46;
+DROP ROLE r47;
+DROP ROLE r48;
+DROP ROLE r49;
+DROP ROLE r50;
+DROP ROLE r51;
+DROP ROLE r52;
+DROP ROLE r53;
+DROP ROLE r54;
+DROP ROLE r55;
+DROP ROLE r56;
+DROP ROLE r57;
+DROP ROLE r58;
+DROP ROLE r59;
+DROP ROLE r60;
+DROP ROLE r61;
+DROP ROLE r62;
+DROP ROLE r63;
+DROP ROLE r64;
+DROP ROLE r65;
+DROP ROLE r66;
+DROP ROLE r67;
+DROP ROLE r68;
+DROP ROLE r69;
+DROP ROLE r70;
+DROP ROLE r71;
+DROP ROLE r72;
+DROP ROLE r73;
+DROP ROLE r74;
+DROP ROLE r75;
+DROP ROLE r76;
+DROP ROLE r77;
+DROP ROLE r78;
+DROP ROLE r79;
+DROP ROLE r80;
+DROP ROLE r81;
+DROP ROLE r82;
+DROP ROLE r83;
+DROP ROLE r84;
+DROP ROLE r85;
+DROP ROLE r86;
+DROP ROLE r87;
+DROP ROLE r88;
+DROP ROLE r89;
+DROP ROLE r90;
+DROP ROLE r91;
+DROP ROLE r92;
+DROP ROLE r93;
+DROP ROLE r94;
+DROP ROLE r95;
+DROP ROLE r96;
+DROP ROLE r97;
+DROP ROLE r98;
+DROP ROLE r99;
+DROP ROLE r100;
+DROP ROLE r101;
+DROP ROLE r102;
+DROP ROLE r103;
+DROP ROLE r104;
+DROP ROLE r105;
+DROP ROLE r106;
+DROP ROLE r107;
+DROP ROLE r108;
+DROP ROLE r109;
+DROP ROLE r110;
+DROP ROLE r111;
+DROP ROLE r112;
+DROP ROLE r113;
+DROP ROLE r114;
+DROP ROLE r115;
+DROP ROLE r116;
+DROP ROLE r117;
+DROP ROLE r118;
+DROP ROLE r119;
+DROP ROLE r120;
+DROP ROLE r121;
+DROP ROLE r122;
+DROP ROLE r123;
+DROP ROLE r124;
+DROP ROLE r125;
+DROP ROLE r126;
+DROP ROLE r127;
+DROP ROLE r128;
+DROP ROLE d;
+DROP ROLE '%';
+DROP USER 't';
+DROP USER u@localhost;
diff --git a/mysql-test/suite/roles/rebuild_role_grants.test b/mysql-test/suite/roles/rebuild_role_grants.test
new file mode 100644
index 00000000..7007df0e
--- /dev/null
+++ b/mysql-test/suite/roles/rebuild_role_grants.test
@@ -0,0 +1,100 @@
+source include/not_embedded.inc;
+
+create role r1;
+create user u1;
+grant r1 to u1;
+
+#CHECK IF GRANTS ARE UPDATED ON GRANT
+--sorted_result
+show grants for u1;
+
+create user u2;
+
+#CHECK THAT GRANTS ARE UPDATED ON ACL_USERS CHANGE
+--sorted_result
+show grants for u1;
+--sorted_result
+show grants for u2;
+--sorted_result
+select * from mysql.roles_mapping;
+
+revoke r1 from u1;
+#TEST ERROR MESSAGE
+--error ER_CANNOT_REVOKE_ROLE
+revoke r1 from u1;
+--sorted_result
+show grants for u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+# granting twice is ok
+grant r1 to u1;
+grant r1 to u1;
+--sorted_result
+show grants for u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+drop role r1;
+--sorted_result
+show grants for u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+create role r1;
+grant r1 to u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+drop user u1;
+--error ER_NONEXISTING_GRANT
+show grants for u1;
+--sorted_result
+select * from mysql.roles_mapping;
+
+drop role r1;
+drop user u2;
+
+#
+# MDEV-8614 Assertion `status == 0' failed in add_role_user_mapping_action on RENAME USER
+#
+create user foo@localhost;
+grant create user on *.* to foo@localhost;
+--connect (con1, localhost, foo,,)
+create role look, isp, xxx, ppp;
+rename user current_user to nnnn@'%';
+drop role look, isp, xxx, ppp;
+connection default;
+disconnect con1;
+drop user nnnn@'%';
+
+#
+# MDEV-17964 Assertion `status == 0' failed in add_role_user_mapping_action
+# upon CREATE USER and DROP ROLE
+#
+CREATE USER u@localhost;
+
+--let $n= 1
+while ($n < 129)
+{
+ eval CREATE ROLE r$n;
+ inc $n;
+}
+
+CREATE ROLE n;
+CREATE ROLE d WITH ADMIN n;
+CREATE ROLE '%' WITH ADMIN u@localhost;
+DROP ROLE n;
+CREATE USER 't';
+
+--let $n= 1
+while ($n < 129)
+{
+ eval DROP ROLE r$n;
+ inc $n;
+}
+
+DROP ROLE d;
+DROP ROLE '%';
+DROP USER 't';
+DROP USER u@localhost;
diff --git a/mysql-test/suite/roles/recursive.inc b/mysql-test/suite/roles/recursive.inc
new file mode 100644
index 00000000..7642f2d6
--- /dev/null
+++ b/mysql-test/suite/roles/recursive.inc
@@ -0,0 +1,259 @@
+#
+# This file tests how privilege are propagated through a complex role graph.
+# Here's a graph
+#
+# role1 ->- role2 -->- role4 -->- role6 ->- role8
+# \ / \
+# \->- role5 ->-/ \->- role9 ->- role10 ->- foo@localhost
+# / \ /
+# role3 ->-/ \->- role7 ->-/
+#
+# privilege checks verify that grants/revokes are propagated correctly
+# from the role1 to role10. additionally debug status variables verify
+# that propagation doesn't do unnecessary work (only touches the
+# smallest possible number of nodes and doesn't merge privileges that
+# didn't change)
+#
+source include/not_embedded.inc;
+
+create user foo@localhost;
+grant select on test.* to foo@localhost;
+create role role1;
+create role role2;
+create role role3;
+create role role4;
+create role role5;
+create role role6;
+create role role7;
+create role role8;
+create role role9;
+create role role10;
+
+grant role1 to role2;
+grant role2 to role4;
+grant role2 to role5;
+grant role3 to role5;
+grant role4 to role6;
+grant role5 to role6;
+grant role5 to role7;
+grant role6 to role8;
+grant role6 to role9;
+grant role7 to role9;
+grant role9 to role10;
+grant role10 to foo@localhost;
+
+# try to create a cycle
+--error ER_CANNOT_GRANT_ROLE
+grant role10 to role2;
+
+connect (foo, localhost, foo);
+--sorted_result
+show grants;
+--sorted_result
+select * from information_schema.applicable_roles;
+
+show status like 'debug%';
+
+#
+# global privileges
+#
+connection default;
+grant select on *.* to role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role role10;
+select count(*) from mysql.roles_mapping;
+--sorted_result
+show grants;
+--sorted_result
+select * from information_schema.enabled_roles;
+
+connection default;
+revoke select on *.* from role1;
+show status like 'debug%';
+connection foo;
+# global privileges are cached in the THD, changes don't take effect immediately
+select count(*) from mysql.roles_mapping;
+set role none;
+set role role10;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role none;
+
+#
+# database privileges
+#
+connection default;
+grant select on mysql.* to role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role role10;
+select count(*) from mysql.roles_mapping;
+--sorted_result
+show grants;
+
+connection default;
+revoke select on mysql.* from role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role none;
+
+#
+# table privileges
+#
+
+connection default;
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role role10;
+select count(*) from mysql.roles_mapping;
+--sorted_result
+show grants;
+
+connection default;
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role none;
+
+#
+# column privileges
+#
+
+connection default;
+grant select(User) on mysql.roles_mapping to role1;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(*) from mysql.roles_mapping;
+set role role10;
+--error ER_COLUMNACCESS_DENIED_ERROR
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+select count(concat(User)) from mysql.roles_mapping;
+--sorted_result
+show grants;
+
+connection default;
+grant select(Host) on mysql.roles_mapping to role3;
+show status like 'debug%';
+connection foo;
+--error ER_COLUMNACCESS_DENIED_ERROR
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+select count(concat(User,Host)) from mysql.roles_mapping;
+--sorted_result
+show grants;
+
+connection default;
+revoke select(User) on mysql.roles_mapping from role1;
+show status like 'debug%';
+connection foo;
+--error ER_COLUMNACCESS_DENIED_ERROR
+select count(concat(User,Host)) from mysql.roles_mapping;
+select count(concat(Host)) from mysql.roles_mapping;
+
+connection default;
+revoke select(Host) on mysql.roles_mapping from role3;
+show status like 'debug%';
+connection foo;
+--error ER_TABLEACCESS_DENIED_ERROR
+select count(concat(Host)) from mysql.roles_mapping;
+set role none;
+
+#
+# routine privileges
+#
+
+connection default;
+create procedure pr1() select "pr1";
+create function fn1() returns char(10) return "fn1";
+grant execute on procedure test.pr1 to role1;
+show status like 'debug%';
+connection foo;
+--error ER_PROCACCESS_DENIED_ERROR
+call pr1();
+set role role10;
+call pr1();
+--error ER_PROCACCESS_DENIED_ERROR
+select fn1();
+
+connection default;
+grant execute on function test.fn1 to role5;
+show status like 'debug%';
+connection foo;
+select fn1();
+
+connection default;
+revoke execute on procedure test.pr1 from role1;
+show status like 'debug%';
+connection foo;
+--error ER_PROCACCESS_DENIED_ERROR
+call pr1();
+select fn1();
+
+connection default;
+revoke execute on function test.fn1 from role5;
+show status like 'debug%';
+connection foo;
+--error ER_PROCACCESS_DENIED_ERROR
+select fn1();
+set role none;
+
+connection default;
+drop procedure pr1;
+drop function fn1;
+
+#
+# test shortcuts
+#
+
+grant select on mysql.roles_mapping to role3;
+show status like 'debug%';
+# this grant only propagates to roles role2 and role4,
+# and tries to propagate to role5, discovering that it already has it
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+# this only tries to propagate to role5 and exits early
+revoke select on mysql.roles_mapping from role3;
+show status like 'debug%';
+# propagates to all 8 roles, normally
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+
+grant select on mysql.* to role1;
+show status like 'debug%';
+# only entries for `test` are merged, not for `mysql`
+grant select on test.* to role1;
+show status like 'debug%';
+revoke select on mysql.* from role1;
+show status like 'debug%';
+revoke select on test.* from role1;
+show status like 'debug%';
+
+#
+# cleanup
+#
+
+connection default;
+drop user foo@localhost;
+drop role role1;
+drop role role2;
+drop role role3;
+drop role role4;
+drop role role5;
+drop role role6;
+drop role role7;
+drop role role8;
+drop role role9;
+drop role role10;
+
diff --git a/mysql-test/suite/roles/recursive.result b/mysql-test/suite/roles/recursive.result
new file mode 100644
index 00000000..0706b0a5
--- /dev/null
+++ b/mysql-test/suite/roles/recursive.result
@@ -0,0 +1,366 @@
+create user foo@localhost;
+grant select on test.* to foo@localhost;
+create role role1;
+create role role2;
+create role role3;
+create role role4;
+create role role5;
+create role role6;
+create role role7;
+create role role8;
+create role role9;
+create role role10;
+grant role1 to role2;
+grant role2 to role4;
+grant role2 to role5;
+grant role3 to role5;
+grant role4 to role6;
+grant role5 to role6;
+grant role5 to role7;
+grant role6 to role8;
+grant role6 to role9;
+grant role7 to role9;
+grant role9 to role10;
+grant role10 to foo@localhost;
+grant role10 to role2;
+ERROR HY000: Cannot grant role 'role10' to: 'role2'
+connect foo, localhost, foo;
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT `role10` TO `foo`@`localhost`
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@localhost role10 NO NO
+role10 role9 NO NULL
+role2 role1 NO NULL
+role4 role2 NO NULL
+role5 role2 NO NULL
+role5 role3 NO NULL
+role6 role4 NO NULL
+role6 role5 NO NULL
+role7 role5 NO NULL
+role9 role6 NO NULL
+role9 role7 NO NULL
+show status like 'debug%';
+Variable_name Value
+connection default;
+grant select on *.* to role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON *.* TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+role1
+role10
+role2
+role3
+role4
+role5
+role6
+role7
+role9
+connection default;
+revoke select on *.* from role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+set role none;
+set role role10;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `mysql`.* TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select(User) on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'Host' in table 'roles_mapping'
+select count(concat(User)) from mysql.roles_mapping;
+count(concat(User))
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT (`User`) ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+grant select(Host) on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'Role' in table 'roles_mapping'
+select count(concat(User,Host)) from mysql.roles_mapping;
+count(concat(User,Host))
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT (`Host`) ON `mysql`.`roles_mapping` TO `role3`
+GRANT SELECT (`User`) ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select(User) on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(concat(User,Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'User' in table 'roles_mapping'
+select count(concat(Host)) from mysql.roles_mapping;
+count(concat(Host))
+22
+connection default;
+revoke select(Host) on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select count(concat(Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+create procedure pr1() select "pr1";
+create function fn1() returns char(10) return "fn1";
+grant execute on procedure test.pr1 to role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+set role role10;
+call pr1();
+pr1
+pr1
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+connection default;
+grant execute on function test.fn1 to role5;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select fn1();
+fn1()
+fn1
+connection default;
+revoke execute on procedure test.pr1 from role1;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+select fn1();
+fn1()
+fn1
+connection default;
+revoke execute on function test.fn1 from role5;
+show status like 'debug%';
+Variable_name Value
+connection foo;
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+set role none;
+connection default;
+drop procedure pr1;
+drop function fn1;
+grant select on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+revoke select on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+grant select on test.* to role1;
+show status like 'debug%';
+Variable_name Value
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+revoke select on test.* from role1;
+show status like 'debug%';
+Variable_name Value
+connection default;
+drop user foo@localhost;
+drop role role1;
+drop role role2;
+drop role role3;
+drop role role4;
+drop role role5;
+drop role role6;
+drop role role7;
+drop role role8;
+drop role role9;
+drop role role10;
diff --git a/mysql-test/suite/roles/recursive.test b/mysql-test/suite/roles/recursive.test
new file mode 100644
index 00000000..0858f868
--- /dev/null
+++ b/mysql-test/suite/roles/recursive.test
@@ -0,0 +1,4 @@
+
+source include/not_debug.inc;
+source recursive.inc;
+
diff --git a/mysql-test/suite/roles/recursive_dbug.result b/mysql-test/suite/roles/recursive_dbug.result
new file mode 100644
index 00000000..f6abc406
--- /dev/null
+++ b/mysql-test/suite/roles/recursive_dbug.result
@@ -0,0 +1,486 @@
+show status like 'debug%';
+Variable_name Value
+set @old_dbug=@@global.debug_dbug;
+set global debug_dbug="+d,role_merge_stats";
+create user foo@localhost;
+grant select on test.* to foo@localhost;
+create role role1;
+create role role2;
+create role role3;
+create role role4;
+create role role5;
+create role role6;
+create role role7;
+create role role8;
+create role role9;
+create role role10;
+grant role1 to role2;
+grant role2 to role4;
+grant role2 to role5;
+grant role3 to role5;
+grant role4 to role6;
+grant role5 to role6;
+grant role5 to role7;
+grant role6 to role8;
+grant role6 to role9;
+grant role7 to role9;
+grant role9 to role10;
+grant role10 to foo@localhost;
+grant role10 to role2;
+ERROR HY000: Cannot grant role 'role10' to: 'role2'
+connect foo, localhost, foo;
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT `role10` TO `foo`@`localhost`
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+foo@localhost role10 NO NO
+role10 role9 NO NULL
+role2 role1 NO NULL
+role4 role2 NO NULL
+role5 role2 NO NULL
+role5 role3 NO NULL
+role6 role4 NO NULL
+role6 role5 NO NULL
+role7 role5 NO NULL
+role9 role6 NO NULL
+role9 role7 NO NULL
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 11
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection default;
+grant select on *.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 20
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON *.* TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+role1
+role10
+role2
+role3
+role4
+role5
+role6
+role7
+role9
+connection default;
+revoke select on *.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+set role none;
+set role role10;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 9
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `mysql`.* TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 9
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(*) from mysql.roles_mapping;
+count(*)
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 17
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+grant select(User) on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 26
+Debug_role_merges_column 9
+Debug_role_merges_routine 0
+connection foo;
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role role10;
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'Host' in table 'roles_mapping'
+select count(concat(User)) from mysql.roles_mapping;
+count(concat(User))
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT (`User`) ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+grant select(Host) on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 33
+Debug_role_merges_column 16
+Debug_role_merges_routine 0
+connection foo;
+select count(concat(User,Host,Role)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'Role' in table 'roles_mapping'
+select count(concat(User,Host)) from mysql.roles_mapping;
+count(concat(User,Host))
+22
+show grants;
+Grants for foo@localhost
+GRANT SELECT (`Host`) ON `mysql`.`roles_mapping` TO `role3`
+GRANT SELECT (`User`) ON `mysql`.`roles_mapping` TO `role1`
+GRANT SELECT ON `test`.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role10`
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT USAGE ON *.* TO `role3`
+GRANT USAGE ON *.* TO `role4`
+GRANT USAGE ON *.* TO `role5`
+GRANT USAGE ON *.* TO `role6`
+GRANT USAGE ON *.* TO `role7`
+GRANT USAGE ON *.* TO `role9`
+GRANT `role10` TO `foo`@`localhost`
+GRANT `role1` TO `role2`
+GRANT `role2` TO `role4`
+GRANT `role2` TO `role5`
+GRANT `role3` TO `role5`
+GRANT `role4` TO `role6`
+GRANT `role5` TO `role6`
+GRANT `role5` TO `role7`
+GRANT `role6` TO `role9`
+GRANT `role7` TO `role9`
+GRANT `role9` TO `role10`
+connection default;
+revoke select(User) on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 41
+Debug_role_merges_column 24
+Debug_role_merges_routine 0
+connection foo;
+select count(concat(User,Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'User' in table 'roles_mapping'
+select count(concat(Host)) from mysql.roles_mapping;
+count(concat(Host))
+22
+connection default;
+revoke select(Host) on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 47
+Debug_role_merges_column 30
+Debug_role_merges_routine 0
+connection foo;
+select count(concat(Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+set role none;
+connection default;
+create procedure pr1() select "pr1";
+create function fn1() returns char(10) return "fn1";
+grant execute on procedure test.pr1 to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 47
+Debug_role_merges_column 30
+Debug_role_merges_routine 9
+connection foo;
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+set role role10;
+call pr1();
+pr1
+pr1
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+connection default;
+grant execute on function test.fn1 to role5;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 47
+Debug_role_merges_column 30
+Debug_role_merges_routine 15
+connection foo;
+select fn1();
+fn1()
+fn1
+connection default;
+revoke execute on procedure test.pr1 from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 47
+Debug_role_merges_column 30
+Debug_role_merges_routine 23
+connection foo;
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+select fn1();
+fn1()
+fn1
+connection default;
+revoke execute on function test.fn1 from role5;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 47
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+connection foo;
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+set role none;
+connection default;
+drop procedure pr1;
+drop function fn1;
+grant select on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 54
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 58
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+revoke select on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 59
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 17
+Debug_role_merges_table 67
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 26
+Debug_role_merges_table 67
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+grant select on test.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 35
+Debug_role_merges_table 67
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 43
+Debug_role_merges_table 67
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+revoke select on test.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 29
+Debug_role_merges_db 51
+Debug_role_merges_table 67
+Debug_role_merges_column 30
+Debug_role_merges_routine 28
+connection default;
+drop user foo@localhost;
+drop role role1;
+drop role role2;
+drop role role3;
+drop role role4;
+drop role role5;
+drop role role6;
+drop role role7;
+drop role role8;
+drop role role9;
+drop role role10;
+set global debug_dbug=@old_dbug;
diff --git a/mysql-test/suite/roles/recursive_dbug.test b/mysql-test/suite/roles/recursive_dbug.test
new file mode 100644
index 00000000..81169599
--- /dev/null
+++ b/mysql-test/suite/roles/recursive_dbug.test
@@ -0,0 +1,14 @@
+#
+# run acl_roles_recursive and count the number of merges
+#
+source include/have_debug.inc;
+
+show status like 'debug%';
+
+set @old_dbug=@@global.debug_dbug;
+set global debug_dbug="+d,role_merge_stats";
+
+source recursive.inc;
+
+set global debug_dbug=@old_dbug;
+
diff --git a/mysql-test/suite/roles/rename_user.result b/mysql-test/suite/roles/rename_user.result
new file mode 100644
index 00000000..367f6e4b
--- /dev/null
+++ b/mysql-test/suite/roles/rename_user.result
@@ -0,0 +1,36 @@
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+create role test_role2;
+grant test_role2 to test_role1;
+use mysql;
+select * from roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost';
+select user, host from user where user like 'test%';
+User Host
+test_role1
+test_role2
+test_user_rm newhost
+select * from roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+newhost test_user_rm test_role1 N
+delete from mysql.roles_mapping;
+delete from mysql.user where user like 'test%';
+flush privileges;
+#
+# MDEV-29131 Assertion `status == 0' failed when renaming user after deleting table roles_mapping
+#
+create role r;
+rename table mysql.roles_mapping to test.t1;
+rename user current_user to a@a;
+rename user a@a to root@localhost;
+rename table test.t1 to mysql.roles_mapping;
+drop role r;
diff --git a/mysql-test/suite/roles/rename_user.test b/mysql-test/suite/roles/rename_user.test
new file mode 100644
index 00000000..8c899352
--- /dev/null
+++ b/mysql-test/suite/roles/rename_user.test
@@ -0,0 +1,48 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+create role test_role2;
+grant test_role2 to test_role1;
+
+use mysql;
+--sorted_result
+select * from roles_mapping;
+
+#regular user rename
+rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost';
+--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;
+
+--echo #
+--echo # MDEV-29131 Assertion `status == 0' failed when renaming user after deleting table roles_mapping
+--echo #
+create role r;
+rename table mysql.roles_mapping to test.t1;
+rename user current_user to a@a;
+rename user a@a to root@localhost;
+rename table test.t1 to mysql.roles_mapping;
+drop role r;
+
diff --git a/mysql-test/suite/roles/revoke_all.result b/mysql-test/suite/roles/revoke_all.result
new file mode 100644
index 00000000..864cc57a
--- /dev/null
+++ b/mysql-test/suite/roles/revoke_all.result
@@ -0,0 +1,183 @@
+create role r1;
+create role r2;
+create role r3;
+create role r4;
+create user u1;
+grant r2 to r1;
+grant r3 to r2;
+grant r4 to r3;
+grant r1 to u1;
+grant r4 to r1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+GRANT `r1` TO `u1`@`%`
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r1`
+GRANT `r4` TO `r3`
+grant SELECT on *.* to u1;
+grant INSERT on mysql.* to r1;
+grant DELETE on mysql.roles_mapping to r2;
+grant UPDATE on mysql.user to r3;
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+grant execute on function mysql.test_func to r2;
+grant execute on procedure mysql.test_proc to r3;
+grant execute on mysql.* to r4;
+show grants for r1;
+Grants for r1
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT EXECUTE ON `mysql`.* TO `r4`
+GRANT INSERT ON `mysql`.* TO `r1`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r1`
+GRANT `r4` TO `r3`
+show grants for r2;
+Grants for r2
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT EXECUTE ON `mysql`.* TO `r4`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r3`
+show grants for r3;
+Grants for r3
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT EXECUTE ON `mysql`.* TO `r4`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r4` TO `r3`
+show grants for r4;
+Grants for r4
+GRANT EXECUTE ON `mysql`.* TO `r4`
+GRANT USAGE ON *.* TO `r4`
+revoke all privileges, grant option from r4;
+show grants for r1;
+Grants for r1
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT INSERT ON `mysql`.* TO `r1`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r1`
+GRANT `r4` TO `r3`
+show grants for r2;
+Grants for r2
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r3`
+show grants for r3;
+Grants for r3
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `r3`
+GRANT UPDATE ON `mysql`.`user` TO `r3`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r4` TO `r3`
+show grants for r4;
+Grants for r4
+GRANT USAGE ON *.* TO `r4`
+revoke all privileges, grant option from r3;
+show grants for r1;
+Grants for r1
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT INSERT ON `mysql`.* TO `r1`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r2` TO `r1`
+GRANT `r3` TO `r2`
+GRANT `r4` TO `r1`
+show grants for r2;
+Grants for r2
+GRANT DELETE ON `mysql`.`roles_mapping` TO `r2`
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `r2`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r3`
+GRANT `r3` TO `r2`
+show grants for r3;
+Grants for r3
+GRANT USAGE ON *.* TO `r3`
+show grants for r4;
+Grants for r4
+GRANT USAGE ON *.* TO `r4`
+revoke all privileges, grant option from r2;
+show grants for r1;
+Grants for r1
+GRANT INSERT ON `mysql`.* TO `r1`
+GRANT USAGE ON *.* TO `r1`
+GRANT USAGE ON *.* TO `r2`
+GRANT USAGE ON *.* TO `r4`
+GRANT `r2` TO `r1`
+GRANT `r4` TO `r1`
+show grants for r2;
+Grants for r2
+GRANT USAGE ON *.* TO `r2`
+show grants for r3;
+Grants for r3
+GRANT USAGE ON *.* TO `r3`
+show grants for r4;
+Grants for r4
+GRANT USAGE ON *.* TO `r4`
+revoke all privileges, grant option from r1;
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+show grants for r2;
+Grants for r2
+GRANT USAGE ON *.* TO `r2`
+show grants for r3;
+Grants for r3
+GRANT USAGE ON *.* TO `r3`
+show grants for r4;
+Grants for r4
+GRANT USAGE ON *.* TO `r4`
+revoke all privileges, grant option from u1;
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+drop role r1, r2, r3, r4;
+drop user u1;
diff --git a/mysql-test/suite/roles/revoke_all.test b/mysql-test/suite/roles/revoke_all.test
new file mode 100644
index 00000000..a3dee981
--- /dev/null
+++ b/mysql-test/suite/roles/revoke_all.test
@@ -0,0 +1,103 @@
+source include/not_embedded.inc;
+
+create role r1;
+create role r2;
+create role r3;
+create role r4;
+create user u1;
+
+#CREATE A CHAIN OF ROLES
+grant r2 to r1;
+grant r3 to r2;
+grant r4 to r3;
+grant r1 to u1;
+grant r4 to r1;
+
+--sorted_result
+show grants for u1;
+--sorted_result
+show grants for r1;
+
+grant SELECT on *.* to u1;
+grant INSERT on mysql.* to r1;
+grant DELETE on mysql.roles_mapping to r2;
+grant UPDATE on mysql.user to r3;
+
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+
+
+delimiter |;
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+ select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+delimiter ;|
+
+grant execute on function mysql.test_func to r2;
+grant execute on procedure mysql.test_proc to r3;
+grant execute on mysql.* to r4;
+
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+--sorted_result
+show grants for r4;
+
+revoke all privileges, grant option from r4;
+
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+--sorted_result
+show grants for r4;
+
+revoke all privileges, grant option from r3;
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+--sorted_result
+show grants for r4;
+
+revoke all privileges, grant option from r2;
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+--sorted_result
+show grants for r4;
+
+revoke all privileges, grant option from r1;
+--sorted_result
+show grants for r1;
+--sorted_result
+show grants for r2;
+--sorted_result
+show grants for r3;
+--sorted_result
+show grants for r4;
+
+revoke all privileges, grant option from u1;
+
+show grants for u1;
+
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+
+--sorted_result
+show grants for r1;
+
+drop role r1, r2, r3, r4;
+drop user u1;
diff --git a/mysql-test/suite/roles/role_case_sensitive-10744.result b/mysql-test/suite/roles/role_case_sensitive-10744.result
new file mode 100644
index 00000000..8cb45b13
--- /dev/null
+++ b/mysql-test/suite/roles/role_case_sensitive-10744.result
@@ -0,0 +1,60 @@
+#
+# MDEV-10744 Roles are not fully case-sensitive
+#
+#
+# Test creating two case-different roles.
+#
+create user test_user@'%';
+create role test_ROLE;
+create role test_role;
+#
+# Test if mysql.user has the roles created.
+#
+select user, host from mysql.user where is_role='y' and user like 'test%';
+User Host
+test_ROLE
+test_role
+create database secret_db;
+create table secret_db.t1 (secret varchar(100));
+insert into secret_db.t1 values ("Some Secret P4ssw0rd");
+grant select on secret_db.* to test_role;
+grant test_role to test_user;
+show grants for test_user;
+Grants for test_user@%
+GRANT `test_role` TO `test_user`@`%`
+GRANT USAGE ON *.* TO `test_user`@`%`
+#
+# Now test the UPPER case role.
+#
+grant test_ROLE to test_user;
+grant insert on secret_db.t1 to test_ROLE;
+show grants for test_user;
+Grants for test_user@%
+GRANT `test_role` TO `test_user`@`%`
+GRANT `test_ROLE` TO `test_user`@`%`
+GRANT USAGE ON *.* TO `test_user`@`%`
+connect test_user,localhost,test_user;
+#
+# Test users privileges when interacting with those roles;
+#
+show tables from secret_db;
+ERROR 42000: Access denied for user 'test_user'@'%' to database 'secret_db'
+set role test_ROLE;
+show tables from secret_db;
+Tables_in_secret_db
+t1
+select * from secret_db.t1;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `secret_db`.`t1`
+insert into secret_db.t1 values ("|-|4><");
+set role test_role;
+select * from secret_db.t1 order by secret;
+secret
+Some Secret P4ssw0rd
+|-|4><
+insert into secret_db.t1 values ("|_33T|-|4><");
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table `secret_db`.`t1`
+connection default;
+drop role test_ROLE;
+drop role test_role;
+drop user test_user;
+drop database secret_db;
diff --git a/mysql-test/suite/roles/role_case_sensitive-10744.test b/mysql-test/suite/roles/role_case_sensitive-10744.test
new file mode 100644
index 00000000..281d61bc
--- /dev/null
+++ b/mysql-test/suite/roles/role_case_sensitive-10744.test
@@ -0,0 +1,54 @@
+--source include/not_embedded.inc
+--echo #
+--echo # MDEV-10744 Roles are not fully case-sensitive
+--echo #
+
+--echo #
+--echo # Test creating two case-different roles.
+--echo #
+create user test_user@'%';
+create role test_ROLE;
+create role test_role;
+--echo #
+--echo # Test if mysql.user has the roles created.
+--echo #
+--sorted_result
+select user, host from mysql.user where is_role='y' and user like 'test%';
+
+create database secret_db;
+create table secret_db.t1 (secret varchar(100));
+insert into secret_db.t1 values ("Some Secret P4ssw0rd");
+
+grant select on secret_db.* to test_role;
+grant test_role to test_user;
+show grants for test_user;
+--echo #
+--echo # Now test the UPPER case role.
+--echo #
+grant test_ROLE to test_user;
+grant insert on secret_db.t1 to test_ROLE;
+show grants for test_user;
+connect (test_user,localhost,test_user);
+
+--echo #
+--echo # Test users privileges when interacting with those roles;
+--echo #
+--error ER_DBACCESS_DENIED_ERROR
+show tables from secret_db;
+set role test_ROLE;
+show tables from secret_db;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from secret_db.t1;
+insert into secret_db.t1 values ("|-|4><");
+set role test_role;
+select * from secret_db.t1 order by secret;
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into secret_db.t1 values ("|_33T|-|4><");
+
+connection default;
+
+
+drop role test_ROLE;
+drop role test_role;
+drop user test_user;
+drop database secret_db;
diff --git a/mysql-test/suite/roles/role_grant_propagate.result b/mysql-test/suite/roles/role_grant_propagate.result
new file mode 100644
index 00000000..111fd4db
--- /dev/null
+++ b/mysql-test/suite/roles/role_grant_propagate.result
@@ -0,0 +1,180 @@
+#
+# MDEV-29458 Role grant commands do not propagate all grants
+#
+create user foo;
+create database some_db;
+create table some_db.t1 (a int, b int, secret int);
+CREATE PROCEDURE some_db.p1 (OUT param1 INT)
+BEGIN
+SELECT COUNT(*) INTO param1 FROM some_db.t1;
+END;
+//
+CREATE FUNCTION some_db.f1 (param1 INT) RETURNS INT
+BEGIN
+DECLARE c INT;
+SET c = 100;
+RETURN param1 + c;
+END;
+//
+#
+# These roles will form a two level hierarchy.
+# The "select" role will have the select privilege, while
+# the active role will inherit the select role.
+#
+# The active role will be granted a different privilege but on the same
+# level (global, database, table, proc respectively) *after* the 'select'
+# role has been granted its select privilege.
+#
+create role r_select_global;
+create role r_active_global;
+create role r_select_database;
+create role r_active_database;
+create role r_select_table;
+create role r_active_table;
+create role r_select_column;
+create role r_active_column;
+create role r_execute_proc;
+create role r_active_proc;
+create role r_execute_func;
+create role r_active_func;
+grant r_select_global to r_active_global;
+grant r_select_database to r_active_database;
+grant r_select_table to r_active_table;
+grant r_select_column to r_active_column;
+grant r_execute_proc to r_active_proc;
+grant r_execute_func to r_active_func;
+#
+# These 3 roles form a chain, where only the upper level has select
+# privileges and the middle level will receive a grant for the same level
+# privilege, but different kind (for example select on upper and insert
+# on middle).
+#
+# The lower level should inherit both rights.
+#
+create role upper_level;
+create role middle_level;
+create role lower_level;
+grant upper_level to middle_level;
+grant middle_level to lower_level;
+grant r_active_global to foo;
+grant r_active_database to foo;
+grant r_active_table to foo;
+grant r_active_column to foo;
+grant r_active_proc to foo;
+grant r_active_func to foo;
+grant lower_level to foo;
+grant select on *.* to r_select_global;
+grant select on some_db.* to r_select_database;
+grant select on some_db.t1 to r_select_table;
+grant select(a) on some_db.t1 to r_select_column;
+grant select on *.* to upper_level;
+grant execute on procedure some_db.p1 to r_execute_proc;
+grant execute on function some_db.f1 to r_execute_func;
+#
+# Granting a privilege different than select on the corresponding level.
+# This tests that the base role correctly inherits its granted roles
+# privileges.
+#
+grant insert on *.* to r_active_global;
+grant insert on some_db.* to r_active_database;
+grant insert on some_db.t1 to r_active_table;
+grant insert(a) on some_db.t1 to r_active_column;
+grant insert on *.* to middle_level;
+grant alter routine on procedure some_db.p1 to r_active_proc;
+grant alter routine on function some_db.f1 to r_active_func;
+connect con1, localhost, foo,,;
+select * from some_db.t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `some_db`.`t1`
+#
+# Before MDEV-29458 fix, all these commands would return
+# ER_TABLEACCESS_DENIED_ERROR
+#
+set role r_active_global;
+select * from some_db.t1;
+a b secret
+set role r_active_database;
+select * from some_db.t1;
+a b secret
+set role r_active_table;
+select * from some_db.t1;
+a b secret
+set role r_active_column;
+select a from some_db.t1;
+a
+set role lower_level;
+select * from some_db.t1;
+a b secret
+set role r_active_proc;
+set @var=100;
+call some_db.p1(@var);
+set role r_active_func;
+select some_db.f1(10);
+some_db.f1(10)
+110
+disconnect con1;
+#
+# Cleanup.
+#
+connection default;
+drop database some_db;
+drop role r_select_global, r_select_database, r_select_table, r_select_column;
+drop role r_active_global, r_active_database, r_active_table, r_active_column;
+drop role r_execute_proc, r_execute_func;
+drop role r_active_proc, r_active_func;
+drop role upper_level, middle_level, lower_level;
+drop user foo;
+#
+# Test that dropping of roles clears the intermediate generated
+# (such as an `acl_dbs` element with 0 init_access, but with access != 0)
+# datastructures.
+#
+create role test_role1;
+create role test_role2;
+grant test_role2 to test_role1;
+grant select on mysql.* to test_role2;
+grant select on mysql.user to test_role2;
+grant select(user) on mysql.user to test_role2;
+drop role test_role1, test_role2;
+create role test_role1;
+drop role test_role1;
+#
+# MDEV-29851 Cached role privileges are not invalidated when needed
+#
+create role admin;
+create role student;
+create database crm;
+grant create on crm.* to admin;
+grant select on crm.* to student;
+create user intern@localhost;
+grant student to intern@localhost;
+set default role student for intern@localhost;
+connect con1, localhost, intern;
+use crm;
+disconnect con1;
+connection default;
+grant admin to student;
+connect con1, localhost, intern;
+use crm;
+create table t1 (a int);
+disconnect con1;
+connection default;
+drop user intern@localhost;
+drop role student;
+drop role admin;
+drop database crm;
+#
+# MDEV-30526 Assertion `rights == merged->cols' failed in update_role_columns
+#
+create table t1 ( pk int, i int);
+create role a;
+grant select (i), update (pk) on t1 to a;
+revoke update (pk) on t1 from a;
+show grants for a;
+Grants for a
+GRANT USAGE ON *.* TO `a`
+GRANT SELECT (`i`) ON `test`.`t1` TO `a`
+drop role a;
+drop table t1;
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/suite/roles/role_grant_propagate.test b/mysql-test/suite/roles/role_grant_propagate.test
new file mode 100644
index 00000000..02d451f0
--- /dev/null
+++ b/mysql-test/suite/roles/role_grant_propagate.test
@@ -0,0 +1,212 @@
+--source include/not_embedded.inc
+
+--echo #
+--echo # MDEV-29458 Role grant commands do not propagate all grants
+--echo #
+
+create user foo;
+create database some_db;
+create table some_db.t1 (a int, b int, secret int);
+
+delimiter //;
+CREATE PROCEDURE some_db.p1 (OUT param1 INT)
+ BEGIN
+ SELECT COUNT(*) INTO param1 FROM some_db.t1;
+ END;
+//
+delimiter ;//
+
+delimiter //;
+CREATE FUNCTION some_db.f1 (param1 INT) RETURNS INT
+ BEGIN
+ DECLARE c INT;
+ SET c = 100;
+ RETURN param1 + c;
+ END;
+//
+delimiter ;//
+
+--echo #
+--echo # These roles will form a two level hierarchy.
+--echo # The "select" role will have the select privilege, while
+--echo # the active role will inherit the select role.
+--echo #
+--echo # The active role will be granted a different privilege but on the same
+--echo # level (global, database, table, proc respectively) *after* the 'select'
+--echo # role has been granted its select privilege.
+--echo #
+
+create role r_select_global;
+create role r_active_global;
+
+create role r_select_database;
+create role r_active_database;
+
+create role r_select_table;
+create role r_active_table;
+
+create role r_select_column;
+create role r_active_column;
+
+create role r_execute_proc;
+create role r_active_proc;
+
+create role r_execute_func;
+create role r_active_func;
+
+grant r_select_global to r_active_global;
+grant r_select_database to r_active_database;
+grant r_select_table to r_active_table;
+grant r_select_column to r_active_column;
+grant r_execute_proc to r_active_proc;
+grant r_execute_func to r_active_func;
+
+--echo #
+--echo # These 3 roles form a chain, where only the upper level has select
+--echo # privileges and the middle level will receive a grant for the same level
+--echo # privilege, but different kind (for example select on upper and insert
+--echo # on middle).
+--echo #
+--echo # The lower level should inherit both rights.
+--echo #
+create role upper_level;
+create role middle_level;
+create role lower_level;
+
+grant upper_level to middle_level;
+grant middle_level to lower_level;
+
+grant r_active_global to foo;
+grant r_active_database to foo;
+grant r_active_table to foo;
+grant r_active_column to foo;
+grant r_active_proc to foo;
+grant r_active_func to foo;
+grant lower_level to foo;
+
+grant select on *.* to r_select_global;
+grant select on some_db.* to r_select_database;
+grant select on some_db.t1 to r_select_table;
+grant select(a) on some_db.t1 to r_select_column;
+grant select on *.* to upper_level;
+
+grant execute on procedure some_db.p1 to r_execute_proc;
+grant execute on function some_db.f1 to r_execute_func;
+
+
+--echo #
+--echo # Granting a privilege different than select on the corresponding level.
+--echo # This tests that the base role correctly inherits its granted roles
+--echo # privileges.
+--echo #
+grant insert on *.* to r_active_global;
+grant insert on some_db.* to r_active_database;
+grant insert on some_db.t1 to r_active_table;
+grant insert(a) on some_db.t1 to r_active_column;
+grant insert on *.* to middle_level;
+
+grant alter routine on procedure some_db.p1 to r_active_proc;
+grant alter routine on function some_db.f1 to r_active_func;
+
+--connect (con1, localhost, foo,,)
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from some_db.t1;
+
+--echo #
+--echo # Before MDEV-29458 fix, all these commands would return
+--echo # ER_TABLEACCESS_DENIED_ERROR
+--echo #
+set role r_active_global;
+select * from some_db.t1;
+set role r_active_database;
+select * from some_db.t1;
+set role r_active_table;
+select * from some_db.t1;
+set role r_active_column;
+select a from some_db.t1;
+set role lower_level;
+select * from some_db.t1;
+
+set role r_active_proc;
+set @var=100;
+call some_db.p1(@var);
+
+set role r_active_func;
+select some_db.f1(10);
+
+disconnect con1;
+
+--echo #
+--echo # Cleanup.
+--echo #
+connection default;
+
+drop database some_db;
+drop role r_select_global, r_select_database, r_select_table, r_select_column;
+drop role r_active_global, r_active_database, r_active_table, r_active_column;
+drop role r_execute_proc, r_execute_func;
+drop role r_active_proc, r_active_func;
+drop role upper_level, middle_level, lower_level;
+drop user foo;
+
+--echo #
+--echo # Test that dropping of roles clears the intermediate generated
+--echo # (such as an `acl_dbs` element with 0 init_access, but with access != 0)
+--echo # datastructures.
+--echo #
+create role test_role1;
+create role test_role2;
+
+grant test_role2 to test_role1;
+grant select on mysql.* to test_role2;
+grant select on mysql.user to test_role2;
+grant select(user) on mysql.user to test_role2;
+drop role test_role1, test_role2;
+
+create role test_role1;
+drop role test_role1;
+
+--echo #
+--echo # MDEV-29851 Cached role privileges are not invalidated when needed
+--echo #
+create role admin;
+create role student;
+create database crm;
+grant create on crm.* to admin;
+grant select on crm.* to student;
+create user intern@localhost;
+grant student to intern@localhost;
+set default role student for intern@localhost;
+
+connect con1, localhost, intern;
+use crm;
+disconnect con1;
+
+connection default;
+grant admin to student;
+
+connect con1, localhost, intern;
+use crm;
+create table t1 (a int);
+disconnect con1;
+
+connection default;
+drop user intern@localhost;
+drop role student;
+drop role admin;
+drop database crm;
+
+--echo #
+--echo # MDEV-30526 Assertion `rights == merged->cols' failed in update_role_columns
+--echo #
+create table t1 ( pk int, i int);
+create role a;
+grant select (i), update (pk) on t1 to a;
+revoke update (pk) on t1 from a;
+show grants for a;
+drop role a;
+drop table t1;
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/suite/roles/roles_tables_priv-29465.result b/mysql-test/suite/roles/roles_tables_priv-29465.result
new file mode 100644
index 00000000..85ab1881
--- /dev/null
+++ b/mysql-test/suite/roles/roles_tables_priv-29465.result
@@ -0,0 +1,38 @@
+create user foo;
+create database some_db;
+create table some_db.t1 (a int, b int, secret int);
+create role r_select_column;
+create role r_active_column;
+grant r_select_column to r_active_column;
+grant r_active_column to foo;
+grant select(a) on some_db.t1 to r_select_column;
+select * from mysql.tables_priv order by user;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+localhost mysql mariadb.sys global_priv root@localhost 0000-00-00 00:00:00 Select,Delete
+ some_db r_select_column t1 root@localhost 0000-00-00 00:00:00 Select
+grant insert(a) on some_db.t1 to r_active_column;
+select * from mysql.tables_priv order by user;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+localhost mysql mariadb.sys global_priv root@localhost 0000-00-00 00:00:00 Select,Delete
+ some_db r_active_column t1 root@localhost 0000-00-00 00:00:00 Insert
+ some_db r_select_column t1 root@localhost 0000-00-00 00:00:00 Select
+connect con1, localhost, foo,,;
+insert into some_db.t1(a) values (1);
+ERROR 42000: INSERT command denied to user 'foo'@'localhost' for table `some_db`.`t1`
+set role r_active_column;
+insert into some_db.t1(a) values (1);
+disconnect con1;
+connection default;
+revoke insert(a) on some_db.t1 from r_active_column;
+connect con1, localhost, foo,,;
+insert into some_db.t1(a) values (1);
+ERROR 42000: INSERT command denied to user 'foo'@'localhost' for table `some_db`.`t1`
+set role r_active_column;
+insert into some_db.t1(a) values (1);
+ERROR 42000: INSERT command denied to user 'foo'@'localhost' for table `some_db`.`t1`
+disconnect con1;
+connection default;
+drop role r_select_column;
+drop role r_active_column;
+drop user foo;
+drop database some_db;
diff --git a/mysql-test/suite/roles/roles_tables_priv-29465.test b/mysql-test/suite/roles/roles_tables_priv-29465.test
new file mode 100644
index 00000000..550b05a4
--- /dev/null
+++ b/mysql-test/suite/roles/roles_tables_priv-29465.test
@@ -0,0 +1,40 @@
+--source include/not_embedded.inc
+
+create user foo;
+create database some_db;
+create table some_db.t1 (a int, b int, secret int);
+
+create role r_select_column;
+create role r_active_column;
+grant r_select_column to r_active_column;
+grant r_active_column to foo;
+
+grant select(a) on some_db.t1 to r_select_column;
+select * from mysql.tables_priv order by user;
+grant insert(a) on some_db.t1 to r_active_column;
+select * from mysql.tables_priv order by user;
+
+--connect (con1, localhost, foo,,)
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into some_db.t1(a) values (1);
+set role r_active_column;
+insert into some_db.t1(a) values (1);
+disconnect con1;
+
+connection default;
+revoke insert(a) on some_db.t1 from r_active_column;
+
+--connect (con1, localhost, foo,,)
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into some_db.t1(a) values (1);
+set role r_active_column;
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into some_db.t1(a) values (1);
+disconnect con1;
+
+connection default;
+
+drop role r_select_column;
+drop role r_active_column;
+drop user foo;
+drop database some_db;
diff --git a/mysql-test/suite/roles/rpl_definer.result b/mysql-test/suite/roles/rpl_definer.result
new file mode 100644
index 00000000..185b17fd
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_definer.result
@@ -0,0 +1,73 @@
+include/master-slave.inc
+[connection master]
+create role role1;
+create role role2;
+grant execute on test.* to role2;
+grant role2 to role1;
+set role role1;
+show grants;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT EXECUTE ON `test`.* TO `role2`
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT `role1` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT `role2` TO `role1`
+GRANT `role2` TO `root`@`localhost` WITH ADMIN OPTION
+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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1" latin1 latin1_swedish_ci latin1_swedish_ci
+connection slave;
+set role role1;
+show grants;
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT EXECUTE ON `test`.* TO `role2`
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `role1`
+GRANT USAGE ON *.* TO `role2`
+GRANT `role1` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT `role2` TO `role1`
+GRANT `role2` TO `root`@`localhost` WITH ADMIN OPTION
+show create procedure pcu;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pcu STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 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 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1" latin1 latin1_swedish_ci latin1_swedish_ci
+connection master;
+drop procedure pcu;
+drop procedure pu;
+drop procedure pcr;
+drop procedure pr;
+drop role role1;
+drop role role2;
+include/rpl_end.inc
diff --git a/mysql-test/suite/roles/rpl_definer.test b/mysql-test/suite/roles/rpl_definer.test
new file mode 100644
index 00000000..8e19bd16
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_definer.test
@@ -0,0 +1,46 @@
+#
+# replication of the DEFINER=current_role
+#
+
+--source include/master-slave.inc
+
+create role role1;
+create role role2;
+grant execute on test.* to role2;
+grant role2 to role1;
+set role role1;
+--sorted_result
+show grants;
+
+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;
+
+set role role1;
+--sorted_result
+show grants;
+
+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;
+drop role role2;
+
+--source include/rpl_end.inc
+
diff --git a/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.result b/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.result
new file mode 100644
index 00000000..ef2b9648
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.result
@@ -0,0 +1,8 @@
+include/master-slave.inc
+[connection master]
+create role r1;
+set role r1;
+grant select on db.* to current_role;
+revoke all privileges, grant option from current_role;
+drop role r1;
+include/rpl_end.inc
diff --git a/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.test b/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.test
new file mode 100644
index 00000000..10e5e938
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_grant_revoke_current_role-8638.test
@@ -0,0 +1,12 @@
+--source include/have_binlog_format_mixed.inc
+--source include/master-slave.inc
+
+--enable_connect_log
+
+create role r1;
+set role r1;
+grant select on db.* to current_role;
+revoke all privileges, grant option from current_role;
+drop role r1;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/roles/set_and_drop.result b/mysql-test/suite/roles/set_and_drop.result
new file mode 100644
index 00000000..e52a82bd
--- /dev/null
+++ b/mysql-test/suite/roles/set_and_drop.result
@@ -0,0 +1,128 @@
+create database mysqltest1;
+create table mysqltest1.t1 (a int, b int);
+create table mysqltest1.t2 (a int, b int);
+insert mysqltest1.t1 values (1,2),(3,4);
+insert mysqltest1.t2 values (5,6),(7,8);
+create procedure mysqltest1.pr1() select "pr1";
+create user foo@localhost;
+create role role1;
+create role role2;
+grant role2 to role1;
+grant role1 to foo@localhost;
+grant reload on *.* to role2;
+grant select on mysql.* to role2;
+grant execute on procedure mysqltest1.pr1 to role2;
+grant select on mysqltest1.t1 to role2;
+grant select (a) on mysqltest1.t2 to role2;
+connect foo,localhost,foo;
+flush tables;
+ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+show tables from mysqltest1;
+ERROR 42000: Access denied for user 'foo'@'localhost' to database 'mysqltest1'
+set role role1;
+flush tables;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ role1 role2 N
+localhost foo role1 N
+localhost root role1 Y
+localhost root role2 Y
+show tables from mysqltest1;
+Tables_in_mysqltest1
+t1
+t2
+select * from mysqltest1.t1;
+a b
+1 2
+3 4
+select * from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t2`
+select a from mysqltest1.t2;
+a
+5
+7
+call mysqltest1.pr1();
+pr1
+pr1
+connection default;
+revoke execute on procedure mysqltest1.pr1 from role2;
+connection foo;
+call mysqltest1.pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'mysqltest1.pr1'
+connection default;
+drop role role2;
+connection foo;
+show grants;
+Grants for foo@localhost
+GRANT `role1` TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `foo`@`localhost`
+GRANT USAGE ON *.* TO `role1`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+role1
+flush tables;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+select * from mysqltest1.t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t1`
+select a from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t2`
+set role none;
+connection default;
+grant reload on *.* to role1;
+grant select on mysql.* to role1;
+grant execute on procedure mysqltest1.pr1 to role1;
+grant select on mysqltest1.t1 to role1;
+grant select (a) on mysqltest1.t2 to role1;
+connection foo;
+set role role1;
+flush tables;
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost foo role1 N
+localhost root role1 Y
+show tables from mysqltest1;
+Tables_in_mysqltest1
+t1
+t2
+select * from mysqltest1.t1;
+a b
+1 2
+3 4
+select * from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t2`
+select a from mysqltest1.t2;
+a
+5
+7
+call mysqltest1.pr1();
+pr1
+pr1
+connection default;
+drop role role1;
+connection foo;
+flush tables;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysql`.`roles_mapping`
+select * from mysqltest1.t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t1`
+select a from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table `mysqltest1`.`t2`
+show grants;
+Grants for foo@localhost
+GRANT USAGE ON *.* TO `foo`@`localhost`
+select * from information_schema.enabled_roles;
+ROLE_NAME
+NULL
+select * from information_schema.enabled_roles;
+ROLE_NAME
+NULL
+select current_role();
+current_role()
+role1
+disconnect foo;
+connection default;
+drop user foo@localhost;
+drop database mysqltest1;
diff --git a/mysql-test/suite/roles/set_and_drop.test b/mysql-test/suite/roles/set_and_drop.test
new file mode 100644
index 00000000..852e79fd
--- /dev/null
+++ b/mysql-test/suite/roles/set_and_drop.test
@@ -0,0 +1,113 @@
+--source include/not_embedded.inc
+#
+# test setting and dropping a role
+#
+
+create database mysqltest1;
+create table mysqltest1.t1 (a int, b int);
+create table mysqltest1.t2 (a int, b int);
+insert mysqltest1.t1 values (1,2),(3,4);
+insert mysqltest1.t2 values (5,6),(7,8);
+
+create procedure mysqltest1.pr1() select "pr1";
+
+create user foo@localhost;
+create role role1;
+create role role2;
+
+grant role2 to role1;
+grant role1 to foo@localhost;
+grant reload on *.* to role2;
+grant select on mysql.* to role2;
+grant execute on procedure mysqltest1.pr1 to role2;
+grant select on mysqltest1.t1 to role2;
+grant select (a) on mysqltest1.t2 to role2;
+
+connect (foo,localhost,foo);
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+flush tables;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+--error ER_DBACCESS_DENIED_ERROR
+show tables from mysqltest1;
+
+set role role1;
+
+flush tables;
+--sorted_result
+select * from mysql.roles_mapping;
+show tables from mysqltest1;
+select * from mysqltest1.t1;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysqltest1.t2;
+select a from mysqltest1.t2;
+call mysqltest1.pr1();
+
+connection default;
+revoke execute on procedure mysqltest1.pr1 from role2;
+connection foo;
+
+--error ER_PROCACCESS_DENIED_ERROR
+call mysqltest1.pr1();
+
+connection default;
+drop role role2;
+connection foo;
+
+show grants;
+select * from information_schema.enabled_roles;
+
+flush tables;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysqltest1.t1;
+--error ER_TABLEACCESS_DENIED_ERROR
+select a from mysqltest1.t2;
+
+set role none;
+
+connection default;
+
+grant reload on *.* to role1;
+grant select on mysql.* to role1;
+grant execute on procedure mysqltest1.pr1 to role1;
+grant select on mysqltest1.t1 to role1;
+grant select (a) on mysqltest1.t2 to role1;
+
+connection foo;
+set role role1;
+
+flush tables;
+--sorted_result
+select * from mysql.roles_mapping;
+show tables from mysqltest1;
+select * from mysqltest1.t1;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysqltest1.t2;
+select a from mysqltest1.t2;
+call mysqltest1.pr1();
+
+connection default;
+drop role role1;
+connection foo;
+
+flush tables;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysqltest1.t1;
+--error ER_TABLEACCESS_DENIED_ERROR
+select a from mysqltest1.t2;
+
+show grants;
+select * from information_schema.enabled_roles;
+select * from information_schema.enabled_roles; # yes, repeat it twice
+select current_role();
+
+disconnect foo;
+connection default;
+
+drop user foo@localhost;
+drop database mysqltest1;
diff --git a/mysql-test/suite/roles/set_default_role_clear.result b/mysql-test/suite/roles/set_default_role_clear.result
new file mode 100644
index 00000000..8a3ae908
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_clear.result
@@ -0,0 +1,36 @@
+create user test_user@localhost;
+create role test_role;
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+set default role test_role;
+select user, host, default_role from mysql.user;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost test_role
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT SELECT ON *.* TO `test_role`
+SET DEFAULT ROLE `test_role` FOR `test_user`@`localhost`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost test_role
+set default role NONE;
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost
+set default role invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost
+select user, host, default_role from mysql.user;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+drop role test_role;
+drop user test_user@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_clear.test b/mysql-test/suite/roles/set_default_role_clear.test
new file mode 100644
index 00000000..32c9661c
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_clear.test
@@ -0,0 +1,52 @@
+source include/not_embedded.inc;
+
+# This test checks clearing a default role from a user.
+
+# Create a user with no privileges
+create user test_user@localhost;
+
+create role test_role;
+
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+
+change_user 'test_user';
+show grants;
+set default role test_role;
+
+# Even though a user has the default role set, without reconnecting, we should
+# not already have the roles privileges.
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user;
+
+change_user 'root';
+select user, host, default_role from mysql.user where user='test_user';
+
+change_user 'test_user';
+# This should show that the new test_user has the role's grants enabled.
+show grants;
+select user, host, default_role from mysql.user where user='test_user';
+
+set default role NONE;
+
+# We should still have the role set right now.
+select user, host, default_role from mysql.user where user='test_user';
+
+# Make sure we do not somehow get privileges to set an invalid role
+--error ER_INVALID_ROLE
+set default role invalid_role;
+
+change_user 'root';
+select user, host, default_role from mysql.user where user='test_user';
+
+change_user 'test_user';
+# The user does not have a default role set anymore. Make sure we don't still
+# get the privileges.
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user;
+
+change_user 'root';
+
+# Cleanup
+drop role test_role;
+drop user test_user@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_for.result b/mysql-test/suite/roles/set_default_role_for.result
new file mode 100644
index 00000000..57a14711
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_for.result
@@ -0,0 +1,60 @@
+create user user_a@localhost;
+create user user_b@localhost;
+create role role_a;
+create role role_b;
+grant role_a to user_a@localhost;
+grant role_b to user_b@localhost;
+grant role_a to user_a@localhost;
+grant select on *.* to role_a;
+grant role_b to user_b@localhost;
+grant insert, update on *.* to role_b;
+set default role role_a for user_b@localhost;
+ERROR 42000: Access denied for user 'user_a'@'localhost' to database 'mysql'
+set default role role_a for user_a@localhost;
+set default role invalid_role for user_a@localhost;
+ERROR OP000: Invalid role specification `invalid_role`
+set default role role_b for user_a@localhost;
+ERROR OP000: User `root`@`localhost` has not been granted role `role_b`
+set default role role_b for user_b@localhost;
+show grants;
+Grants for user_a@localhost
+GRANT `role_a` TO `user_a`@`localhost`
+GRANT USAGE ON *.* TO `user_a`@`localhost`
+GRANT SELECT ON *.* TO `role_a`
+SET DEFAULT ROLE `role_a` FOR `user_a`@`localhost`
+select user, host, default_role from mysql.user where user like 'user_%';
+User Host default_role
+user_a localhost role_a
+user_b localhost role_b
+set default role NONE for current_user;
+select user, host, default_role from mysql.user where user like 'user_%';
+User Host default_role
+user_a localhost
+user_b localhost role_b
+set default role current_role for current_user;
+select user, host, default_role from mysql.user where user like 'user_%';
+User Host default_role
+user_a localhost role_a
+user_b localhost role_b
+set default role role_b for current_user;
+ERROR OP000: User `user_a`@`localhost` has not been granted role `role_b`
+show grants;
+Grants for user_b@localhost
+GRANT `role_b` TO `user_b`@`localhost`
+GRANT USAGE ON *.* TO `user_b`@`localhost`
+GRANT INSERT, UPDATE ON *.* TO `role_b`
+SET DEFAULT ROLE `role_b` FOR `user_b`@`localhost`
+select user, host, default_role from mysql.user where user like 'user_%';
+ERROR 42000: SELECT command denied to user 'user_b'@'localhost' for table `mysql`.`user`
+set default role NONE for user_a@localhost;
+show grants;
+Grants for user_a@localhost
+GRANT `role_a` TO `user_a`@`localhost`
+GRANT USAGE ON *.* TO `user_a`@`localhost`
+GRANT INSERT, UPDATE ON *.* TO `role_b`
+select user, host, default_role from mysql.user where user like 'user_%';
+ERROR 42000: SELECT command denied to user 'user_a'@'localhost' for table `mysql`.`user`
+drop role role_a;
+drop role role_b;
+drop user user_a@localhost;
+drop user user_b@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_for.test b/mysql-test/suite/roles/set_default_role_for.test
new file mode 100644
index 00000000..eff999a5
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_for.test
@@ -0,0 +1,84 @@
+source include/not_embedded.inc;
+
+# This test checks setting a default role to a different user;
+
+
+create user user_a@localhost;
+create user user_b@localhost;
+
+create role role_a;
+create role role_b;
+
+grant role_a to user_a@localhost;
+grant role_b to user_b@localhost;
+
+grant role_a to user_a@localhost;
+grant select on *.* to role_a;
+
+grant role_b to user_b@localhost;
+grant insert, update on *.* to role_b;
+
+change_user 'user_a';
+
+# A user should not be a able to set a default role for someone else,
+# if he hasn't got write access to the database.
+--error ER_DBACCESS_DENIED_ERROR
+set default role role_a for user_b@localhost;
+
+# Should have the same effect as set default role role_a.
+set default role role_a for user_a@localhost;
+
+change_user 'root';
+
+# Not even a 'root' user should be able to set an invalid role for a user.
+--error ER_INVALID_ROLE
+set default role invalid_role for user_a@localhost;
+
+--error ER_INVALID_ROLE
+set default role role_b for user_a@localhost;
+
+# Make sure we can set a default role for a different user than the one that
+# is actually running the command.
+set default role role_b for user_b@localhost;
+
+change_user 'user_a';
+
+show grants;
+--sorted_result
+select user, host, default_role from mysql.user where user like 'user_%';
+
+set default role NONE for current_user;
+--sorted_result
+select user, host, default_role from mysql.user where user like 'user_%';
+
+set default role current_role for current_user;
+--sorted_result
+select user, host, default_role from mysql.user where user like 'user_%';
+
+# Make sure we can't set a default role not granted to us, using current_user
+--error ER_INVALID_ROLE
+set default role role_b for current_user;
+
+change_user 'user_b';
+
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user like 'user_%';
+
+# Since we have update privileges on the mysql.user table, we should
+# be able to set a default role for a different user.
+set default role NONE for user_a@localhost;
+
+change_user 'user_a';
+
+# There is no default role set any more.
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user like 'user_%';
+
+change_user 'root';
+
+drop role role_a;
+drop role role_b;
+drop user user_a@localhost;
+drop user user_b@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_invalid.result b/mysql-test/suite/roles/set_default_role_invalid.result
new file mode 100644
index 00000000..12e2c035
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_invalid.result
@@ -0,0 +1,130 @@
+create user test_user@localhost;
+create role test_role;
+create role not_granted_role;
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+select user, host, default_role from mysql.user;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+set default role invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+set default role not_granted_role;
+ERROR OP000: Invalid role specification `not_granted_role`
+set default role test_role;
+select user, host, default_role from mysql.user;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost test_role
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT SELECT ON *.* TO `test_role`
+SET DEFAULT ROLE `test_role` FOR `test_user`@`localhost`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost test_role
+set default role invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+select user, host, default_role from mysql.user where user='test_user';
+User Host default_role
+test_user localhost test_role
+revoke test_role from test_user@localhost;
+select user, host, default_role from mysql.user where user='test_user';
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+drop role test_role;
+drop role not_granted_role;
+drop user test_user@localhost;
+#
+# MDEV-22312: Bad error message for SET DEFAULT ROLE when user account
+# is not granted the role
+#
+CREATE USER a;
+CREATE USER b;
+CREATE ROLE r1;
+CREATE ROLE r2;
+SET DEFAULT ROLE r1 FOR a;
+ERROR OP000: User `root`@`localhost` has not been granted role `r1`
+GRANT r1 TO b;
+GRANT r2 TO b;
+SET DEFAULT ROLE r1 FOR b;
+# Change user b
+SELECT CURRENT_ROLE;
+CURRENT_ROLE
+r1
+SET ROLE r2;
+SELECT CURRENT_ROLE;
+CURRENT_ROLE
+r2
+SET DEFAULT ROLE r1 FOR a;
+ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
+SET DEFAULT ROLE r2;
+# Change user root (session 1: select_priv to b)
+GRANT SELECT ON mysql.* TO b;
+# Change user b (session 1: select_priv)
+SHOW GRANTS FOR b;
+Grants for b@%
+GRANT `r1` TO `b`@`%`
+GRANT `r2` TO `b`@`%`
+GRANT USAGE ON *.* TO `b`@`%`
+GRANT SELECT ON `mysql`.* TO `b`@`%`
+SET DEFAULT ROLE `r2` FOR `b`@`%`
+SET DEFAULT ROLE r1 FOR a;
+ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
+SELECT CURRENT_ROLE;
+CURRENT_ROLE
+r2
+SET DEFAULT ROLE NONE;
+SELECT CURRENT_ROLE;
+CURRENT_ROLE
+r2
+SET DEFAULT ROLE current_role FOR current_user;
+SET DEFAULT ROLE invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+SET DEFAULT ROLE invalid_role FOR a;
+ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
+SET DEFAULT ROLE none FOR a;
+ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
+# Change user root (session 2: adding update_priv to user b)
+GRANT UPDATE ON mysql.* TO b;
+# Change user b
+SHOW GRANTS FOR b;
+Grants for b@%
+GRANT `r1` TO `b`@`%`
+GRANT `r2` TO `b`@`%`
+GRANT USAGE ON *.* TO `b`@`%`
+GRANT SELECT, UPDATE ON `mysql`.* TO `b`@`%`
+SET DEFAULT ROLE `r2` FOR `b`@`%`
+SET DEFAULT ROLE r1 FOR a;
+ERROR OP000: User `b`@`%` has not been granted role `r1`
+SET DEFAULT ROLE invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+SET DEFAULT ROLE invalid_role FOR a;
+ERROR OP000: Invalid role specification `invalid_role`
+SET DEFAULT ROLE none FOR a;
+# Change user root (session 3: Grant role to user a)
+GRANT r1 TO a;
+SET DEFAULT ROLE r1 FOR a;
+# Change user a (verify session 3)
+SELECT CURRENT_ROLE;
+CURRENT_ROLE
+r1
+SET DEFAULT ROLE None;
+# Change user b (session 3: role granted to user a)
+SET DEFAULT ROLE r1 FOR a;
+SET DEFAULT ROLE r2 FOR a;
+ERROR OP000: User `b`@`%` has not been granted role `r2`
+SET DEFAULT ROLE invalid_role;
+ERROR OP000: Invalid role specification `invalid_role`
+SET DEFAULT ROLE invalid_role FOR a;
+ERROR OP000: Invalid role specification `invalid_role`
+SELECT user, host, default_role FROM mysql.user where user='a' or user='b';
+User Host default_role
+a % r1
+b % r2
+DROP ROLE r1, r2;
+DROP USER a, b;
diff --git a/mysql-test/suite/roles/set_default_role_invalid.test b/mysql-test/suite/roles/set_default_role_invalid.test
new file mode 100644
index 00000000..02fca110
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_invalid.test
@@ -0,0 +1,169 @@
+source include/not_embedded.inc;
+
+# This test checks the error paths possible during set default role.
+
+# Create a user with no privileges
+create user test_user@localhost;
+
+create role test_role;
+create role not_granted_role;
+
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+
+change_user 'test_user';
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user;
+
+# A user can not set a default role that does not exist in the database.
+--error ER_INVALID_ROLE
+set default role invalid_role;
+
+# A user can not set a default role if he can not call set role <role>.
+--error ER_INVALID_ROLE
+set default role not_granted_role;
+
+set default role test_role;
+
+# Even though a user has the default role set, without reconnecting, we should
+# not already have the roles privileges.
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user;
+
+change_user 'root';
+select user, host, default_role from mysql.user where user='test_user';
+
+change_user 'test_user';
+# This should show that the new test_user has the role's grants enabled.
+show grants;
+select user, host, default_role from mysql.user where user='test_user';
+
+# If we have a failed set default role attempt, don't change the already set
+# default role.
+--error ER_INVALID_ROLE
+set default role invalid_role;
+select user, host, default_role from mysql.user where user='test_user';
+
+change_user 'root';
+# Now, even though a default role is still set for test_user, make sure the
+# user does not get the rights, if he can not set the role.
+revoke test_role from test_user@localhost;
+
+change_user 'test_user';
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user='test_user';
+
+change_user 'root';
+
+# Cleanup
+drop role test_role;
+drop role not_granted_role;
+drop user test_user@localhost;
+
+--echo #
+--echo # MDEV-22312: Bad error message for SET DEFAULT ROLE when user account
+--echo # is not granted the role
+--echo #
+
+CREATE USER a;
+CREATE USER b;
+CREATE ROLE r1;
+CREATE ROLE r2;
+# Role has not been granted to user a, but the role is visible to current_user
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE r1 FOR a;
+# Granting roles to user b
+GRANT r1 TO b;
+GRANT r2 TO b;
+# After granting the role, role can be set as default
+SET DEFAULT ROLE r1 FOR b;
+
+--echo # Change user b
+change_user b;
+SELECT CURRENT_ROLE;
+SET ROLE r2;
+SELECT CURRENT_ROLE;
+# User b has no UPDATE_PRIV for mysql.user
+--error ER_DBACCESS_DENIED_ERROR
+SET DEFAULT ROLE r1 FOR a;
+SET DEFAULT ROLE r2;
+
+--echo # Change user root (session 1: select_priv to b)
+change_user root;
+# Let's grant select_priv to user b
+GRANT SELECT ON mysql.* TO b;
+
+--echo # Change user b (session 1: select_priv)
+change_user b;
+SHOW GRANTS FOR b;
+# User must have update_priv before setting the role
+--error ER_DBACCESS_DENIED_ERROR
+SET DEFAULT ROLE r1 FOR a;
+# Testing the `CURRENT_ROLE` as a special case
+SELECT CURRENT_ROLE;
+SET DEFAULT ROLE NONE;
+SELECT CURRENT_ROLE;
+SET DEFAULT ROLE current_role FOR current_user;
+# Testing of non-existing role
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE invalid_role;
+# Testing of non-existing role for different user
+--error ER_DBACCESS_DENIED_ERROR
+SET DEFAULT ROLE invalid_role FOR a;
+# Testing the `None` role for different user
+-- error ER_DBACCESS_DENIED_ERROR
+SET DEFAULT ROLE none FOR a;
+
+--echo # Change user root (session 2: adding update_priv to user b)
+change_user root;
+# update_priv are enough
+GRANT UPDATE ON mysql.* TO b;
+
+--echo # Change user b
+change_user b;
+SHOW GRANTS FOR b;
+# In all tests in session user a has not been granted the role
+# Testing setting role for different user, should fail with new error
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE r1 FOR a;
+# Testing of non-existing role
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE invalid_role;
+# Testing of non-existing role for different user with update_priv
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE invalid_role FOR a;
+# Testing the `None` role for different user with update_priv
+SET DEFAULT ROLE none FOR a;
+
+--echo # Change user root (session 3: Grant role to user a)
+change_user root;
+# After granting the privilege for a, user b can set default role
+GRANT r1 TO a;
+SET DEFAULT ROLE r1 FOR a;
+
+--echo # Change user a (verify session 3)
+change_user a;
+SELECT CURRENT_ROLE;
+SET DEFAULT ROLE None;
+
+--echo # Change user b (session 3: role granted to user a)
+change_user b;
+# This should set role because b has update_priv
+SET DEFAULT ROLE r1 FOR a;
+# Testing non-granted role r2 still should fail
+-- error ER_INVALID_ROLE
+SET DEFAULT ROLE r2 FOR a;
+# Testing of non-existing role
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE invalid_role;
+# Testing of non-existing role for different user
+--error ER_INVALID_ROLE
+SET DEFAULT ROLE invalid_role FOR a;
+
+# Clear the workspace
+change_user root;
+--sorted_result
+SELECT user, host, default_role FROM mysql.user where user='a' or user='b';
+DROP ROLE r1, r2;
+DROP USER a, b;
diff --git a/mysql-test/suite/roles/set_default_role_new_connection.result b/mysql-test/suite/roles/set_default_role_new_connection.result
new file mode 100644
index 00000000..95c71274
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_new_connection.result
@@ -0,0 +1,62 @@
+create user test_user@localhost;
+create role test_role;
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+connect c1, localhost, test_user,,;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+select user, host, default_role from mysql.user where user = 'test_user';
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+set default role test_role;
+select user, host, default_role from mysql.user where user = 'test_user';
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+disconnect c1;
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+User Host default_role
+test_user localhost test_role
+connect c1, localhost, test_user,,;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT SELECT ON *.* TO `test_role`
+SET DEFAULT ROLE `test_role` FOR `test_user`@`localhost`
+select user, host, default_role from mysql.user where user = 'test_user';
+User Host default_role
+test_user localhost test_role
+set default role NONE;
+disconnect c1;
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+User Host default_role
+test_user localhost
+connect c1, localhost, test_user,,;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+select user, host, default_role from mysql.user where user = 'test_user';
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+disconnect c1;
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+User Host default_role
+test_user localhost
+set default role test_role for test_user@localhost;
+connect c1, localhost, test_user,,;
+show grants;
+Grants for test_user@localhost
+GRANT `test_role` TO `test_user`@`localhost`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT SELECT ON *.* TO `test_role`
+SET DEFAULT ROLE `test_role` FOR `test_user`@`localhost`
+select user, host, default_role from mysql.user where user = 'test_user';
+User Host default_role
+test_user localhost test_role
+disconnect c1;
+connection default;
+drop role test_role;
+drop user test_user@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_new_connection.test b/mysql-test/suite/roles/set_default_role_new_connection.test
new file mode 100644
index 00000000..81f7f2ff
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_new_connection.test
@@ -0,0 +1,47 @@
+source include/not_embedded.inc;
+
+create user test_user@localhost;
+create role test_role;
+grant select on *.* to test_role;
+grant test_role to test_user@localhost;
+
+--connect (c1, localhost, test_user,,)
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user = 'test_user';
+set default role test_role;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user = 'test_user';
+disconnect c1;
+
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+
+
+--connect (c1, localhost, test_user,,)
+show grants;
+select user, host, default_role from mysql.user where user = 'test_user';
+set default role NONE;
+disconnect c1;
+
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+
+--connect (c1, localhost, test_user,,)
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select user, host, default_role from mysql.user where user = 'test_user';
+disconnect c1;
+
+connection default;
+select user, host, default_role from mysql.user where user = 'test_user';
+set default role test_role for test_user@localhost;
+
+--connect (c1, localhost, test_user,,)
+show grants;
+select user, host, default_role from mysql.user where user = 'test_user';
+disconnect c1;
+
+connection default;
+drop role test_role;
+drop user test_user@localhost;
diff --git a/mysql-test/suite/roles/set_default_role_ps-6960.result b/mysql-test/suite/roles/set_default_role_ps-6960.result
new file mode 100644
index 00000000..505861e8
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_ps-6960.result
@@ -0,0 +1,10 @@
+select priv into @root_priv from mysql.global_priv where user='root' and host='localhost';
+create role r1;
+prepare stmt from "set password = '11111111111111111111111111111111111111111'";
+execute stmt;
+prepare stmt from "set default role r1";
+execute stmt;
+set password = '';
+set default role NONE;
+drop role r1;
+update mysql.global_priv set priv=@root_priv where user='root' and host='localhost';
diff --git a/mysql-test/suite/roles/set_default_role_ps-6960.test b/mysql-test/suite/roles/set_default_role_ps-6960.test
new file mode 100644
index 00000000..fd965c2a
--- /dev/null
+++ b/mysql-test/suite/roles/set_default_role_ps-6960.test
@@ -0,0 +1,20 @@
+#
+# MDEV-6960 Server crashes in check_alter_user on setting a default role via PS
+#
+
+--source include/not_embedded.inc
+
+select priv into @root_priv from mysql.global_priv where user='root' and host='localhost';
+
+create role r1;
+prepare stmt from "set password = '11111111111111111111111111111111111111111'";
+execute stmt;
+prepare stmt from "set default role r1";
+execute stmt;
+
+set password = '';
+set default role NONE;
+drop role r1;
+
+#cleanup
+update mysql.global_priv set priv=@root_priv where user='root' and host='localhost';
diff --git a/mysql-test/suite/roles/set_role-13655.result b/mysql-test/suite/roles/set_role-13655.result
new file mode 100644
index 00000000..9da16c1d
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-13655.result
@@ -0,0 +1,52 @@
+#
+# MDEV-13655: SET ROLE does not properly grant privileges.
+#
+# We must test that if aditional db privileges get granted to a role
+# which previously inherited privileges from another granted role
+# keep the internal memory structures intact.
+#
+create role simple;
+#
+# First we create an entry with privileges for databases for the simple role.
+#
+grant select, insert, update, delete, lock tables, execute on t.* to simple;
+create role admin;
+#
+# Now we grant the simple role to admin. This means that db privileges
+# should propagate to admin.
+#
+grant simple to admin;
+show grants for admin;
+Grants for admin
+GRANT `simple` TO `admin`
+GRANT USAGE ON *.* TO `admin`
+GRANT USAGE ON *.* TO `simple`
+GRANT SELECT, INSERT, UPDATE, DELETE, LOCK TABLES, EXECUTE ON `t`.* TO `simple`
+#
+# Finally, we give the admin all the available privileges for the db.
+#
+grant all on t.* to admin;
+#
+# Create a user to test out the new roles;
+#
+create user foo;
+grant admin to foo;
+connect foo,localhost,foo,,,,,;
+create database t;
+ERROR 42000: Access denied for user 'foo'@'%' to database 't'
+set role admin;
+show grants;
+Grants for foo@%
+GRANT `admin` TO `foo`@`%`
+GRANT USAGE ON *.* TO `foo`@`%`
+GRANT `simple` TO `admin`
+GRANT USAGE ON *.* TO `admin`
+GRANT ALL PRIVILEGES ON `t`.* TO `admin`
+GRANT USAGE ON *.* TO `simple`
+GRANT SELECT, INSERT, UPDATE, DELETE, LOCK TABLES, EXECUTE ON `t`.* TO `simple`
+create database t;
+drop database t;
+connection default;
+drop role simple;
+drop role admin;
+drop user foo;
diff --git a/mysql-test/suite/roles/set_role-13655.test b/mysql-test/suite/roles/set_role-13655.test
new file mode 100644
index 00000000..97a82109
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-13655.test
@@ -0,0 +1,49 @@
+source include/not_embedded.inc;
+
+--echo #
+--echo # MDEV-13655: SET ROLE does not properly grant privileges.
+--echo #
+--echo # We must test that if aditional db privileges get granted to a role
+--echo # which previously inherited privileges from another granted role
+--echo # keep the internal memory structures intact.
+--echo #
+
+create role simple;
+
+--echo #
+--echo # First we create an entry with privileges for databases for the simple role.
+--echo #
+grant select, insert, update, delete, lock tables, execute on t.* to simple;
+create role admin;
+
+--echo #
+--echo # Now we grant the simple role to admin. This means that db privileges
+--echo # should propagate to admin.
+--echo #
+grant simple to admin;
+show grants for admin;
+
+--echo #
+--echo # Finally, we give the admin all the available privileges for the db.
+--echo #
+grant all on t.* to admin;
+
+--echo #
+--echo # Create a user to test out the new roles;
+--echo #
+create user foo;
+grant admin to foo;
+
+connect (foo,localhost,foo,,,,,);
+--error ER_DBACCESS_DENIED_ERROR
+create database t;
+set role admin;
+show grants;
+create database t;
+drop database t;
+
+connection default;
+
+drop role simple;
+drop role admin;
+drop user foo;
diff --git a/mysql-test/suite/roles/set_role-5232.result b/mysql-test/suite/roles/set_role-5232.result
new file mode 100644
index 00000000..6158c95e
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-5232.result
@@ -0,0 +1,18 @@
+create user ''@localhost;
+create user c;
+grant select on mysql.* to c;
+create role r1;
+grant r1 to c;
+connect c,localhost,c,,,,,;
+select user(), current_user();
+user() current_user()
+c@localhost @localhost
+select user from mysql.user group by user;
+ERROR 42000: SELECT command denied to user ''@'localhost' for table `mysql`.`user`
+set role r1;
+ERROR OP000: Invalid role specification `r1`
+disconnect c;
+connection default;
+drop role r1;
+drop user c;
+drop user ''@localhost;
diff --git a/mysql-test/suite/roles/set_role-5232.test b/mysql-test/suite/roles/set_role-5232.test
new file mode 100644
index 00000000..c6cb3d92
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-5232.test
@@ -0,0 +1,20 @@
+#
+# MDEV-5232 SET ROLE checks privileges differently from check_access()
+#
+--source include/not_embedded.inc
+create user ''@localhost;
+create user c;
+grant select on mysql.* to c;
+create role r1;
+grant r1 to c;
+connect (c,localhost,c,,,,,);
+select user(), current_user();
+--error ER_TABLEACCESS_DENIED_ERROR
+select user from mysql.user group by user;
+--error ER_INVALID_ROLE
+set role r1;
+disconnect c;
+connection default;
+drop role r1;
+drop user c;
+drop user ''@localhost;
diff --git a/mysql-test/suite/roles/set_role-9614.result b/mysql-test/suite/roles/set_role-9614.result
new file mode 100644
index 00000000..d4d689b8
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-9614.result
@@ -0,0 +1,96 @@
+#
+# MDEV-9614 Roles and Users Longer than 6 characters
+#
+# This test case checks the edge case presented in the MDEV. The
+# real issue is actually apparent when the username is longer than the
+# rolename.
+#
+# We need a separate database not including test or test_% names. Due to
+# default privileges given on these databases.
+#
+DROP DATABASE IF EXISTS `bug_db`;
+Warnings:
+Note 1008 Can't drop database 'bug_db'; database doesn't exist
+#
+# The first user did not show the bug as john's length is smaller
+# than client. The bug is apparent most of the time for usertestjohn.
+#
+CREATE USER `john`@`%`;
+CREATE USER `usertestjohn`@`%`;
+CREATE ROLE `client`;
+#
+# Setup the required tables.
+#
+CREATE DATABASE `bug_db`;
+CREATE TABLE `bug_db`.`t0`(`c0` INT);
+#
+# Setup select privileges only on the role. Setting the role should give
+# select access to bug_db.t0.
+#
+GRANT SELECT ON `bug_db`.`t0` TO `client`;
+GRANT `client` TO `john`@`%`;
+GRANT `client` TO `usertestjohn`@`%`;
+#
+# Check to see grants are set.
+#
+SHOW GRANTS FOR `john`@`%`;
+Grants for john@%
+GRANT `client` TO `john`@`%`
+GRANT USAGE ON *.* TO `john`@`%`
+SHOW GRANTS FOR `usertestjohn`@`%`;
+Grants for usertestjohn@%
+GRANT `client` TO `usertestjohn`@`%`
+GRANT USAGE ON *.* TO `usertestjohn`@`%`
+SHOW GRANTS FOR `client`;
+Grants for client
+GRANT USAGE ON *.* TO `client`
+GRANT SELECT ON `bug_db`.`t0` TO `client`
+show databases;
+Database
+bug_db
+information_schema
+mtr
+mysql
+performance_schema
+sys
+test
+#
+# Try using the database as john.
+#
+connect john, localhost, john,,information_schema;
+show databases;
+Database
+information_schema
+set role client;
+show databases;
+Database
+bug_db
+information_schema
+use bug_db;
+#
+# Try using the database as usertestjohn.
+#
+connect usertestjohn, localhost, usertestjohn,,information_schema;
+show databases;
+Database
+information_schema
+set role client;
+show databases;
+Database
+bug_db
+information_schema
+show grants;
+Grants for usertestjohn@%
+GRANT `client` TO `usertestjohn`@`%`
+GRANT USAGE ON *.* TO `usertestjohn`@`%`
+GRANT USAGE ON *.* TO `client`
+GRANT SELECT ON `bug_db`.`t0` TO `client`
+use bug_db;
+#
+# Cleanup
+#
+connection default;
+drop user john;
+drop user usertestjohn;
+drop role client;
+drop database bug_db;
diff --git a/mysql-test/suite/roles/set_role-9614.test b/mysql-test/suite/roles/set_role-9614.test
new file mode 100644
index 00000000..5e9f7dac
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-9614.test
@@ -0,0 +1,79 @@
+--source include/not_embedded.inc
+
+--echo #
+--echo # MDEV-9614 Roles and Users Longer than 6 characters
+--echo #
+--echo # This test case checks the edge case presented in the MDEV. The
+--echo # real issue is actually apparent when the username is longer than the
+--echo # rolename.
+
+--enable_connect_log
+--echo #
+--echo # We need a separate database not including test or test_% names. Due to
+--echo # default privileges given on these databases.
+--echo #
+DROP DATABASE IF EXISTS `bug_db`;
+
+--echo #
+--echo # The first user did not show the bug as john's length is smaller
+--echo # than client. The bug is apparent most of the time for usertestjohn.
+--echo #
+CREATE USER `john`@`%`;
+CREATE USER `usertestjohn`@`%`;
+CREATE ROLE `client`;
+
+--echo #
+--echo # Setup the required tables.
+--echo #
+CREATE DATABASE `bug_db`;
+CREATE TABLE `bug_db`.`t0`(`c0` INT);
+
+--echo #
+--echo # Setup select privileges only on the role. Setting the role should give
+--echo # select access to bug_db.t0.
+--echo #
+GRANT SELECT ON `bug_db`.`t0` TO `client`;
+GRANT `client` TO `john`@`%`;
+GRANT `client` TO `usertestjohn`@`%`;
+
+--echo #
+--echo # Check to see grants are set.
+--echo #
+SHOW GRANTS FOR `john`@`%`;
+SHOW GRANTS FOR `usertestjohn`@`%`;
+SHOW GRANTS FOR `client`;
+
+show databases;
+
+--echo #
+--echo # Try using the database as john.
+--echo #
+connect (john, localhost, john,,information_schema);
+
+show databases;
+set role client;
+show databases;
+use bug_db;
+
+--echo #
+--echo # Try using the database as usertestjohn.
+--echo #
+connect (usertestjohn, localhost, usertestjohn,,information_schema);
+
+show databases;
+set role client;
+show databases;
+
+show grants;
+use bug_db;
+
+
+--echo #
+--echo # Cleanup
+--echo #
+connection default;
+drop user john;
+drop user usertestjohn;
+drop role client;
+drop database bug_db;
+--disable_connect_log
diff --git a/mysql-test/suite/roles/set_role-database-recursive.result b/mysql-test/suite/roles/set_role-database-recursive.result
new file mode 100644
index 00000000..594ea059
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-recursive.result
@@ -0,0 +1,82 @@
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_user@localhost;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+localhost test_user test_role2 N
+select user, host from mysql.db;
+user host
+grant select on mysql.* to test_role2;
+flush privileges;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+localhost test_user test_role2 N
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+set role test_role2;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role2
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+localhost test_user test_role2 N
+create role test_role3;
+grant test_role3 to test_role2;
+create role test_role4;
+grant test_role4 to test_role3;
+set role test_role1;
+delete from mysql.user where user='no such user';
+ERROR 42000: DELETE command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+grant delete on mysql.* to test_role4;
+set role test_role1;
+delete from mysql.user where user='no such user';
+show grants;
+Grants for test_user@localhost
+GRANT DELETE ON `mysql`.* TO `test_role4`
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role3`
+GRANT USAGE ON *.* TO `test_role4`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+GRANT `test_role2` TO `test_user`@`localhost`
+GRANT `test_role3` TO `test_role2`
+GRANT `test_role4` TO `test_role3`
+drop user test_user@localhost;
+drop role test_role1, test_role2, test_role3, test_role4;
diff --git a/mysql-test/suite/roles/set_role-database-recursive.test b/mysql-test/suite/roles/set_role-database-recursive.test
new file mode 100644
index 00000000..17c93d45
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-recursive.test
@@ -0,0 +1,64 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_user@localhost;
+grant test_role2 to test_role1;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+--sorted_result
+select user, host from mysql.db;
+
+grant select on mysql.* to test_role2;
+flush privileges;
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+--sorted_result
+select * from mysql.roles_mapping;
+set role none;
+select current_user(), current_role();
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+set role test_role2;
+select current_user(), current_role();
+--sorted_result
+select * from mysql.roles_mapping;
+
+change_user 'root';
+
+create role test_role3;
+grant test_role3 to test_role2;
+create role test_role4;
+grant test_role4 to test_role3;
+
+change_user 'test_user';
+set role test_role1;
+--error ER_TABLEACCESS_DENIED_ERROR
+delete from mysql.user where user='no such user';
+
+change_user 'root';
+grant delete on mysql.* to test_role4;
+
+change_user 'test_user';
+set role test_role1;
+delete from mysql.user where user='no such user';
+--sorted_result
+show grants;
+
+change_user 'root';
+drop user test_user@localhost;
+drop role test_role1, test_role2, test_role3, test_role4;
diff --git a/mysql-test/suite/roles/set_role-database-simple.result b/mysql-test/suite/roles/set_role-database-simple.result
new file mode 100644
index 00000000..969a7ab1
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-simple.result
@@ -0,0 +1,53 @@
+create user 'test_user'@'localhost';
+create role test_role1;
+grant test_role1 to test_user@localhost;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root test_role1 Y
+localhost test_user test_role1 N
+grant select on mysql.* to test_role1;
+grant insert, delete on mysql.roles_mapping to test_role1;
+grant reload on *.* to test_role1;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root test_role1 Y
+localhost test_user test_role1 N
+insert into mysql.user (user, host) values ('Dummy', 'Dummy');
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+insert into mysql.roles_mapping values ('localhost', 'test_user', 'test_role2', 'N');
+delete from mysql.roles_mapping where Role='test_role2';
+use mysql;
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+use mysql;
+ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql'
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+insert into mysql.user (user, host) values ('Dummy', 'Dummy');
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table `mysql`.`user`
+insert into mysql.roles_mapping values ('localhost', 'test_user', 'test_role2', 'N');
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+delete from mysql.roles_mapping where Role='test_role2';
+ERROR 42000: DELETE command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+drop user 'test_user'@'localhost';
+revoke select on mysql.* from test_role1;
+revoke insert, delete on mysql.roles_mapping from test_role1;
+drop role test_role1;
+delete from mysql.roles_mapping where Role='test_role1';
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-database-simple.test b/mysql-test/suite/roles/set_role-database-simple.test
new file mode 100644
index 00000000..daac13a0
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-simple.test
@@ -0,0 +1,56 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user 'test_user'@'localhost';
+create role test_role1;
+
+grant test_role1 to test_user@localhost;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+grant select on mysql.* to test_role1;
+grant insert, delete on mysql.roles_mapping to test_role1;
+
+grant reload on *.* to test_role1;
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+--sorted_result
+select * from mysql.roles_mapping;
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into mysql.user (user, host) values ('Dummy', 'Dummy');
+insert into mysql.roles_mapping values ('localhost', 'test_user', 'test_role2', 'N');
+delete from mysql.roles_mapping where Role='test_role2';
+
+use mysql;
+
+set role none;
+select current_user(), current_role();
+
+--error ER_DBACCESS_DENIED_ERROR
+use mysql;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into mysql.user (user, host) values ('Dummy', 'Dummy');
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into mysql.roles_mapping values ('localhost', 'test_user', 'test_role2', 'N');
+--error ER_TABLEACCESS_DENIED_ERROR
+delete from mysql.roles_mapping where Role='test_role2';
+
+change_user 'root';
+drop user 'test_user'@'localhost';
+revoke select on mysql.* from test_role1;
+revoke insert, delete on mysql.roles_mapping from test_role1;
+drop role test_role1;
+delete from mysql.roles_mapping where Role='test_role1';
+flush privileges;
+
diff --git a/mysql-test/suite/roles/set_role-multiple-role.result b/mysql-test/suite/roles/set_role-multiple-role.result
new file mode 100644
index 00000000..e4cb3b85
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-multiple-role.result
@@ -0,0 +1,147 @@
+create user 'test_user'@'localhost';
+create role r_sel;
+create role r_ins;
+create role r_upd;
+create role r_del;
+create role r_crt;
+create role r_drp;
+create role r_rld;
+grant select on *.* to r_sel;
+grant insert on *.* to r_ins;
+grant update on *.* to r_upd;
+grant delete on *.* to r_del;
+grant create on *.* to r_crt;
+grant drop on *.* to r_drp;
+grant reload on *.* to r_rld;
+grant r_sel to test_user@localhost;
+grant r_ins to test_user@localhost;
+grant r_upd to test_user@localhost;
+grant r_del to test_user@localhost;
+grant r_crt to test_user@localhost;
+grant r_drp to test_user@localhost;
+grant r_rld to test_user@localhost;
+flush privileges;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `r_crt` TO `test_user`@`localhost`
+GRANT `r_del` TO `test_user`@`localhost`
+GRANT `r_drp` TO `test_user`@`localhost`
+GRANT `r_ins` TO `test_user`@`localhost`
+GRANT `r_rld` TO `test_user`@`localhost`
+GRANT `r_sel` TO `test_user`@`localhost`
+GRANT `r_upd` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role r_sel;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_sel
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `r_sel`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `r_crt` TO `test_user`@`localhost`
+GRANT `r_del` TO `test_user`@`localhost`
+GRANT `r_drp` TO `test_user`@`localhost`
+GRANT `r_ins` TO `test_user`@`localhost`
+GRANT `r_rld` TO `test_user`@`localhost`
+GRANT `r_sel` TO `test_user`@`localhost`
+GRANT `r_upd` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root r_crt Y
+localhost root r_del Y
+localhost root r_drp Y
+localhost root r_ins Y
+localhost root r_rld Y
+localhost root r_sel Y
+localhost root r_upd Y
+localhost test_user r_crt N
+localhost test_user r_del N
+localhost test_user r_drp N
+localhost test_user r_ins N
+localhost test_user r_rld N
+localhost test_user r_sel N
+localhost test_user r_upd N
+set role r_ins;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_ins
+show grants;
+Grants for test_user@localhost
+GRANT INSERT ON *.* TO `r_ins`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `r_crt` TO `test_user`@`localhost`
+GRANT `r_del` TO `test_user`@`localhost`
+GRANT `r_drp` TO `test_user`@`localhost`
+GRANT `r_ins` TO `test_user`@`localhost`
+GRANT `r_rld` TO `test_user`@`localhost`
+GRANT `r_sel` TO `test_user`@`localhost`
+GRANT `r_upd` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_rld', 'N');
+flush privileges;
+ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
+set role r_rld;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_rld
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_sel
+flush privileges;
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+flush privileges;
+ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation
+set role r_ins;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_ins
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_upd', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_del', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_crt', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_drp', 'N');
+insert into mysql.roles_mapping values ('', 'r_del', 'r_ins', 'N');
+set role r_rld;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_rld
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_sel
+update mysql.roles_mapping set Role='r_ins' where Role='r_ins_wrong';
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_sel
+create table mysql.random_test_table (id INT);
+insert into mysql.random_test_table values (1);
+select * from mysql.random_test_table;
+id
+1
+delete from mysql.roles_mapping where Role='r_ins';
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost r_sel
+insert into mysql.random_test_table values (1);
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table `mysql`.`random_test_table`
+drop table mysql.random_test_table;
+delete from mysql.user where user like 'r\_%';
+delete from mysql.roles_mapping where Role like 'r\_%';
+flush privileges;
+drop user 'test_user'@'localhost';
diff --git a/mysql-test/suite/roles/set_role-multiple-role.test b/mysql-test/suite/roles/set_role-multiple-role.test
new file mode 100644
index 00000000..ecfe8869
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-multiple-role.test
@@ -0,0 +1,102 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user 'test_user'@'localhost';
+
+create role r_sel;
+create role r_ins;
+create role r_upd;
+create role r_del;
+create role r_crt;
+create role r_drp;
+create role r_rld;
+
+grant select on *.* to r_sel;
+grant insert on *.* to r_ins;
+grant update on *.* to r_upd;
+grant delete on *.* to r_del;
+grant create on *.* to r_crt;
+grant drop on *.* to r_drp;
+grant reload on *.* to r_rld;
+
+#####################################
+#set up roles mapping
+#####################################
+grant r_sel to test_user@localhost;
+grant r_ins to test_user@localhost;
+grant r_upd to test_user@localhost;
+grant r_del to test_user@localhost;
+grant r_crt to test_user@localhost;
+grant r_drp to test_user@localhost;
+grant r_rld to test_user@localhost;
+flush privileges;
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+select current_user(), current_role();
+set role r_sel;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--sorted_result
+select * from mysql.roles_mapping;
+
+set role r_ins;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_rld', 'N');
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+flush privileges;
+set role r_rld;
+select current_user(), current_role();
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+flush privileges;
+set role none;
+select current_user(), current_role();
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+flush privileges;
+
+set role r_ins;
+select current_user(), current_role();
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_upd', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_del', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_crt', 'N');
+insert into mysql.roles_mapping values ('', 'r_sel', 'r_drp', 'N');
+insert into mysql.roles_mapping values ('', 'r_del', 'r_ins', 'N');
+set role r_rld;
+select current_user(), current_role();
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+update mysql.roles_mapping set Role='r_ins' where Role='r_ins_wrong';
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+
+create table mysql.random_test_table (id INT);
+insert into mysql.random_test_table values (1);
+--sorted_result
+select * from mysql.random_test_table;
+delete from mysql.roles_mapping where Role='r_ins';
+flush privileges;
+set role r_sel;
+select current_user(), current_role();
+--error ER_TABLEACCESS_DENIED_ERROR
+insert into mysql.random_test_table values (1);
+drop table mysql.random_test_table;
+
+change_user 'root';
+delete from mysql.user where user like 'r\_%';
+delete from mysql.roles_mapping where Role like 'r\_%';
+flush privileges;
+drop user 'test_user'@'localhost';
diff --git a/mysql-test/suite/roles/set_role-recursive.result b/mysql-test/suite/roles/set_role-recursive.result
new file mode 100644
index 00000000..f93a731b
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-recursive.result
@@ -0,0 +1,119 @@
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+create role test_role2;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_user localhost
+select * from mysql.roles_mapping where User like 'test_user';
+Host User Role Admin_option
+localhost test_user test_role1 N
+select * from mysql.roles_mapping where User like 'test_role1';
+Host User Role Admin_option
+ test_role1 test_role2 N
+grant select on *.* to test_role2;
+select * from mysql.user where user like 'test_role1';
+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 Delete_history_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role default_role max_statement_time
+ 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 N 0 0 0 0 N Y 0.000000
+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 Delete_history_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role default_role max_statement_time
+ 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 N 0 0 0 0 N Y 0.000000
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+select * from mysql.roles_mapping where Host='';
+Host User Role Admin_option
+ test_role1 test_role2 N
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+set role test_role2;
+ERROR OP000: User `test_user`@`localhost` has not been granted role `test_role2`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+select * from mysql.roles_mapping where Host='';
+Host User Role Admin_option
+ test_role1 test_role2 N
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+delete from mysql.user where user='test_role1';
+delete from mysql.user where user='test_role2';
+delete from mysql.roles_mapping;
+flush privileges;
+drop user 'test_user'@'localhost';
diff --git a/mysql-test/suite/roles/set_role-recursive.test b/mysql-test/suite/roles/set_role-recursive.test
new file mode 100644
index 00000000..23d623e1
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-recursive.test
@@ -0,0 +1,79 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+create role test_role2;
+grant test_role2 to test_role1;
+
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping where User like 'test_user';
+--sorted_result
+select * from mysql.roles_mapping where User like 'test_role1';
+grant select on *.* to test_role2;
+--sorted_result
+select * from mysql.user where user like 'test_role1';
+--sorted_result
+select * from mysql.user where user like 'test_role2';
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+--sorted_result
+show grants;
+select * from mysql.roles_mapping where Host='';
+
+--sorted_result
+show grants;
+set role none;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+--error ER_INVALID_ROLE
+set role test_role2;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+#Make sure that this still works after an ER_INVALID_ROLE error
+--sorted_result
+show grants;
+set role test_role1;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--sorted_result
+select * from mysql.roles_mapping where Host='';
+
+--sorted_result
+show grants;
+set role none;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+change_user 'root';
+delete from mysql.user where user='test_role1';
+delete from mysql.user where user='test_role2';
+delete from mysql.roles_mapping;
+flush privileges;
+drop user 'test_user'@'localhost';
diff --git a/mysql-test/suite/roles/set_role-routine-simple.result b/mysql-test/suite/roles/set_role-routine-simple.result
new file mode 100644
index 00000000..eaa630f4
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-routine-simple.result
@@ -0,0 +1,104 @@
+create user 'test_user'@'localhost';
+create role test_role1;
+create role test_role2;
+create role test_role3;
+grant test_role1 to test_user@localhost;
+grant test_role3 to test_user@localhost;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_role3
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost root test_role3 Y
+localhost test_user test_role1 N
+localhost test_user test_role3 N
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+grant execute on function mysql.test_func to test_role2;
+grant execute on procedure mysql.test_proc to test_role2;
+grant execute on mysql.* to test_role3;
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role3` TO `test_user`@`localhost`
+use mysql;
+ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql'
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+use mysql;
+call test_proc(@a);
+SELECT @a;
+@a
+6
+SELECT test_func('AABBCCDD');
+test_func('AABBCCDD')
+Test string: AABBCCDD
+show grants;
+Grants for test_user@localhost
+GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO `test_role2`
+GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+GRANT `test_role3` TO `test_user`@`localhost`
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role3` TO `test_user`@`localhost`
+call test_proc(@a);
+ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_proc'
+SELECT test_func('AABBCCDD');
+ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_func'
+set role test_role3;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role3
+show grants;
+Grants for test_user@localhost
+GRANT EXECUTE ON `mysql`.* TO `test_role3`
+GRANT USAGE ON *.* TO `test_role3`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role3` TO `test_user`@`localhost`
+call test_proc(@a);
+SELECT @a;
+@a
+6
+SELECT test_func('AABBCCDD');
+test_func('AABBCCDD')
+Test string: AABBCCDD
+drop user 'test_user'@'localhost';
+revoke execute on function mysql.test_func from test_role2;
+revoke execute on procedure mysql.test_proc from test_role2;
+revoke execute on mysql.* from test_role3;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-routine-simple.test b/mysql-test/suite/roles/set_role-routine-simple.test
new file mode 100644
index 00000000..0e808d19
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-routine-simple.test
@@ -0,0 +1,81 @@
+source include/not_embedded.inc;
+
+create user 'test_user'@'localhost';
+create role test_role1;
+create role test_role2;
+create role test_role3;
+
+grant test_role1 to test_user@localhost;
+grant test_role3 to test_user@localhost;
+grant test_role2 to test_role1;
+
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+create function mysql.test_func (s CHAR(20))
+returns CHAR(50) DETERMINISTIC
+return concat('Test string: ',s);
+
+
+delimiter |;
+create procedure mysql.test_proc (OUT param1 INT)
+begin
+ select COUNT(*) into param1 from mysql.roles_mapping;
+end|
+delimiter ;|
+
+grant execute on function mysql.test_func to test_role2;
+grant execute on procedure mysql.test_proc to test_role2;
+
+grant execute on mysql.* to test_role3;
+
+change_user 'test_user';
+--sorted_result
+show grants;
+
+--error ER_DBACCESS_DENIED_ERROR
+use mysql;
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+use mysql;
+
+call test_proc(@a);
+SELECT @a;
+
+SELECT test_func('AABBCCDD');
+
+--sorted_result
+show grants;
+set role none;
+select current_user(), current_role();
+--sorted_result
+show grants;
+
+--error ER_PROCACCESS_DENIED_ERROR
+call test_proc(@a);
+
+--error ER_PROCACCESS_DENIED_ERROR
+SELECT test_func('AABBCCDD');
+
+set role test_role3;
+select current_user(), current_role();
+--sorted_result
+show grants;
+call test_proc(@a);
+SELECT @a;
+
+SELECT test_func('AABBCCDD');
+
+change_user 'root';
+drop user 'test_user'@'localhost';
+revoke execute on function mysql.test_func from test_role2;
+revoke execute on procedure mysql.test_proc from test_role2;
+revoke execute on mysql.* from test_role3;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+drop function mysql.test_func;
+drop procedure mysql.test_proc;
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-simple.result b/mysql-test/suite/roles/set_role-simple.result
new file mode 100644
index 00000000..c603f727
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-simple.result
@@ -0,0 +1,59 @@
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root test_role1 Y
+localhost test_user test_role1 N
+grant select on *.* to test_role1;
+select * from mysql.user where user='test_role1';
+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 Delete_history_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role default_role max_statement_time
+ test_role1 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 N 0 0 0 0 N Y 0.000000
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+localhost root test_role1 Y
+localhost test_user test_role1 N
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+delete from mysql.user where user='test_role1';
+delete from mysql.roles_mapping where Role='test_role1';
+flush privileges;
+drop user 'test_user'@'localhost';
+create user user1;
+connect con1,localhost,user1,,;
+select current_user;
+current_user
+user1@%
+show grants;
+Grants for user1@%
+GRANT USAGE ON *.* TO `user1`@`%`
+set role none;
+connection default;
+drop user user1;
diff --git a/mysql-test/suite/roles/set_role-simple.test b/mysql-test/suite/roles/set_role-simple.test
new file mode 100644
index 00000000..ed884fa2
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-simple.test
@@ -0,0 +1,54 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user test_user@localhost;
+create role test_role1;
+grant test_role1 to test_user@localhost;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+grant select on *.* to test_role1;
+--sorted_result
+select * from mysql.user where user='test_role1';
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--sorted_result
+select * from mysql.roles_mapping;
+
+set role none;
+select current_user(), current_role();
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+change_user 'root';
+delete from mysql.user where user='test_role1';
+delete from mysql.roles_mapping where Role='test_role1';
+flush privileges;
+drop user 'test_user'@'localhost';
+
+#
+# MDEV-9898 SET ROLE NONE can crash mysqld.
+#
+
+create user user1;
+
+--connect (con1,localhost,user1,,)
+select current_user;
+show grants;
+set role none;
+
+connection default;
+drop user user1;
+
diff --git a/mysql-test/suite/roles/set_role-table-column-priv.result b/mysql-test/suite/roles/set_role-table-column-priv.result
new file mode 100644
index 00000000..a680e3ff
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-column-priv.result
@@ -0,0 +1,71 @@
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+grant select (Role) on mysql.roles_mapping to test_role2;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT (`Role`) ON `mysql`.`roles_mapping` TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for column 'Host' in table 'roles_mapping'
+select Role from mysql.roles_mapping;
+Role
+test_role1
+test_role1
+test_role2
+test_role2
+show grants;
+Grants for test_user@localhost
+GRANT SELECT (`Role`) ON `mysql`.`roles_mapping` TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+use mysql;
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+select Role from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+localhost mysql mariadb.sys global_priv root@localhost 0000-00-00 00:00:00 Select,Delete
+ mysql test_role2 roles_mapping root@localhost 0000-00-00 00:00:00 Select
+revoke select on mysql.roles_mapping from test_role2;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-table-column-priv.test b/mysql-test/suite/roles/set_role-table-column-priv.test
new file mode 100644
index 00000000..e154b259
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-column-priv.test
@@ -0,0 +1,56 @@
+source include/not_embedded.inc;
+
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_role1;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+grant select (Role) on mysql.roles_mapping to test_role2;
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+
+--sorted_result
+show grants;
+
+--error ER_COLUMNACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+select Role from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+
+use mysql;
+
+set role none;
+select current_user(), current_role();
+
+--sorted_result
+--error ER_TABLEACCESS_DENIED_ERROR
+select Role from mysql.roles_mapping;
+
+change_user 'root';
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+revoke select on mysql.roles_mapping from test_role2;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-table-simple.result b/mysql-test/suite/roles/set_role-table-simple.result
new file mode 100644
index 00000000..3f1a68ee
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-simple.result
@@ -0,0 +1,69 @@
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+grant select on mysql.roles_mapping to test_role2;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON `mysql`.`roles_mapping` TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON `mysql`.`roles_mapping` TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+use mysql;
+set role none;
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table `mysql`.`roles_mapping`
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+localhost mysql mariadb.sys global_priv root@localhost 0000-00-00 00:00:00 Select,Delete
+ mysql test_role2 roles_mapping root@localhost 0000-00-00 00:00:00 Select
+revoke select on mysql.roles_mapping from test_role2;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+flush privileges;
diff --git a/mysql-test/suite/roles/set_role-table-simple.test b/mysql-test/suite/roles/set_role-table-simple.test
new file mode 100644
index 00000000..8de77819
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-simple.test
@@ -0,0 +1,53 @@
+source include/not_embedded.inc;
+
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_role1;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+grant select on mysql.roles_mapping to test_role2;
+
+change_user 'test_user';
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+
+select current_user(), current_role();
+set role test_role1;
+select current_user(), current_role();
+
+--sorted_result
+show grants;
+
+--sorted_result
+select * from mysql.roles_mapping;
+
+--sorted_result
+show grants;
+
+use mysql;
+
+set role none;
+select current_user(), current_role();
+
+--sorted_result
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from mysql.roles_mapping;
+
+change_user 'root';
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+revoke select on mysql.roles_mapping from test_role2;
+delete from mysql.user where user like'test_%';
+delete from mysql.roles_mapping where Role like 'test%';
+
+flush privileges;
diff --git a/mysql-test/suite/roles/show_create_database-10463.result b/mysql-test/suite/roles/show_create_database-10463.result
new file mode 100644
index 00000000..94f7d7e9
--- /dev/null
+++ b/mysql-test/suite/roles/show_create_database-10463.result
@@ -0,0 +1,65 @@
+drop database if exists db;
+Warnings:
+Note 1008 Can't drop database 'db'; database doesn't exist
+create role r1;
+create user beep@'%';
+create database db;
+create table db.t1 (i int);
+create table db.t2 (b int);
+grant select on db.* to r1;
+grant r1 to beep@'%';
+connect con1,localhost,beep,,;
+show databases;
+Database
+information_schema
+show create database db;
+ERROR 42000: Access denied for user 'beep'@'localhost' to database 'db'
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+table_schema table_name
+set role r1;
+show databases;
+Database
+db
+information_schema
+show create database db;
+Database Create Database
+db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+table_schema table_name
+db t1
+db t2
+connection default;
+create role r2;
+create user beep2@'%';
+grant update on db.* to r2;
+grant r2 to beep2;
+connect con2,localhost,beep2,,;
+show databases;
+Database
+information_schema
+show create database db;
+ERROR 42000: Access denied for user 'beep2'@'localhost' to database 'db'
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+table_schema table_name
+set role r2;
+show databases;
+Database
+db
+information_schema
+show create database db;
+Database Create Database
+db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+table_schema table_name
+db t1
+db t2
+connection default;
+drop database db;
+drop role r1;
+drop user beep;
+drop role r2;
+drop user beep2;
diff --git a/mysql-test/suite/roles/show_create_database-10463.test b/mysql-test/suite/roles/show_create_database-10463.test
new file mode 100644
index 00000000..b1eaaf5f
--- /dev/null
+++ b/mysql-test/suite/roles/show_create_database-10463.test
@@ -0,0 +1,56 @@
+--source include/not_embedded.inc
+--source include/default_charset.inc
+
+drop database if exists db;
+
+create role r1;
+create user beep@'%';
+
+create database db;
+create table db.t1 (i int);
+create table db.t2 (b int);
+grant select on db.* to r1;
+grant r1 to beep@'%';
+
+--connect (con1,localhost,beep,,)
+show databases;
+--error ER_DBACCESS_DENIED_ERROR
+show create database db;
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+
+set role r1;
+show databases;
+show create database db;
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+
+
+connection default;
+create role r2;
+create user beep2@'%';
+
+grant update on db.* to r2;
+grant r2 to beep2;
+--connect (con2,localhost,beep2,,)
+show databases;
+--error ER_DBACCESS_DENIED_ERROR
+show create database db;
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+
+set role r2;
+show databases;
+
+show create database db;
+select table_schema, table_name from information_schema.tables
+where table_schema = 'db' order by table_name;
+
+
+connection default;
+
+drop database db;
+drop role r1;
+drop user beep;
+drop role r2;
+drop user beep2;
diff --git a/mysql-test/suite/roles/show_grants.result b/mysql-test/suite/roles/show_grants.result
new file mode 100644
index 00000000..21c5a74e
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants.result
@@ -0,0 +1,162 @@
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_user@localhost;
+grant test_role2 to test_role1;
+select user, host from mysql.user where user not like 'root';
+User Host
+mariadb.sys localhost
+test_role1
+test_role2
+test_user localhost
+select * from mysql.roles_mapping;
+Host User Role Admin_option
+ test_role1 test_role2 N
+localhost root test_role1 Y
+localhost root test_role2 Y
+localhost test_user test_role1 N
+localhost test_user test_role2 N
+select user, host from mysql.db;
+user host
+grant select on mysql.* to test_role2;
+flush privileges;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+root@localhost test_role1 YES NO
+root@localhost test_role2 YES NO
+test_role1 test_role2 NO NULL
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+test_role1 test_role2 NO NULL
+test_user@localhost test_role1 NO NO
+test_user@localhost test_role2 NO NO
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+set role test_role1;
+select * from information_schema.enabled_roles;
+ROLE_NAME
+test_role1
+test_role2
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role1
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_role1`
+GRANT `test_role2` TO `test_user`@`localhost`
+set role none;
+select * from information_schema.enabled_roles;
+ROLE_NAME
+NULL
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost NULL
+show grants;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for test_user@localhost;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for test_role1;
+ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql'
+show grants for test_role2;
+ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql'
+show grants for CURRENT_USER;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for CURRENT_USER();
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for CURRENT_ROLE;
+ERROR 42000: There is no such grant defined for user 'test_user' on host 'localhost'
+show grants for CURRENT_ROLE();
+ERROR 42000: There is no such grant defined for user 'test_user' on host 'localhost'
+set role test_role2;
+select * from information_schema.enabled_roles;
+ROLE_NAME
+test_role2
+select current_user(), current_role();
+current_user() current_role()
+test_user@localhost test_role2
+show grants;
+Grants for test_user@localhost
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for test_user@localhost;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for test_role1;
+Grants for test_role1
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role1`
+GRANT USAGE ON *.* TO `test_role2`
+GRANT `test_role2` TO `test_role1`
+show grants for test_role2;
+Grants for test_role2
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role2`
+show grants for CURRENT_USER;
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for CURRENT_USER();
+Grants for test_user@localhost
+GRANT USAGE ON *.* TO `test_user`@`localhost`
+GRANT `test_role1` TO `test_user`@`localhost`
+GRANT `test_role2` TO `test_user`@`localhost`
+show grants for CURRENT_ROLE;
+Grants for test_role2
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role2`
+show grants for CURRENT_ROLE();
+Grants for test_role2
+GRANT SELECT ON `mysql`.* TO `test_role2`
+GRANT USAGE ON *.* TO `test_role2`
+drop user 'test_user'@'localhost';
+revoke select on mysql.* from test_role2;
+drop role test_role1;
+drop role test_role2;
+delete from mysql.roles_mapping where Role='test_role1';
+delete from mysql.roles_mapping where Role='test_role2';
+flush privileges;
+#
+# MDEV-24289: show grants missing with grant option
+#
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+Grants for anel
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `anel`
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+Grants for MariaDB_admin
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO `MariaDB_admin` WITH GRANT OPTION
+drop role MariaDB_admin;
+drop role anel;
diff --git a/mysql-test/suite/roles/show_grants.test b/mysql-test/suite/roles/show_grants.test
new file mode 100644
index 00000000..fc2165ac
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants.test
@@ -0,0 +1,103 @@
+source include/not_embedded.inc;
+
+#create a user with no privileges
+create user test_user@localhost;
+create role test_role1;
+create role test_role2;
+
+grant test_role1 to test_user@localhost;
+grant test_role2 to test_user@localhost;
+grant test_role2 to test_role1;
+--sorted_result
+select user, host from mysql.user where user not like 'root';
+--sorted_result
+select * from mysql.roles_mapping;
+
+--sorted_result
+select user, host from mysql.db;
+
+grant select on mysql.* to test_role2;
+flush privileges;
+
+--sorted_result
+select * from information_schema.applicable_roles;
+
+change_user 'test_user';
+
+--sorted_result
+select * from information_schema.applicable_roles;
+
+--sorted_result
+show grants;
+select current_user(), current_role();
+set role test_role1;
+--sorted_result
+select * from information_schema.enabled_roles;
+select current_user(), current_role();
+--sorted_result
+show grants;
+set role none;
+--sorted_result
+select * from information_schema.enabled_roles;
+select current_user(), current_role();
+--sorted_result
+show grants;
+
+--sorted_result
+show grants for test_user@localhost;
+--error ER_DBACCESS_DENIED_ERROR
+show grants for test_role1;
+--error ER_DBACCESS_DENIED_ERROR
+show grants for test_role2;
+--sorted_result
+show grants for CURRENT_USER;
+--sorted_result
+show grants for CURRENT_USER();
+--error ER_NONEXISTING_GRANT
+show grants for CURRENT_ROLE;
+--error ER_NONEXISTING_GRANT
+show grants for CURRENT_ROLE();
+
+set role test_role2;
+--sorted_result
+select * from information_schema.enabled_roles;
+select current_user(), current_role();
+--sorted_result
+show grants;
+--sorted_result
+show grants for test_user@localhost;
+--sorted_result
+show grants for test_role1;
+--sorted_result
+show grants for test_role2;
+--sorted_result
+show grants for CURRENT_USER;
+--sorted_result
+show grants for CURRENT_USER();
+--sorted_result
+show grants for CURRENT_ROLE;
+--sorted_result
+show grants for CURRENT_ROLE();
+
+
+change_user 'root';
+drop user 'test_user'@'localhost';
+revoke select on mysql.* from test_role2;
+drop role test_role1;
+drop role test_role2;
+delete from mysql.roles_mapping where Role='test_role1';
+delete from mysql.roles_mapping where Role='test_role2';
+flush privileges;
+
+--echo #
+--echo # MDEV-24289: show grants missing with grant option
+--echo #
+create role anel;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'anel';
+SHOW GRANTS for 'anel';
+
+create role MariaDB_admin;
+GRANT SELECT, UPDATE, DELETE, ALTER ON *.* TO 'MariaDB_admin' WITH GRANT OPTION;
+SHOW GRANTS for 'MariaDB_admin';
+drop role MariaDB_admin;
+drop role anel;
diff --git a/mysql-test/suite/roles/show_grants_replicated.result b/mysql-test/suite/roles/show_grants_replicated.result
new file mode 100644
index 00000000..9438939b
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants_replicated.result
@@ -0,0 +1,58 @@
+include/master-slave.inc
+[connection master]
+create user u1;
+create role r1;
+#
+# On master SHOW GRANTS work both for the user and the role:
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+#
+connection slave;
+#
+# The role has been replicated,
+# it's visible in mysql.user and I_S:
+#
+select user, host, is_role from mysql.user where user in ('u1', 'r1');
+User Host is_role
+r1 Y
+u1 % N
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE IS_DEFAULT
+root@localhost r1 YES NO
+#
+# Check show grants for the new user.
+show grants for u1;
+Grants for u1@%
+GRANT USAGE ON *.* TO `u1`@`%`
+#
+# Check show grants for the new role.
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+#
+# Check if flushing privileges preserves the state.
+flush privileges;
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+#
+# Check SHOW GRANTS after setting the role.
+set role r1;
+show grants;
+Grants for root@localhost
+GRANT `r1` TO `root`@`localhost` WITH ADMIN OPTION
+GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT USAGE ON *.* TO `r1`
+show grants for r1;
+Grants for r1
+GRANT USAGE ON *.* TO `r1`
+connection master;
+drop role r1;
+drop user u1;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/roles/show_grants_replicated.test b/mysql-test/suite/roles/show_grants_replicated.test
new file mode 100644
index 00000000..91129c43
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants_replicated.test
@@ -0,0 +1,38 @@
+--source include/master-slave.inc
+
+create user u1;
+create role r1;
+--echo #
+--echo # On master SHOW GRANTS work both for the user and the role:
+show grants for u1;
+show grants for r1;
+--echo #
+--sync_slave_with_master
+--echo #
+--echo # The role has been replicated,
+--echo # it's visible in mysql.user and I_S:
+--echo #
+--sorted_result
+select user, host, is_role from mysql.user where user in ('u1', 'r1');
+select * from information_schema.applicable_roles;
+--echo #
+--echo # Check show grants for the new user.
+show grants for u1;
+--echo #
+--echo # Check show grants for the new role.
+show grants for r1;
+--echo #
+--echo # Check if flushing privileges preserves the state.
+flush privileges;
+show grants for r1;
+--echo #
+--echo # Check SHOW GRANTS after setting the role.
+set role r1;
+show grants;
+show grants for r1;
+
+connection master;
+drop role r1;
+drop user u1;
+--sync_slave_with_master
+--source include/rpl_end.inc